Skip to content

Guide File System

Kris Simon edited this page Mar 1, 2026 · 8 revisions

File System

ARO provides built-in file system operations for reading, writing, and watching files. This chapter covers file I/O and directory monitoring.

Reading Files

Text Files

Read the <content> from the <file: "./data.txt">.
Read the <readme> from the <file: "./README.md">.

JSON Files

Read the <config: JSON> from the <file: "./config.json">.
Read the <data: JSON> from the <file: "./data.json">.

Binary Files

Read the <image: bytes> from the <file: "./logo.png">.
Read the <document: bytes> from the <file: "./report.pdf">.

Dynamic Paths

(GET /files/{name}: File API) {
    Extract the <filename> from the <request: parameters>.
    Read the <content> from the <file: "./uploads/${filename}">.
    Return an <OK: status> with <content>.
}

Error Handling

(Load Config: Configuration) {
    Read the <config: JSON> from the <file: "./config.json">.

    Log "Config not found, using defaults" to the <console> when <config> is empty.
    Create the <config> with {
        port: 8080,
        debug: false
    } when <config> is empty.

    Publish as <app-config> <config>.
    Return an <OK: status> for the <loading>.
}

Writing Files

Text Files

Write the <content> to the <file: "./output.txt">.
Write the <report> to the <file: "./reports/daily.txt">.

JSON Files

Write the <config: JSON> to the <file: "./config.json">.
Write the <data: JSON> to the <file: "./export.json">.

Appending

Store the <log-entry> into the <file: "./logs/app.log">.
Store the <record> into the <file: "./data/records.csv">.

Creating Directories

Directories are created automatically when writing:

