IO (Input Output)

work-in-progress

File paths and operations

Rye uses URI-style file paths prefixed with %. These support various path operations:

Path manipulation

; Get file extension (including the dot)
File-ext? %data/file.txt        ; returns ".txt"
File-ext? %data/file.temp.png   ; returns ".png"

; Get filename with extension
Filename? %path/to/document.pdf ; returns "document.pdf"

; Get filename without extension (stem)
Stem? %path/to/document.pdf     ; returns "document"

; Get directory path
Dir? %path/to/document.pdf      ; returns "path/to"

; Split path into components
Split %path/to/document.pdf     ; returns { "path" "to" "document.pdf" }

; Check if path is absolute
Is-absolute %/home/user/file.txt  ; returns true
Is-absolute %relative/path.txt    ; returns false

; Check if file exists
Does-exist %data/file.txt         ; returns true or false

Reading files

; Read entire file as string
Read %test.txt
; returns string with full content

; Read file as block of lines
Read\lines %test.txt
; returns block with one string per line

; Read file as bytes
Read\bytes %binary.dat
; returns native bytes object

Writing files

; Write string to file (creates or overwrites)
Write %output.txt "Hello, World!\n"
; returns the content that was written

File objects

For more control, you can work with file objects directly:

; Create a new file and write to it
file: Create %output.txt
file .Write "Hello, World!\n"
file .Write "Second line\n"
file .Close

; Open existing file for reading
file: Open %data.txt
content: Read-all file
file .Close
print content

; Get file information
file: Open %data.txt
info: Stat file
print "File size:" + to-string Size? info
file .Close

Reader/Writer pattern

Rye uses a reader/writer pattern for streaming data. This allows processing large files without loading everything into memory.

Creating readers

; Reader from file path
reader: Reader %data.txt

; Reader from file object
file: Open %data.txt
reader: Reader file

; Reader from string (for testing/processing)
reader: reader "some string content"

; Standard input
reader: stdin

Creating writers

; Writer from file object
file: Create %output.txt
writer: Writer file

; Standard output/error
writer: stdout
writer: stderr

Reading content

; Read all content as string
reader: Reader %data.txt
content: reader .Read\string
reader .Close
print content

Copying between reader and writer

; Copy without loading into memory
Open %source.txt |Copy Create %destination.txt

; Or using reader explicitly
reader: Reader %source.txt
writer: Writer Create %dest.txt
reader .Copy writer

Appending to files

; Open file for appending
file: Open\append %log.txt
file .Write "New log entry\n"
file .Close

File tailing (monitoring)

The tail-file builtin monitors files for new content - useful for log file monitoring:

; Tail a file, following new content
tailer: tail-file %app.log true true
; Arguments: path, follow (true), reopen on rotation (true)

; Read lines as they are added
forever {
    line: tailer .Read-line
    print "New line:" ++ line
}

; Close when done
tailer .Close

Console input

; Prompt user for input
name: input "Enter your name: "
print "Hello, " ++ name

HTTP/HTTPS requests

Simple GET request

; HTTPS GET - returns response body as string
Get https://api.example.com/data |print

; HTTP GET
Get http://api.example.com/data |print

POST requests

; POST with JSON content type
Post https://api.example.com/users "{\"name\":\"test\"}" 'json |print

; POST with text content type
Post https://api.example.com/submit "plain text data" 'text |print

; POST with URL-encoded form data
Post https://api.example.com/form "name=test&value=123" 'urlencoded |print

Advanced HTTP requests

For more control over requests (custom headers, authentication), use the Request pattern:

; Create a request with custom method
req: Request https://api.example.com/resource 'POST "{\"data\":\"value\"}"

; Add headers
req .Header! 'Content-Type "application/json"
req .Header! 'Authorization "Bearer token123"

; Or use Basic Authentication
req .Basic-auth! "username" "password"

; Execute the request
response: req .Call

; Read response body
body: response .Read-body
print body

Streaming downloads

Download large files without loading into memory:

; Stream HTTPS download directly to file
Open https://example.com/large-file.zip
|Copy Create %download.zip

; With progress (using reader)
reader: Open https://example.com/file.zip
writer: Writer Create %file.zip
reader .Copy writer

FTP operations

; Load credentials from a file
info: context { do Load %.ftpinfo }

; Connect, login, and retrieve a file
Open ftp://download.example.com:21
|Login info/user info/pwd
|Retrieve "path/to/file.zip"
|Copy Create %local.zip

Running shell commands

The cmd builtin creates command objects for running external programs:

; Simple command execution
cmd { echo -n Hello World } |Output
; returns "Hello World"

; Command with shell pipes
cmd { echo -n Hello World |tr A-Z a-z |sed "s/hello/goodbye/" } |Output
; returns "goodbye world"

; Embed Rye values into commands
cmd { echo -n "1 + 1 =" { 1 + 1 } } |Output
; returns "1 + 1 = 2"

; Pass list as arguments
args: list { "two" "arguments" }
cmd { printf "'%s' " ?args } |Output
; returns "'two' 'arguments' "

Command configuration

; Change working directory
cmd { pwd } |Dir! %/ |Output |trim
; returns "/"

; Pipe commands together
cmd { echo -n Hello World } |Pipe cmd { tr a-z A-Z } |Output
; returns "HELLO WORLD"

; Run and check success
cmd { true } |Run   ; succeeds
cmd { false } |Run  ; fails with error

; Get exit status
cmd { true } |Status   ; returns 0
cmd { false } |Status  ; returns 1
cmd { false |true } |Status  ; returns list [ 1 0 ]

Database connections

SQLite

; Open SQLite database
db: Open sqlite://mydata.db

; Execute statements (CREATE, INSERT, UPDATE, DELETE)
db .Exec "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)"
db .Exec { INSERT INTO users ( name , age ) VALUES ( "Alice" , 30 ) }

; Use Rye variables in SQL (note spacing in SQL dialect)
name: "Bob"
db .Exec { INSERT INTO users ( name , age ) VALUES ( ?name , 42 ) }

; Query data
db .Query { SELECT * FROM users WHERE age > 25 } |print

; Debug SQL generation
db .Show-SQL { SELECT * FROM users WHERE name = ?name }
; returns the SQL string with parameters

PostgreSQL

; Open PostgreSQL connection
db: Open postgres://user:pass@localhost:5432/dbname

; Execute and query work the same as SQLite
db .Exec { INSERT INTO users ( name , age ) VALUES ( "Alice" , 30 ) }
result: db .Query "SELECT * FROM users"

MySQL

; Open MySQL connection
db: Open mysql://user:pass@tcp(localhost:3306)/dbname

; Or with separate password
db: Open\pwd mysql://user@tcp(localhost:3306)/dbname "password"

; Execute and query
db .Exec { INSERT INTO test VALUES ( 1 , "test" ) }
id: 101
db .Query { SELECT * FROM test WHERE id = ?id }

Bytes operations

; Read file as bytes
bytes1: Read\bytes %file1.bin

; Append two byte arrays
combined: append\bytes bytes1 bytes2

; Write bytes to file
write\bytes combined "output.bin"