# Module: `nupp.io` # Byte buffers, readers and writers `nupp.io` supplies in-memory byte I/O without requiring a stream framework. A buffer holds its bytes in a LuaJIT FFI array, so using it adds no native dependency. Files and processes build on the same reader and writer contracts when their native features are selected; sockets and general asynchronous streams remain separate future layers. ## Buffers `newBuffer()` creates an empty growable buffer. A string supplies initial bytes; an integer reserves capacity without changing the length. ```nupp local bytes = nupp.io.newBuffer("hello") bytes:setString("!", bytes:length()) assert(bytes:getString() == "hello!") bytes:ensureCapacity(4096) assert(bytes:capacity() >= 4096) bytes:resize(3) assert(bytes:getString() == "hel") ``` Buffer offsets are zero-based. `getString(offset, count)` copies a range. `setString` overwrites from an offset and grows the buffer as needed; a gap is filled with zero bytes. `clear` sets the length to zero without discarding capacity. `resize` truncates or zero-fills. Capacity is the allocation, not a recorded number. Growing at least doubles it, so appending through a writer costs amortized constant time per byte and `capacity()` reports bytes that are actually held. `ensureCapacity` reserves at least the minimum asked for. `close()` releases the buffer, is safe to call repeatedly, and makes later operations raise. `isReleased()` reports that state. | Member | Purpose | | --- | --- | | `length()`, `capacity()` | Inspect logical and reserved byte counts. | | `clear()`, `resize(length)` | Remove bytes, truncate, or zero-extend. | | `ensureCapacity(minimum)` | Reserve without changing logical length. | | `getString(offset?, count?)` | Copy all bytes or a zero-based range. | | `setString(bytes, offset?)` | Overwrite and grow from a zero-based offset. | | `view(offset?, count?)` | Retain an immutable snapshot range. | | `newReader()`, `newWriter()` | Open directional in-memory I/O. | | `isReleased()`, `close()` | Inspect or release ownership. | ## Byte views `view(offset, count)` returns an immutable snapshot, not a mutable alias into the buffer. It remains valid if the source buffer changes or closes: ```nupp local buffer = nupp.io.newBuffer("header-body") local header = buffer:view(0, 6) buffer:clear() assert(header:getString() == "header") header:close() ``` A view can make a smaller view, open its own snapshot reader with `newReader`, report its byte length, copy to a string, and be closed. Data and UTF-8 functions accept views so callers can avoid coupling those APIs to a mutable buffer. `ByteView` provides `newReader`, `length`, `getString`, `view`, `isReleased`, and `close`. Its `view` offsets are zero-based like Buffer offsets. ## Readers A `nupp.io.Reader` is a forward-only byte source. `newStringReader(text)` reads a string; `buffer:newReader()` reads a snapshot of the buffer's current contents. ```nupp local reader = nupp.io.newStringReader("abcdef") assert(reader:read(2) == "ab") local destination = nupp.io.newBuffer() assert(reader:readInto(destination, 0, 3) == 3) assert(destination:getString() == "cde") assert(reader:read(8) == "f") assert(reader:read(8) == "") -- EOF ``` `read(count)` returns at most `max(1, count)` bytes, so zero and negative counts still make progress. It returns an empty string at EOF and `nil, reason` after close. `readInto(buffer, offset, count)` returns zero at EOF. Its default count is 64 KiB. `transferTo(writer)` copies the entire remaining source and returns the byte count. | Reader member | Result | | --- | --- | | `read(count)` | `string?, reason?` | | `readInto(buffer, offset?, count?)` | `integer?, reason?` | | `transferTo(writer)` | `integer?, reason?` | | `close()` | `boolean, reason?` | ## Writers `buffer:newWriter()` clears the buffer and returns a forward-only writer targeting it. ```nupp local destination = nupp.io.newBuffer() local writer = destination:newWriter() assert(writer:write("prefix:")) local payload = nupp.io.newBuffer("body") assert(writer:writeFrom(payload) == 4) assert(writer:flush()) assert(destination:getString() == "prefix:body") ``` `write` returns a boolean. `writeFrom` and `writeView` return the byte count. All return a reason when closed or when the destination was released. Writing a buffer into itself is rejected. `flush` is a no-op for memory but is part of the common writer contract, allowing a later file or socket writer to implement the same interface. | Writer member | Result | | --- | --- | | `write(bytes)` | `boolean, reason?` | | `writeFrom(buffer, offset?, count?)` | `integer?, reason?` | | `writeView(view, offset?, count?)` | `integer?, reason?` | | `flush()` | `boolean, reason?` | | `close()` | `boolean, reason?` | For filesystem names rather than file contents, see [`nupp.io.Path`](path-uri.md#paths). ## Child processes `nupp.io.process` starts a child without exposing descriptors, platform handles, or signals. Reaching the module selects the native process provider and the suspension runtime it needs. ```nupp local process = require("nupp.io.process") local child, spawnReason = process.new({ args = {"cc", "--version"}, stdin = "null", }) assert(child, spawnReason) local result, communicateReason = child:communicate() assert(result, communicateReason) assert(result:succeeded(), result.errorOutput) print(result.output) assert(child:close()) ``` `args[1]` is the program and the remaining entries are its arguments. `cwd` changes the child's directory. `env` overlays inherited variables unless `clearEnv` starts from an empty environment. Standard streams default to `"pipe"` and may instead be `"inherit"` or `"null"`; stderr alone may be `"stdout"` to share stdout's actual destination. `timeoutMs` is measured from spawn, not from the first wait. `communicate({input?, maxOutputBytes?})` is the safe whole-process operation: it feeds stdin while draining stdout and stderr together, closes stdin to deliver EOF, and waits for the exit. Doing those operations sequentially can deadlock when a child fills one output pipe while waiting for more input. The concrete `Reader` and `Writer` satisfy the shared completion-oriented `nupp.io.Reader` and `nupp.io.Writer` contracts and also expose the nonblocking `poll` and `offer` operations needed by that combined drain. `asReader` and `asWriter` borrow the same records through their shared interfaces; they do not allocate or duplicate the native handle. The owning `Process` retains and eventually destroys that handle, so a borrow may not outlive it. Every wait is contextual. With no [suspension handler](start/suspension-handlers.md) installed, it sleeps in the platform readiness wait. Under a scheduler handler, the same call parks the current task while the scheduler keeps running. Ready operations do neither. [Suspension](start/suspension.md) explains how the same ordinary call takes those paths and how several waits compose with `all`, `race`, or `batch`. `Process.close()` is idempotent and is also its lexical `@drop`: it attempts every stream release, terminates a child still running, waits for it to finish, and releases the child handle. An `Exit` reports `exitCode`, `killed`, and `timedOut`; `succeeded()` is true only for an ordinary zero exit. Buffers, byte views, readers, writers, filesystem paths and URIs. ## Submodules | Module | Description | | --- | --- | | `nupp.io.files` | Filesystem metadata, directory contents, and the operations that move names rather than bytes. | | `nupp.io.http` | An optional asynchronous HTTP client whose sockets and TLS run in the native Reqwest/Tokio provider. | | `nupp.io.process` | Running a child process. | | `nupp.io.processtypes` | What a child process is, and what a platform has to provide to run one. | ## Constructors ### `newBuffer` _constructor_ Creates an owned growable byte buffer. ```nupp local newBuffer: function(initial: integer | string?): Buffer ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `initial` | `integer | string?` | initial bytes, an initial capacity, or nothing for an empty buffer | #### Returns | Type | Description | | --- | --- | | `Buffer` | the new buffer | ### `newPath` _constructor_ Creates a path by joining one or more components. #### Examples Build a path from components: ```nupp local source = nupp.io.newPath("src", "main.nupp") assert(source:toString() == "src" .. nupp.io.separator() .. "main.nupp") ``` Normalize the path after joining its components: ```nupp local source = nupp.io.newPath("src", "app", "..", "main.nupp"):normalize() assert(source:fileName() == "main.nupp") ``` ```nupp local newPath: function(first: string, ...: string): Path ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `first` | `string` | the first path component | | `...` | `string` | the remaining path components | #### Returns | Type | Description | | --- | --- | | `Path` | the new path | ### `newStringReader` _constructor_ Creates a forward-only reader over a string. ```nupp local newStringReader: function(text: string): Reader ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `text` | `string` | the bytes to read | #### Returns | Type | Description | | --- | --- | | `Reader` | the new reader | ### `newURI` _constructor_ Parses and normalizes an absolute URI. ```nupp local newURI: function(value: string | URI.Components): (URI?, string?) ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `value` | `string | URI.Components` | URI text or its individual components | #### Returns | Type | Description | | --- | --- | | `URI?` | the parsed URI, or nil on failure | | `string?` | a failure reason, when unsuccessful | ## Types ### `Buffer` _interface_ An owned growable byte sequence. All offsets are zero-based. ```nupp interface Buffer newReader: function(self: Buffer): Reader newWriter: function(self: Buffer): Writer length: function(self: Buffer): integer capacity: function(self: Buffer): integer clear: function(self: Buffer) ensureCapacity: function(self: Buffer, minimum: integer) resize: function(self: Buffer, length: integer) getString: function(self: Buffer, offset: integer?, count: integer?): string setString: function(self: Buffer, bytes: string, offset: integer?) view: function(self: Buffer, offset: integer?, count: integer?): ByteView isReleased: function(self: Buffer): boolean close: function(self: Buffer): (boolean, string?) end ``` #### Methods ##### `newReader` Opens a reader over a snapshot of the current bytes. ```nupp newReader: function(self: Buffer): Reader ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | ###### Returns | Type | Description | | --- | --- | | `Reader` | the new reader | ##### `newWriter` Clears the buffer and opens a writer that targets it. ```nupp newWriter: function(self: Buffer): Writer ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | ###### Returns | Type | Description | | --- | --- | | `Writer` | the new writer | ##### `length` Reports the logical byte length. ```nupp length: function(self: Buffer): integer ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | ###### Returns | Type | Description | | --- | --- | | `integer` | the byte length | ##### `capacity` Reports the reserved byte capacity. ```nupp capacity: function(self: Buffer): integer ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | ###### Returns | Type | Description | | --- | --- | | `integer` | the byte capacity | ##### `clear` Removes every byte without discarding reserved capacity. ```nupp clear: function(self: Buffer) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | ##### `ensureCapacity` Reserves at least a byte capacity without changing the length. ```nupp ensureCapacity: function(self: Buffer, minimum: integer) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | | `minimum` | `integer` | the minimum capacity | ##### `resize` Changes the byte length, truncating or zero-filling as needed. ```nupp resize: function(self: Buffer, length: integer) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | | `length` | `integer` | the new byte length | ##### `getString` Copies a byte range into a string. ```nupp getString: function(self: Buffer, offset: integer?, count: integer?): string ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | | `offset` | `integer?` | the zero-based start, or zero when omitted | | `count` | `integer?` | the number of bytes, or the rest of the buffer when omitted | ###### Returns | Type | Description | | --- | --- | | `string` | the copied bytes | ##### `setString` Overwrites bytes at an offset, growing and zero-filling any gap. ```nupp setString: function(self: Buffer, bytes: string, offset: integer?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | | `bytes` | `string` | the bytes to write | | `offset` | `integer?` | the zero-based start, or zero when omitted | ##### `view` Retains an immutable snapshot of a byte range. ```nupp view: function(self: Buffer, offset: integer?, count: integer?): ByteView ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | | `offset` | `integer?` | the zero-based start, or zero when omitted | | `count` | `integer?` | the number of bytes, or the rest of the buffer when omitted | ###### Returns | Type | Description | | --- | --- | | `ByteView` | the retained byte view | ##### `isReleased` Reports whether this buffer has been released. ```nupp isReleased: function(self: Buffer): boolean ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the buffer is released | ##### `close` Releases this buffer. Repeated calls are safe. ```nupp close: function(self: Buffer): (boolean, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Buffer` | this buffer | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the release succeeded | | `string?` | a failure reason, when unsuccessful | ### `ByteView` _interface_ An immutable snapshot of bytes. ```nupp interface ByteView newReader: function(self: ByteView): Reader length: function(self: ByteView): integer getString: function(self: ByteView): string view: function(self: ByteView, offset: integer?, count: integer?): ByteView isReleased: function(self: ByteView): boolean close: function(self: ByteView): (boolean, string?) end ``` #### Methods ##### `newReader` Opens a reader over this snapshot. ```nupp newReader: function(self: ByteView): Reader ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | this byte view | ###### Returns | Type | Description | | --- | --- | | `Reader` | the new reader | ##### `length` Reports the number of bytes in this snapshot. ```nupp length: function(self: ByteView): integer ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | this byte view | ###### Returns | Type | Description | | --- | --- | | `integer` | the byte length | ##### `getString` Copies all bytes into a string. ```nupp getString: function(self: ByteView): string ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | this byte view | ###### Returns | Type | Description | | --- | --- | | `string` | the copied bytes | ##### `view` Retains an immutable subrange of this snapshot. ```nupp view: function(self: ByteView, offset: integer?, count: integer?): ByteView ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | this byte view | | `offset` | `integer?` | the zero-based start, or zero when omitted | | `count` | `integer?` | the number of bytes, or the rest of the view when omitted | ###### Returns | Type | Description | | --- | --- | | `ByteView` | the retained subrange | ##### `isReleased` Reports whether this snapshot has been released. ```nupp isReleased: function(self: ByteView): boolean ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | this byte view | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the view is released | ##### `close` Releases this snapshot. Repeated calls are safe. ```nupp close: function(self: ByteView): (boolean, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `ByteView` | this byte view | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the release succeeded | | `string?` | a failure reason, when unsuccessful | ### `Files` _record_ Filesystem metadata and directory contents. Every operation here answers before a transfer could have started: it reads a name, a listing, or an attribute. Reading and writing a file's bytes is a separate layer. ```nupp record Files type Kind = "file" | "directory" | "symlink" | "other" type UserFolder = "home" | "documents" | "downloads" | "desktop" | "pictures" | "music" | "videos" record Info kind: Kind size: integer modified: number readOnly: boolean end type Mode = "r" | "w" | "a" | "r+" | "w+" | "a+" type Origin = "set" | "current" | "end" type LineIterator = function(): string? record File newReader: nosuspend function(self: File): nupp.io.Reader newWriter: nosuspend function(self: File): nupp.io.Writer size: nosuspend function(self: File): (integer?, string?) seek: nosuspend function(self: File, offset: integer?, origin: Origin?): (integer?, string?) position: nosuspend function(self: File): (integer?, string?) flush: nosuspend function(self: File): (boolean, string?) isReleased: nosuspend function(self: File): boolean @drop close: nosuspend function(takes self: File): boolean end record TemporaryPath toString: nosuspend function(self: TemporaryPath): string persist: nosuspend function(self: TemporaryPath, destination: string | nupp.io.Path): (boolean, string?) isReleased: nosuspend function(self: TemporaryPath): boolean @drop close: nosuspend function(takes self: TemporaryPath): boolean end record Entry name: string kind: Kind end record TemporaryOptions directory: (string | nupp.io.Path)? prefix: string? suffix: string? end end ``` #### Types ##### `Info` _record_ One resolved path's attributes. ###### Fields | Name | Type | Description | | --- | --- | --- | | `kind` | `Kind` | What the path refers to, after following symbolic links. | | `size` | `integer` | The byte length of a file's contents. | | `modified` | `number` | Seconds since the Unix epoch, with a fractional part where the platform records one. | | `readOnly` | `boolean` | Whether the platform refuses writes to this path. | ##### `File` _record_ An open file, and the obligation to close it. Readers and writers opened from it satisfy the same [`nupp.io.Reader` and `nupp.io.Writer`](../docs/io.md) contracts a buffer's do, so code written against those works over a file without knowing one is there. ###### Methods ###### `newReader` Opens a forward-only reader at the file's current position. ```nupp newReader: nosuspend function(self: File): nupp.io.Reader ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `File` | this file | ###### Returns | Type | Description | | --- | --- | | `nupp.io.Reader` | the new reader | ###### `newWriter` Opens a forward-only writer at the file's current position. ```nupp newWriter: nosuspend function(self: File): nupp.io.Writer ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `File` | this file | ###### Returns | Type | Description | | --- | --- | | `nupp.io.Writer` | the new writer | ###### `size` Answers the file's byte length without moving the cursor. ```nupp size: nosuspend function(self: File): (integer?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `File` | this file | ###### Returns | Type | Description | | --- | --- | | `integer?` | the byte length, or nil on failure | | `string?` | a failure reason, when unsuccessful | ###### `seek` Moves the cursor and answers where it landed. ```nupp seek: nosuspend function( self: File, offset: integer?, origin: Origin? ): (integer?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `File` | this file | | `offset` | `integer?` | the distance to move | | `origin` | `Origin?` | what the offset is measured from, defaulting to the start | ###### Returns | Type | Description | | --- | --- | | `integer?` | the new position, or nil on failure | | `string?` | a failure reason, when unsuccessful | ###### `position` Answers the cursor's current position. ```nupp position: nosuspend function(self: File): (integer?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `File` | this file | ###### Returns | Type | Description | | --- | --- | | `integer?` | the position, or nil on failure | | `string?` | a failure reason, when unsuccessful | ###### `flush` Pushes buffered writes at the operating system. ```nupp flush: nosuspend function(self: File): (boolean, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `File` | this file | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the flush succeeded | | `string?` | a failure reason, when unsuccessful | ###### `isReleased` Whether this file has been closed. ```nupp isReleased: nosuspend function(self: File): boolean ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `File` | this file | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether it is closed | ###### `close` ```nupp close: nosuspend function(takes self: File): boolean ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `File` | | ###### Returns | Type | Description | | --- | --- | | `boolean` | | ##### `TemporaryPath` _record_ A created temporary path, and the obligation to settle it. Closing removes it. `persist` moves it somewhere permanent instead and discharges the obligation, which is the whole reason to make one: write to a name nobody else can take, then put it where it belongs. ###### Methods ###### `toString` Answers the created path. ```nupp toString: nosuspend function(self: TemporaryPath): string ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TemporaryPath` | this temporary path | ###### Returns | Type | Description | | --- | --- | | `string` | the path text | ###### `persist` Moves this path to a permanent destination, replacing what is there. ```nupp persist: nosuspend function(self: TemporaryPath, destination: string | nupp.io.Path): (boolean, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TemporaryPath` | this temporary path | | `destination` | `string | nupp.io.Path` | where to move it | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the move happened | | `string?` | a failure reason, when unsuccessful | ###### `isReleased` Whether this path has been removed or persisted. ```nupp isReleased: nosuspend function(self: TemporaryPath): boolean ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TemporaryPath` | this temporary path | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether it is settled | ###### `close` ```nupp close: nosuspend function(takes self: TemporaryPath): boolean ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `TemporaryPath` | | ###### Returns | Type | Description | | --- | --- | | `boolean` | | ##### `Entry` _record_ One directory child, as the directory itself describes it. ###### Fields | Name | Type | Description | | --- | --- | --- | | `name` | `string` | The child's name, without any directory part. | | `kind` | `Kind` | What the entry itself is, without following a symbolic link. | ##### `TemporaryOptions` _record_ The generated part of a temporary name. ###### Fields | Name | Type | Description | | --- | --- | --- | | `directory` | `(string | nupp.io.Path)?` | Where to create it, or the platform's temporary directory when omitted. | | `prefix` | `string?` | Text before the generated part. | | `suffix` | `string?` | Text after the generated part, such as an extension. | #### Fields | Name | Type | Description | | --- | --- | --- | | `Kind` | `typeAlias` | What a resolved path refers to. A `symlink` answer only ever comes from `isSymlink`, since every other operation follows the link first. | | `UserFolder` | `typeAlias` | A well-known user folder. Resolved from the environment, so a desktop that records its folders elsewhere is not consulted. | | `Mode` | `typeAlias` | How an open file may be used. The three update modes read and write: `r+` needs an existing file, `w+` truncates one, and `a+` appends. | | `Origin` | `typeAlias` | What a seek offset is measured from. | | `LineIterator` | `typeAlias` | Answers each line in turn, and nil at the end of the file. | ### `Path` _record_ An immutable platform-native UTF-8 filesystem path. ```nupp record Path toString: function(self: Path): string join: function(self: Path, ...: string | Path): Path normalize: function(self: Path): Path absolute: function(self: Path): (Path?, string?) resolve: function(self: Path, ...: string | Path): (Path?, string?) canonicalize: function(self: Path): (Path?, string?) relativeTo: function(self: Path, base: string | Path): (Path?, string?) parent: function(self: Path): Path? fileName: function(self: Path): string? stem: function(self: Path): string? extension: function(self: Path): string? withFileName: function(self: Path, name: string): Path withExtension: function(self: Path, extension: string): Path isAbsolute: function(self: Path): boolean isRelative: function(self: Path): boolean end ``` #### Methods ##### `toString` Returns the native UTF-8 path text. ```nupp toString: function(self: Path): string ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `string` | the path text | ##### `join` Appends path components using the current platform's rules. ```nupp join: function(self: Path, ...: string | Path): Path ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | | `...` | `string | Path` | the components to append | ###### Returns | Type | Description | | --- | --- | | `Path` | the joined path | ##### `normalize` Removes lexical `.` and `..` components without accessing the filesystem. ```nupp normalize: function(self: Path): Path ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `Path` | the normalized path | ##### `absolute` Resolves a relative path against the current working directory. ```nupp absolute: function(self: Path): (Path?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `Path?` | the absolute path, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `resolve` Makes this path absolute, appends components, and normalizes the result. ```nupp resolve: function(self: Path, ...: string | Path): (Path?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | | `...` | `string | Path` | the components to append | ###### Returns | Type | Description | | --- | --- | | `Path?` | the resolved path, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `canonicalize` Resolves filesystem links and returns the real path. ```nupp canonicalize: function(self: Path): (Path?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `Path?` | the canonical path, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `relativeTo` Computes this path relative to a compatible base path. ```nupp relativeTo: function(self: Path, base: string | Path): (Path?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | | `base` | `string | Path` | the path to make this value relative to | ###### Returns | Type | Description | | --- | --- | | `Path?` | the relative path, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `parent` Returns the parent path, when one exists. ```nupp parent: function(self: Path): Path? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `Path?` | the parent path | ##### `fileName` Returns the final path component, when one exists. ```nupp fileName: function(self: Path): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `string?` | the file name | ##### `stem` Returns the file name without its final extension, when one exists. ```nupp stem: function(self: Path): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `string?` | the file stem | ##### `extension` Returns the final file-name extension without its separator. ```nupp extension: function(self: Path): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `string?` | the extension | ##### `withFileName` Replaces the final path component. ```nupp withFileName: function(self: Path, name: string): Path ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | | `name` | `string` | the replacement file name, without path separators | ###### Returns | Type | Description | | --- | --- | | `Path` | the modified path | ##### `withExtension` Replaces the final file-name extension. ```nupp withExtension: function(self: Path, extension: string): Path ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | | `extension` | `string` | the replacement extension, without path separators | ###### Returns | Type | Description | | --- | --- | | `Path` | the modified path | ##### `isAbsolute` Reports whether this path is absolute. ```nupp isAbsolute: function(self: Path): boolean ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the path is absolute | ##### `isRelative` Reports whether this path is relative. ```nupp isRelative: function(self: Path): boolean ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Path` | this path | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the path is relative | ### `Reader` _interface_ A forward-only byte source. An empty read or zero-byte readInto is EOF. ```nupp interface Reader read: function(self: Reader, count: integer): (string?, string?) readInto: function(self: Reader, destination: Buffer, offset: integer?, count: integer?): (integer?, string?) transferTo: function(self: Reader, destination: Writer): (integer?, string?) close: function(self: Reader): (boolean, string?) end ``` #### Methods ##### `read` Reads the next bytes, returning an empty string at EOF. ```nupp read: function(self: Reader, count: integer): (string?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Reader` | this reader | | `count` | `integer` | the maximum number of bytes; non-positive values still read one | ###### Returns | Type | Description | | --- | --- | | `string?` | the bytes, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `readInto` Reads bytes into a buffer, returning zero at EOF. ```nupp readInto: function( self: Reader, destination: Buffer, offset: integer?, count: integer? ): (integer?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Reader` | this reader | | `destination` | `Buffer` | the buffer to append to or overwrite | | `offset` | `integer?` | the zero-based destination offset, or zero when omitted | | `count` | `integer?` | the maximum bytes to read, or 64 KiB when omitted | ###### Returns | Type | Description | | --- | --- | | `integer?` | the number of bytes read, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `transferTo` Copies all remaining bytes into a writer. ```nupp transferTo: function(self: Reader, destination: Writer): (integer?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Reader` | this reader | | `destination` | `Writer` | the writer to receive the bytes | ###### Returns | Type | Description | | --- | --- | | `integer?` | the number of bytes copied, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `close` Closes this reader. Repeated calls are safe. ```nupp close: function(self: Reader): (boolean, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Reader` | this reader | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the close succeeded | | `string?` | a failure reason, when unsuccessful | ### `URI` _record_ An immutable normalized absolute URI. ```nupp record URI record Components scheme: string userInfo: string? host: string? port: integer? path: string? query: string? fragment: string? end toString: function(self: URI): string scheme: function(self: URI): string authority: function(self: URI): string? username: function(self: URI): string password: function(self: URI): string? host: function(self: URI): string? port: function(self: URI): integer? path: function(self: URI): string query: function(self: URI): string? fragment: function(self: URI): string? userInfo: function(self: URI): string? withScheme: function(self: URI, scheme: string): URI withUserInfo: function(self: URI, userInfo: string?): URI withHost: function(self: URI, host: string?): URI withPort: function(self: URI, port: integer?): URI withPath: function(self: URI, path: string): URI withQuery: function(self: URI, query: string?): URI withFragment: function(self: URI, fragment: string?): URI concatPath: function(self: URI, path: string): URI withEndpoint: function(self: URI, endpoint: URI): URI resolve: function(self: URI, reference: string): (URI?, string?) end ``` #### Methods ##### `toString` Returns the complete normalized URI text. ```nupp toString: function(self: URI): string ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string` | the URI text | ##### `scheme` Returns the normalized URI scheme. ```nupp scheme: function(self: URI): string ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string` | the scheme | ##### `authority` Returns the complete authority component, when present. ```nupp authority: function(self: URI): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string?` | the authority | ##### `username` Returns the username component, or an empty string when absent. ```nupp username: function(self: URI): string ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string` | the username | ##### `password` Returns the password component, when present. ```nupp password: function(self: URI): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string?` | the password | ##### `host` Returns the normalized host, when present. ```nupp host: function(self: URI): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string?` | the host | ##### `port` Returns the port, when explicitly present. ```nupp port: function(self: URI): integer? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `integer?` | the port | ##### `path` Returns the URI path component. ```nupp path: function(self: URI): string ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string` | the path | ##### `query` Returns the query without its leading `?`, when present. ```nupp query: function(self: URI): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string?` | the query | ##### `fragment` Returns the fragment without its leading `#`, when present. ```nupp fragment: function(self: URI): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string?` | the fragment | ##### `userInfo` Returns the complete user-information component, when present. ```nupp userInfo: function(self: URI): string? ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | ###### Returns | Type | Description | | --- | --- | | `string?` | the user information | ##### `withScheme` Returns a copy with a replacement scheme. ```nupp withScheme: function(self: URI, scheme: string): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `scheme` | `string` | the replacement scheme | ###### Returns | Type | Description | | --- | --- | | `URI` | the modified URI | ##### `withUserInfo` Returns a copy with replacement or removed user information. ```nupp withUserInfo: function(self: URI, userInfo: string?): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `userInfo` | `string?` | the replacement, or nil to remove it | ###### Returns | Type | Description | | --- | --- | | `URI` | the modified URI | ##### `withHost` Returns a copy with a replacement or removed host. ```nupp withHost: function(self: URI, host: string?): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `host` | `string?` | the replacement, or nil to remove it | ###### Returns | Type | Description | | --- | --- | | `URI` | the modified URI | ##### `withPort` Returns a copy with a replacement or removed port. ```nupp withPort: function(self: URI, port: integer?): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `port` | `integer?` | the replacement from 0 through 65535, or nil to remove it | ###### Returns | Type | Description | | --- | --- | | `URI` | the modified URI | ##### `withPath` Returns a copy with a replacement path. ```nupp withPath: function(self: URI, path: string): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `path` | `string` | the replacement path | ###### Returns | Type | Description | | --- | --- | | `URI` | the modified URI | ##### `withQuery` Returns a copy with a replacement or removed query. ```nupp withQuery: function(self: URI, query: string?): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `query` | `string?` | the replacement without `?`, or nil to remove it | ###### Returns | Type | Description | | --- | --- | | `URI` | the modified URI | ##### `withFragment` Returns a copy with a replacement or removed fragment. ```nupp withFragment: function(self: URI, fragment: string?): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `fragment` | `string?` | the replacement without `#`, or nil to remove it | ###### Returns | Type | Description | | --- | --- | | `URI` | the modified URI | ##### `concatPath` Appends path text without interpreting it as a URI reference. ```nupp concatPath: function(self: URI, path: string): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `path` | `string` | the path text to append | ###### Returns | Type | Description | | --- | --- | | `URI` | the modified URI | ##### `withEndpoint` Replaces the scheme and authority while retaining resource components. ```nupp withEndpoint: function(self: URI, endpoint: URI): URI ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `endpoint` | `URI` | the URI supplying the scheme and authority | ###### Returns | Type | Description | | --- | --- | | `URI` | the endpoint-rerouted URI | ##### `resolve` Resolves a URI reference according to RFC reference-resolution rules. ```nupp resolve: function(self: URI, reference: string): (URI?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `URI` | this URI | | `reference` | `string` | the URI reference to resolve | ###### Returns | Type | Description | | --- | --- | | `URI?` | the resolved URI, or nil on failure | | `string?` | a failure reason, when unsuccessful | #### Types ##### `Components` _record_ Components accepted when constructing an absolute URI. ###### Fields | Name | Type | Description | | --- | --- | --- | | `scheme` | `string` | The required URI scheme. | | `userInfo` | `string?` | Optional user information, including a password when needed. | | `host` | `string?` | The optional host name or address. | | `port` | `integer?` | The optional port from 0 through 65535. | | `path` | `string?` | The path, or an empty path when omitted. | | `query` | `string?` | The optional query without its leading `?`. | | `fragment` | `string?` | The optional fragment without its leading `#`. | ### `Writer` _interface_ A forward-only byte destination. ```nupp interface Writer write: function(self: Writer, bytes: string): (boolean, string?) writeFrom: function(self: Writer, source: Buffer, offset: integer?, count: integer?): (integer?, string?) writeView: function(self: Writer, source: ByteView, offset: integer?, count: integer?): (integer?, string?) flush: function(self: Writer): (boolean, string?) close: function(self: Writer): (boolean, string?) end ``` #### Methods ##### `write` Writes a string of bytes. ```nupp write: function(self: Writer, bytes: string): (boolean, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | this writer | | `bytes` | `string` | the bytes to write | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the write succeeded | | `string?` | a failure reason, when unsuccessful | ##### `writeFrom` Copies bytes from a mutable buffer. ```nupp writeFrom: function(self: Writer, source: Buffer, offset: integer?, count: integer?): (integer?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | this writer | | `source` | `Buffer` | the source buffer | | `offset` | `integer?` | the zero-based start, or zero when omitted | | `count` | `integer?` | the number of bytes, or the rest of the buffer when omitted | ###### Returns | Type | Description | | --- | --- | | `integer?` | the number of bytes written, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `writeView` Copies bytes from an immutable view. ```nupp writeView: function( self: Writer, source: ByteView, offset: integer?, count: integer? ): (integer?, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | this writer | | `source` | `ByteView` | the source byte view | | `offset` | `integer?` | the zero-based start, or zero when omitted | | `count` | `integer?` | the number of bytes, or the rest of the view when omitted | ###### Returns | Type | Description | | --- | --- | | `integer?` | the number of bytes written, or nil on failure | | `string?` | a failure reason, when unsuccessful | ##### `flush` Flushes any buffered output. ```nupp flush: function(self: Writer): (boolean, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | this writer | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the flush succeeded | | `string?` | a failure reason, when unsuccessful | ##### `close` Closes this writer. Repeated calls are safe. ```nupp close: function(self: Writer): (boolean, string?) ``` ###### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Writer` | this writer | ###### Returns | Type | Description | | --- | --- | | `boolean` | whether the close succeeded | | `string?` | a failure reason, when unsuccessful | ## Functions ### `currentDirectory` _function_ Reads the process's current working directory. ```nupp local currentDirectory: function(): (Path?, string?) ``` #### Returns | Type | Description | | --- | --- | | `Path?` | the current directory, or nil on failure | | `string?` | a failure reason, when unsuccessful | ### `isURI` _function_ Reports whether a value is a URI object. ```nupp local isURI: function(value: any): boolean ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `value` | `any` | the value to inspect | #### Returns | Type | Description | | --- | --- | | `boolean` | whether the value is a URI | ### `separator` _function_ Returns the current platform's primary path separator. ```nupp local separator: function(): string ``` #### Returns | Type | Description | | --- | --- | | `string` | the path separator | ### `validate` _function_ Checks whether text is a valid absolute URI without retaining an object. ```nupp local validate: function(text: string): (boolean, string?) ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `text` | `string` | the URI text to validate | #### Returns | Type | Description | | --- | --- | | `boolean` | whether the text is valid | | `string?` | a failure reason, when invalid |