(* ./reports/2024/01/ is created if it doesn't exist *)
Write the <report> to the <file: "./reports/2024/01/daily.txt">.

Format-Aware File I/O

ARO automatically detects the file format based on the extension and serializes/deserializes data accordingly. Write to .json and get JSON. Write to .csv and get CSV. No manual serialization needed.

Supported Formats

Extension Format Description
.json JSON Pretty-printed JSON
.jsonl, .ndjson JSON Lines One JSON object per line
.yaml, .yml YAML Human-readable YAML
.toml TOML Configuration format
.xml XML Variable name as root element
.csv CSV Comma-separated with headers
.tsv TSV Tab-separated with headers
.md Markdown Markdown table
.html HTML HTML table
.txt Plain Text key=value pairs
.sql SQL INSERT statements
.log Log Date-prefixed entries
.env Environment KEY=VALUE format
.obj Binary Raw binary data

Writing to Different Formats

Same data, different formats - just change the extension:

Create the <users> with [
    { "id": 1, "name": "Alice", "email": "alice@example.com" },
    { "id": 2, "name": "Bob", "email": "bob@example.com" }
].

(* JSON - pretty printed *)
Write the <users> to "./output/users.json".

(* JSON Lines - one object per line, compact *)
Write the <users> to "./output/users.jsonl".

(* YAML - human readable *)
Write the <users> to "./output/users.yaml".

(* CSV - spreadsheet friendly *)
Write the <users> to "./output/users.csv".

(* XML - variable name becomes root element *)
Write the <users> to "./output/users.xml".

(* Markdown table *)
Write the <users> to "./docs/users.md".

(* SQL INSERT statements *)
Write the <users> to "./backup/users.sql".

(* Log file - date-prefixed entries *)
Write the <message> to "./app.log" with "Server started".
Append the <entry> to "./app.log" with "User logged in".

(* Environment file - uppercase keys with underscore nesting *)
Write the <config> to "./.env".

Reading with Format Detection

When reading, the extension determines how content is parsed:

(* JSON - parsed to object/array *)
Read the <config> from "./settings.json".

(* JSON Lines - parsed to array of objects *)
Read the <events> from "./logs/events.jsonl".

(* CSV - parsed to array with headers as keys *)
Read the <records> from "./data.csv".

(* YAML - parsed to object/array *)
Read the <settings> from "./config.yaml".

Bypassing Format Detection

Use the String qualifier to read raw content without parsing:

(* Read as raw string, no JSON parsing *)
Read the <raw-json: String> from "./data.json".

CSV/TSV Options

CSV and TSV support custom options:

(* Custom delimiter *)
Write the <data> to "./export.csv" with { delimiter: ";" }.

(* Without header row *)
Write the <data> to "./export.csv" with { header: false }.

(* Read with custom options *)
Read the <data> from "./import.csv" with { delimiter: ";", header: false }.
Option Type Default Description
delimiter String , / \t Field separator
header Boolean true Include/expect header row
quote String " Quote character

Multi-Format Export Pattern

A common pattern is exporting data to multiple formats:

(exportData: Export API) {
    Retrieve the <users> from the <user-repository>.

    (* Export to multiple formats *)
    Write the <users> to "./export/users.json".
    Write the <users> to "./export/users.csv".
    Write the <users> to "./export/users.yaml".
    Write the <users> to "./docs/users.md".

    Return an <OK: status> with "Exported to 4 formats".
}

Round-Trip Example

Read from one format, write to another:

(convertData: Data Conversion) {
    (* Read CSV input *)
    Read the <records> from "./input/data.csv".

    (* Write to JSON and YAML *)
    Write the <records> to "./output/data.json".
    Write the <records> to "./output/data.yaml".

    Return an <OK: status> for the <conversion>.
}

Deleting Files

Delete the <file: "./temp/cache.json">.
Delete the <file: path>.

Directory Operations

Listing Directory Contents

List all files in a directory:

Create the <uploads-path> with "./uploads".
List the <entries> from the <directory: uploads-path>.

Filter with glob patterns:

Create the <src-path> with "./src".
List the <aro-files> from the <directory: src-path> matching "*.aro".

List recursively:

Create the <project-path> with "./project".
List the <all-files> from the <directory: project-path> recursively.

Each entry contains:

  • name - file or directory name
  • path - full path
  • size - file size in bytes
  • isFile - true if file
  • isDirectory - true if directory
  • modified - last modification date

Checking Existence

Check if a file exists:

Exists the <found> for the <file: "./config.json">.

when <found> is false {
    Log "Config not found!" to the <console>.
}

Check if a directory exists:

Exists the <dir-exists> for the <directory: "./output">.

Creating Directories

Create a directory (including parent directories):

(* Natural syntax with "make" verb *)
Make the <directory> at the <path: "./output/reports/2024">.

(* Alternative syntax *)
CreateDirectory the <output-dir> to the <path: "./output/reports/2024">.

Getting File Stats

Get detailed metadata for a file:

Stat the <info> for the <file: "./document.pdf">.
Log <info: size> to the <console>.
Log <info: modified> to the <console>.

Get directory metadata:

Stat the <dir-info> for the <directory: "./src">.

Copying Files and Directories

Copy a file:

Copy the <file: "./template.txt"> to the <destination: "./copy.txt">.

Copy a directory (recursive by default):

Copy the <directory: "./src"> to the <destination: "./backup/src">.

Moving and Renaming

Rename a file:

Move the <file: "./draft.txt"> to the <destination: "./final.txt">.

Move to a different directory:

Move the <file: "./inbox/report.pdf"> to the <destination: "./archive/report.pdf">.

Move a directory:

Move the <directory: "./temp"> to the <destination: "./processed">.

Appending to Files

Append data to a file:

Append the <log-line> to the <file: "./logs/app.log">.

Creates the file if it doesn't exist.

File Watching

Starting a Watcher

Watch directories for changes using the Watch action:

(Application-Start: File Processor) {
    Log "Starting file processor" to the <console>.

    (* Watch the inbox directory for new files *)
    Watch the <file-monitor> for the <directory> with "./inbox".

    (* Keep running until shutdown *)
    Keepalive the <application> for the <events>.

    Return an <OK: status> for the <startup>.
}

Watch Syntax:

Watch the <file-monitor> for the <directory> with "path".

The Watch action:

  • Monitors the specified directory recursively
  • Emits events when files are created, modified, or deleted
  • Runs asynchronously (does not block execution)
  • Continues until application shutdown

File Events

Watchers emit events when files change:

Event When Triggered Data
FileCreatedEvent New file created path - file path
FileModifiedEvent Existing file modified path - file path
FileDeletedEvent File deleted path - file path

Event Handler Naming Convention

Feature sets with business activity File Event Handler receive file events. The feature set name determines which event type it handles:

Feature Set Name Handles Event
Handle File Created FileCreatedEvent
Handle File Modified FileModifiedEvent
Handle File Deleted FileDeletedEvent

Event Handler Examples

(* Handle new files *)
(Handle File Created: File Event Handler) {
    Extract the <path> from the <event: path>.
    Log "file created" to the <console>.
    Return an <OK: status> for the <event>.
}

(* Handle modified files *)
(Handle File Modified: File Event Handler) {
    Extract the <path> from the <event: path>.
    Log "file modified" to the <console>.
    Return an <OK: status> for the <event>.
}

(* Handle deleted files *)
(Handle File Deleted: File Event Handler) {
    Extract the <path> from the <event: path>.
    Log "file deleted" to the <console>.
    Return an <OK: status> for the <event>.
}

Common Patterns

Config Hot-Reload

(Application-Start: Hot Reload App) {
    (* Load initial config *)
    Read the <config: JSON> from the <file: "./config.json">.
    Publish as <app-config> <config>.

    (* Watch for config changes *)
    Watch the <file-monitor> for the <directory> with ".".

    Start the <http-server> on port <config: port>.
    Wait for <shutdown-signal>.
    Return an <OK: status> for the <startup>.
}

(Handle File Modified: File Event Handler) {
    Extract the <path> from the <event: path>.

    Log "Reloading configuration..." to the <console> when <path> is "./config.json".
    Read the <new-config: JSON> from the <file: "./config.json"> when <path> is "./config.json".
    Publish as <app-config> <new-config> when <path> is "./config.json".
    Log "Configuration reloaded" to the <console> when <path> is "./config.json".

    Return an <OK: status> for the <reload>.
}

File Upload Processing

(Application-Start: Upload Processor) {
    Watch the <file-monitor> for the <directory> with "./uploads".
    Start the <http-server> on port 8080.
    Wait for <shutdown-signal>.
    Return an <OK: status> for the <startup>.
}

(POST /upload: Upload API) {
    Extract the <file-data> from the <request: body>.
    Extract the <filename> from the <request: headers.filename>.

    Write the <file-data> to the <file: "./uploads/${filename}">.

    Return a <Created: status> with { filename: <filename> }.
}

(Handle File Created: File Event Handler) {
    Extract the <path> from the <event: path>.

    (* Only process files in uploads directory *)
    Read the <content> from the <file: path> when <path> starts with "./uploads/".
    Transform the <processed> from the <content> when <path> starts with "./uploads/".
    Write the <processed> to the <file: "./processed/${filename}"> when <path> starts with "./uploads/".
    Delete the <file: path> when <path> starts with "./uploads/".
    Log "Processed: ${path}" to the <console> when <path> starts with "./uploads/".

    Return an <OK: status> for the <processing>.
}

Log File Management

(Application-Start: Logging App) {
    (* Create log directory if needed *)
    Write the <header> to the <file: "./logs/app.log">.
    Return an <OK: status> for the <startup>.
}

(Log Event: ApplicationEvent Handler) {
    Extract the <event-type> from the <event: type>.
    Extract the <event-data> from the <event: data>.

    Create the <log-entry> with {
        timestamp: <current-time>,
        type: <event-type>,
        data: <event-data>
    }.

    Store the <log-entry: JSON> into the <file: "./logs/app.log">.
    Return an <OK: status> for the <logging>.
}

Batch File Processing

(Process Batch: Scheduled Task) {
    List the <files> from the <directory: "./inbox">.

    for each <file> in <files> {
        Read the <content> from the <file: file>.
        Process the <result> from the <content>.
        Write the <result> to the <file: "./outbox/${file.name}">.
        Delete the <file: file>.
    }

    Return an <OK: status> for the <batch>.
}

File Paths

Relative Paths

Read the <content> from the <file: "./config.json">.       (* Relative to app *)
Read the <content> from the <file: "../shared/data.json">. (* Parent directory *)

Absolute Paths

Read the <content> from the <file: "/etc/myapp/config.json">.

Path Construction

Create the <path> with "./uploads/${user-id}/${filename}".
Write the <data> to the <file: path>.

Best Practices

Always Handle Missing Files

(Load Data: Initialization) {
    Read the <data: JSON> from the <file: "./data.json">.

    (* Handle missing file *)
    Create the <data> with { items: [] } when <data> is empty.
    Write the <data: JSON> to the <file: "./data.json"> when <data> is empty.

    Publish as <app-data> <data>.
    Return an <OK: status> for the <loading>.
}

Validate File Types

(POST /upload: Upload API) {
    Extract the <filename> from the <request: headers.filename>.
    Extract the <content-type> from the <request: headers.Content-Type>.

    (* Validate file type *)
    when <content-type> is not "image/png" and <content-type> is not "image/jpeg" {
        Return a <BadRequest: status> for the <invalid: file-type>.
    }

    Write the <data> to the <file: "./uploads/${filename}">.
    Return a <Created: status> with { filename: <filename> }.
}

Use Appropriate Encodings

(* Text files *)
Read the <text> from the <file: "./data.txt">.

(* Binary files *)
Read the <binary: bytes> from the <file: "./image.png">.

(* JSON files *)
Read the <json: JSON> from the <file: "./config.json">.

Clean Up Temporary Files

(Process File: Temporary Processing) {
    Read the <input> from the <file: "./input.txt">.
    Write the <temp> to the <file: "./temp/processing.tmp">.

    Process the <result> from the <temp>.

    Write the <result> to the <file: "./output.txt">.
    Delete the <file: "./temp/processing.tmp">.

    Return an <OK: status> for the <processing>.
}

Next Steps

Clone this wiki locally