nupp.io.files
nupp.io.files reads what the filesystem knows about a name: whether it resolves, what it refers to, what a directory contains, and where the platform keeps a user's folders. It also moves and removes names.
local files = nupp.io.files
for _, entry in ipairs(assert(files.list("src"))) do
print(entry.kind, entry.name)
endA file's bytes move through the same Reader and Writer contracts a buffer uses, so a parser written against byte I/O works over a file without knowing one is there.
A path argument is a string or a nupp.io.path.Path; a path result is a string. An operation that fails because of the environment answers nil or false with a reason, and a malformed argument raises at the call site.
Querying a name#
info answers one record of attributes:
local info, reason = files.info("nupp.lua")
assert(info, reason)
assert(info.kind == "file")
print(info.size, info.modified, info.readOnly)info follows symbolic links, so it describes what a name refers to rather than the name itself. size is a byte count, modified is seconds since the Unix epoch with whatever fractional part the platform records, and readOnly is the platform's write refusal rather than a permission model.
exists, isFile and isDirectory answer the same question without the record and without a reason. A missing path is false rather than an error:
if files.isDirectory(candidate) then
return files.list(candidate)
endisSymlink is the one query that does not follow, because following is what would hide the answer:
assert(files.createSymlink("target.txt", "alias"))
assert(files.isSymlink("alias"))
assert(files.isFile("alias"))
assert(files.readLink("alias") == "target.txt")createSymlink takes an optional third argument, "file" or "directory". Only Windows distinguishes the two; elsewhere it is ignored.
Listing a directory#
list answers every entry in a directory, in one call:
Each entry has a name without any directory part and a kind describing the entry itself, so a link inside a listing reads as symlink rather than as what it points at. The order is the platform's, which is not sorted.
The kind comes from the directory itself rather than a second query per name, so listing a large directory costs one call.
Matching paths#
glob expands a pattern into sorted path strings. * and ? stay within one path component, [abc] and [!abc] select characters, and ** crosses directory boundaries. No matches is an empty list; an invalid pattern or a filesystem error answers a reason.
Creating, moving and removing#
These three operations move names rather than bytes, so each answers now:
assert(files.createDirectory("out/lib/native"))
assert(files.rename("out/report.tmp", "out/report.json"))
assert(files.remove("out/stale", true))createDirectory creates every missing parent, and an existing directory succeeds, which is what a caller building a tree wants rather than a race with its own earlier call. rename replaces an existing destination. remove takes a file, a symbolic link, or an empty directory; the second argument removes a directory's contents with it, and without it a populated directory answers a reason.
setReadOnly sets or clears the write refusal that info reports.
Temporary names#
createTemporaryFile and createTemporaryDirectory both take options and answer a path:
local scratch, reason = files.createTemporaryDirectory({prefix = "build-"})
assert(scratch, reason)The file or directory is created, not proposed, so no second caller can take the name between the answer and the use. directory selects where, defaulting to the platform's temporary directory; prefix and suffix bracket the generated part, which is what puts an extension on a temporary file.
A temporary is an owner: closing it removes what it created, and the checker runs that cleanup at the end of the scope whether the block falls through, returns early, or raises.
do
local scratch = assert(files.createTemporaryFile({suffix = ".json"}))
assert(files.write(scratch:toString(), encoded))
assert(scratch:persist("out/report.json"))
endpersist moves it somewhere permanent and discharges the obligation, so the close that follows does nothing. That pair is the reason to make one: write to a name nobody else can take, then put it where it belongs, so a reader never sees a half-written file under the final name.
Reading and writing a whole file#
read and write move a file's complete contents in one call:
local text, reason = files.read("nupp.lua")
assert(text, reason)
assert(files.write("out/report.json", encoded))append adds to the end and creates a missing file. copy duplicates one path over another. writeAtomic writes through a temporary beside the destination and renames over it, so an interrupted write leaves the destination as it was rather than half replaced. A failed write removes the temporary rather than leaving it behind.
A NUL byte is content, not a terminator, in every direction.
lines closes the file when it reaches the end. A trailing carriage return is removed, so a file written on either platform reads the same. Abandoning the iterator early leaves the file open until it is collected; open it yourself when you mean to stop. A read that fails once iteration has begun raises, because the iterator has no failure channel to answer through.
Reading and writing through a cursor#
open hands over a File and the obligation to close it:
do
local file = assert(files.open("image.png"))
local reader = file:newReader()
print(reader:read(8))
endThe reader and writer satisfy nupp.io.Reader and nupp.io.Writer, so read, readSpan, readInto, transferTo, write, writeSpan and flush mean what they mean over a buffer. Native callers can read and write through checked spans without separating a pointer from its bound. readInto commits the destination buffer only after a successful read, and transferTo streams a file of any size through a fixed window:
do
local source = assert(files.open("input.bin"))
local sink = assert(files.open("output.bin", "w"))
print(source:newReader():transferTo(sink:newWriter()))
endmode is r, w, a, or the update modes r+, w+ and a+, matching C and Lua. seek(offset, origin) moves the cursor, with origin one of set, current or end; position and size answer where it is and how long the file is. A reader or writer over a closed file answers a reason rather than raising.
Platform directories#
Two queries answer where the process is and where the platform keeps a user's folders:
print(assert(files.currentDirectory()))
print(assert(files.userFolder("documents")))userFolder takes home, documents, downloads, desktop, pictures, music or videos. It resolves from the environment: the XDG_* variables where they are set, and the platform's conventional names under the home directory otherwise. A desktop that records its folders somewhere else is not consulted, and a folder that does not exist answers a reason rather than a path that is not there.
Waiting#
A whole-file read, write, append, writeAtomic or copy settles on a worker thread rather than on yours, and the call waits for it by suspending. What that means depends on where the call runs, and on nothing the call says:
| Where it runs | Effect |
|---|---|
| an ordinary program | it sleeps, driving the readiness pump |
| under an installed handler | it parks, and the handler resumes it |
inside a nosuspend region |
it is refused, at compile time |
One call site covers all three. A library that reads a file works inside a game frame and inside a command-line program without knowing which it is in, and a transfer that settled before it was observed never reaches any of this.
Names answer now, bytes go to a worker lane
Anything that moves names rather than bytes answers now, because the platform answers it now. Anything that moves a whole file's bytes is submitted to the native library's worker lane and waited for, so a program inside a scheduler yields instead of blocking its host. Making both suspend would tax every stat for a wait that never happens; making neither suspend would block a host on a transfer it cannot see. See Suspension for how one call takes either path.
The immediate operations cannot suspend, so a region that forbids waiting still permits asking what a path is, listing a directory, or renaming one:
do
if files.isDirectory(candidate) then
print(#assert(files.list(candidate)))
end
print(files.read(candidate)) -- NUPP2701: `read` may suspend
endCost#
Reaching nupp.io.files selects the compiler-owned Rust provider, whose filesystem operations use the platform standard library and whose blocking transfers run through the shared native executor, and loads it on first use. A program that never reaches it links nothing and initializes nothing, which is the rule for every facility in the standard library. A target that uses it also carries the suspension runtime, because that is what answers the wait above.
The lane those workers run is bounded three ways: how many transfers may be live, how many bytes they may hold between them, and how large one may be. Past any of the three a submission answers a reason rather than queueing, because a queue that grows with its callers eventually takes the process with it. pendingTransfers answers what the lane is holding.
Metadata, listings and cursor reads through an open File do not use the lane. Scheduling a transfer costs more than those cost to run.
Module contents
Types
| Type | Kind | Description |
|---|---|---|
Entry | record | One directory child, as the directory itself describes it. |
File | record | An open file, and the obligation to close it. |
FileReader | record | A forward-only reader over an open file. |
FileWriter | record | An appending writer over an open file. |
Info | record | One resolved path's attributes. |
Kind | type | What a resolved path refers to. |
LineIterator | type | Answers each line in turn, and nil at the end of the file. |
Mode | type | How an open file may be used. |
Origin | type | What a seek offset is measured from. |
TemporaryOptions | record | Where a temporary is created, and what surrounds its generated name. |
TemporaryPath | record | A created temporary path, and the obligation to settle it. |
UserFolder | type | A well-known user folder. |
Functions
| Function | Kind | Description |
|---|---|---|
append | function | Adds to the end of a file, creating it when it does not exist. |
copy | function | Copies a file's contents over a destination. |
createDirectory | function | Creates a directory and every missing parent. |
createSymlink | function | Creates a symbolic link. |
createTemporaryDirectory | function | Creates a uniquely named empty directory and hands over the obligation to remove or persist it. |
createTemporaryFile | function | Creates a uniquely named empty file and hands over the obligation to remove or persist it. |
currentDirectory | function | Reads the process's current working directory. |
exists | function | Whether a path resolves to anything at all. |
glob | function | Expands a filesystem pattern into matching paths. |
info | function | Describes one path, following symbolic links. |
isDirectory | function | Whether a path resolves to a directory. |
isFile | function | Whether a path resolves to a regular file. |
isSymlink | function | Whether a path is itself a symbolic link, without following it. |
lines | function | Iterates a file's lines, closing it at the end. |
list | function | Lists a directory's immediate children. |
open | function | Opens a file and hands over the obligation to close it. |
pendingTransfers | function | How many whole-file transfers this program is still holding. |
read | function | Reads a whole file. |
readLink | function | Reads a symbolic link's target without resolving it. |
remove | function | Removes a file, a symbolic link, or a directory. |
rename | function | Renames a path, replacing an existing destination. |
setReadOnly | function | Sets or clears a path's read-only attribute. |
userFolder | function | Answers a well-known user folder. |
write | function | Writes a whole file, replacing its contents. |
writeAtomic | function | Writes a whole file through a temporary beside it, so an interrupted write leaves the destination as it was rather... |
Types#
Entryrecord#
One directory child, as the directory itself describes it.
Fields
Filerecord#
record File
function newReader(self): FileReader end
function newWriter(self): FileWriter end
function size(self): (integer?, string?) end
function seek(self, offset: integer?, origin: Origin?): (integer?, string?) end
function position(self): (integer?, string?) end
function flush(self): (boolean, string?) end
function isReleased(self): boolean end
drop: function(takes self: File): nil
function close(takes self): boolean end
endAn open file, and the obligation to close it.
Methods
newReader#
newReader: function newReader(self): FileReaderOpens a forward-only reader at the file's current position.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this file |
Returns
| Type | Description |
|---|---|
FileReader | the new reader |
Raises
when the file is closed
newWriter#
newWriter: function newWriter(self): FileWriterOpens a forward-only writer at the file's current position.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this file |
Returns
| Type | Description |
|---|---|
FileWriter | the new writer |
Raises
when the file is closed
size#
size: function size(self): integer?, string?Answers the file's byte length without moving the cursor.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this file |
Returns
| Type | Description |
|---|---|
integer? | the byte length, or nil on failure |
string? | a failure reason, when unsuccessful |
Raises
when the file is closed
seek#
Moves the cursor and answers where it landed.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | 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 |
Raises
when origin names no seek origin
position#
position: function position(self): integer?, string?Answers the cursor's current position.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this file |
Returns
| Type | Description |
|---|---|
integer? | the position, or nil on failure |
string? | a failure reason, when unsuccessful |
flush#
flush: function flush(self): boolean, string?Pushes buffered writes at the operating system.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this file |
Returns
| Type | Description |
|---|---|
boolean | whether the flush succeeded |
string? | a failure reason, when unsuccessful |
Raises
when the file is closed
isReleased#
isReleased: function isReleased(self): booleanWhether this file has been closed.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this file |
Returns
| Type | Description |
|---|---|
boolean | whether it is closed |
drop#
drop: function(takes self: File): nilCloses the file as an ownership terminal.
Repeated calls are safe. Declared rather than written inline because the cleanup contract needs the nosuspend, and an inline method cannot carry one.
Arguments
| Name | Type | Description |
|---|---|---|
takes self | File | this file, spent by the call |
Returns
| Type | Description |
|---|---|
nil |
FileReaderrecord#
record FileReader is io.Reader
drop: function(takes self: FileReader): nil
function read(self, count: integer): (string?, string?) end
function readSpan(self, exclusive destination: span.Writable<uint8>): (integer?, string?) end
function readInto(self, exclusive destination: io.Buffer, offset: integer?, count: integer?): (integer?, string?) end
function transferTo(self, exclusive destination: io.Writer): (integer?, string?) end
function close(takes self): nil end
endA forward-only reader over an open file.
Methods
drop#
drop: function(takes self: FileReader): nilReleases it as an ownership terminal.
Repeated calls are safe. Declared rather than written inline: a cleanup contract is nosuspend, and an inline method cannot carry one.
Arguments
| Name | Type | Description |
|---|---|---|
takes self | FileReader | this owner, spent by the call |
Returns
| Type | Description |
|---|---|
nil |
read#
Reads up to count bytes.
An empty answer is the end of the file.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this reader |
count | integer | the most bytes to read |
Returns
| Type | Description |
|---|---|
string? | the bytes, or nil when the reader or its file is closed |
string? | why it could not read, when unsuccessful |
Raises
when count is not a positive integer
readSpan#
readSpan: function readSpan(self, exclusive destination: span.Writable<uint8>): integer?, string?Arguments
| Name | Type | Description |
|---|---|---|
self | any | |
exclusive destination | span.Writable<uint8> |
Returns
| Type | Description |
|---|---|
integer? | |
string? |
readInto#
readInto: function readInto(self, exclusive destination: io.Buffer, offset: integer?, count: integer?): integer?, string?Reads into a buffer.
A zero answer is the end of the file.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this reader |
exclusive destination | io.Buffer | the buffer to write into |
offset | integer? | where in the destination to start, or the beginning |
count | integer? | the most bytes to read |
Returns
| Type | Description |
|---|---|
integer? | how many bytes were read, or nil on failure |
string? | why it could not read, when unsuccessful |
transferTo#
transferTo: function transferTo(self, exclusive destination: io.Writer): integer?, string?Reads the rest of the file into a writer.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this reader |
exclusive destination | io.Writer | the writer to fill |
Returns
| Type | Description |
|---|---|
integer? | how many bytes moved, or nil on failure |
string? | why it could not, when unsuccessful |
FileWriterrecord#
record FileWriter is io.Writer
drop: function(takes self: FileWriter): nil
function write(exclusive self, bytes: string): (boolean, string?) end
function writeSpan(exclusive self, borrows source: span.ByteSpan): (integer?, string?) end
function flush(self): (boolean, string?) end
function close(takes self): nil end
endAn appending writer over an open file.
Methods
drop#
drop: function(takes self: FileWriter): nilReleases it as an ownership terminal.
Repeated calls are safe. Declared rather than written inline: a cleanup contract is nosuspend, and an inline method cannot carry one.
Arguments
| Name | Type | Description |
|---|---|---|
takes self | FileWriter | this owner, spent by the call |
Returns
| Type | Description |
|---|---|
nil |
write#
Writes bytes at the file's current position.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive self | any | this writer |
bytes | string | the bytes to write |
Returns
| Type | Description |
|---|---|
boolean | whether they were written |
string? | why they were not, when unsuccessful |
Raises
when bytes is not a string
writeSpan#
writeSpan: function writeSpan(exclusive self, borrows source: span.ByteSpan): integer?, string?Arguments
| Name | Type | Description |
|---|---|---|
exclusive self | any | |
borrows source | span.ByteSpan |
Returns
| Type | Description |
|---|---|
integer? | |
string? |
Inforecord#
One resolved path's attributes.
Fields
modified#
modified: numberSeconds since the Unix epoch, with a fractional part where the platform records one.
Kindtype#
type Kind = "file" | "directory" | "symlink" | "other"What a resolved path refers to. A symlink answer only ever comes from isSymlink, since every other operation follows the link first.
LineIteratortype#
type LineIterator = function(): string?Answers each line in turn, and nil at the end of the file.
Modetype#
type Mode = "r" | "w" | "a" | "r+" | "w+" | "a+"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.
Origintype#
type Origin = "set" | "current" | "end"What a seek offset is measured from.
TemporaryOptionsrecord#
Where a temporary is created, and what surrounds its generated name.
Fields
TemporaryPathrecord#
record TemporaryPath
function toString(self): string end
function persist(self, destination: string | paths.Path): (boolean, string?) end
function isReleased(self): boolean end
drop: function(takes self: TemporaryPath): nil
function close(takes self): boolean end
__tostring: function(self): string
endA 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#
toString: function toString(self): stringReturns the created path.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this temporary path |
Returns
| Type | Description |
|---|---|
string | the path text |
persist#
persist: function persist(self, destination: string | paths.Path): boolean, string?Moves this path to a permanent destination, replacing what is there.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this temporary path |
destination | string | paths.Path | where to move it |
Returns
| Type | Description |
|---|---|
boolean | whether the move happened |
string? | a failure reason, when unsuccessful |
isReleased#
isReleased: function isReleased(self): booleanWhether this path has been removed or persisted.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | this temporary path |
Returns
| Type | Description |
|---|---|
boolean | whether it is settled |
drop#
drop: function(takes self: TemporaryPath): nilRemoves the path as an ownership terminal.
Repeated calls, and a call after persist, are safe and do nothing. Declared rather than written inline because the cleanup contract needs the nosuspend, and an inline method cannot carry one.
Arguments
| Name | Type | Description |
|---|---|---|
takes self | TemporaryPath | this temporary path, spent by the call |
Returns
| Type | Description |
|---|---|
nil |
close#
close: function close(takes self): booleanRemoves the path and reports whether it succeeded.
Repeated calls, and a call after persist, are safe and do nothing.
Arguments
| Name | Type | Description |
|---|---|---|
takes self | any | this temporary path, spent by the call |
Returns
| Type | Description |
|---|---|
boolean | whether the removal succeeded |
__tostring#
__tostring: function(self): stringArguments
| Name | Type | Description |
|---|---|---|
? | self |
Returns
| Type | Description |
|---|---|
string |
UserFoldertype#
type UserFolder = "home" | "documents" | "downloads" | "desktop" | "pictures" | "music" | "videos"A well-known user folder. Resolved from the environment, so a desktop that records its folders elsewhere is not consulted.
Functions#
appendfunction#
Adds to the end of a file, creating it when it does not exist.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the file to extend |
bytes | string | io.ByteView | what to add |
Returns
| Type | Description |
|---|---|
boolean | whether the write succeeded |
string? | a failure reason, when unsuccessful |
copyfunction#
Copies a file's contents over a destination.
Arguments
| Name | Type | Description |
|---|---|---|
from | string | paths.Path | the file to copy |
to | string | paths.Path | where to copy it |
Returns
| Type | Description |
|---|---|
boolean | whether the copy succeeded |
string? | a failure reason, when unsuccessful |
createDirectoryfunction#
function createDirectory(path: string | paths.Path): boolean, string?Creates a directory and every missing parent.
An existing directory succeeds.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the directory to create |
Returns
| Type | Description |
|---|---|
boolean | whether the directory exists afterwards |
string? | a failure reason, when unsuccessful |
createSymlinkfunction#
function createSymlink(target: string | paths.Path, link: string | paths.Path, kind: ("file" | "directory")?): boolean, string?Creates a symbolic link.
kind selects Windows's directory link and is ignored elsewhere, because only Windows distinguishes the two.
Arguments
| Name | Type | Description |
|---|---|---|
target | string | paths.Path | what the link points at |
link | string | paths.Path | where to create the link |
kind | ("file" | "directory")? | the link kind, defaulting to a file link |
Returns
| Type | Description |
|---|---|
boolean | whether the link was created |
string? | a failure reason, when unsuccessful |
Raises
when kind names neither a file nor a directory link
createTemporaryDirectoryfunction#
function createTemporaryDirectory(options: TemporaryOptions?): affine(TemporaryPath?, destroyOwner), string?Creates a uniquely named empty directory and hands over the obligation to remove or persist it.
Arguments
| Name | Type | Description |
|---|---|---|
options | TemporaryOptions? | where to create it and how to name it |
Returns
| Type | Description |
|---|---|
affine(TemporaryPath?, destroyOwner) | the created path, or nil on failure |
string? | a failure reason, when unsuccessful |
createTemporaryFilefunction#
function createTemporaryFile(options: TemporaryOptions?): affine(TemporaryPath?, destroyOwner), string?Creates a uniquely named empty file and hands over the obligation to remove or persist it.
The file is created rather than merely proposed, so no second caller can take the name in between.
Closing the returned path removes it. persist settles it instead, which is what makes the pair a replacement for writing over a destination in place.
Examples#
Write somewhere nobody else can take, then move it into place:
do
local scratch = assert(nupp.io.files.createTemporaryFile({directory = "build"}))
assert(nupp.io.files.write(scratch:toString(), bytes))
assert(scratch:persist("build/output.bin"))
endArguments
| Name | Type | Description |
|---|---|---|
options | TemporaryOptions? | where to create it and how to name it |
Returns
| Type | Description |
|---|---|
affine(TemporaryPath?, destroyOwner) | the created path, or nil on failure |
string? | a failure reason, when unsuccessful |
currentDirectoryfunction#
function currentDirectory(): string?, string?Reads the process's current working directory.
Returns
| Type | Description |
|---|---|
string? | the current directory, or nil on failure |
string? | a failure reason, when unsuccessful |
existsfunction#
function exists(path: string | paths.Path): booleanWhether a path resolves to anything at all.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the path to test |
Returns
| Type | Description |
|---|---|
boolean | whether it resolves |
globfunction#
function glob(pattern: string | paths.Path): {string}?, string?Expands a filesystem pattern into matching paths.
* and ? match within one path component, character classes use [abc] or [!abc], and ** crosses directory boundaries. The returned paths are sorted, and no matches is an empty list.
Examples#
Walk every source file under a directory:
Arguments
| Name | Type | Description |
|---|---|---|
pattern | string | paths.Path | the filesystem pattern to expand |
Returns
| Type | Description |
|---|---|
{string}? | the matching paths, or nil when the pattern or walk fails |
string? | a failure reason, when unsuccessful |
infofunction#
Describes one path, following symbolic links.
Examples#
Read a file's size:
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the path to describe |
Returns
| Type | Description |
|---|---|
Info? | the attributes, or nil when the path cannot be read |
string? | a failure reason, when unsuccessful |
isDirectoryfunction#
function isDirectory(path: string | paths.Path): booleanWhether a path resolves to a directory.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the path to test |
Returns
| Type | Description |
|---|---|
boolean | whether it is a directory |
isFilefunction#
function isFile(path: string | paths.Path): booleanWhether a path resolves to a regular file.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the path to test |
Returns
| Type | Description |
|---|---|
boolean | whether it is a file |
isSymlinkfunction#
function isSymlink(path: string | paths.Path): booleanWhether a path is itself a symbolic link, without following it.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the path to test |
Returns
| Type | Description |
|---|---|
boolean | whether it is a symbolic link |
linesfunction#
function lines(path: string | paths.Path): LineIterator?, string?Iterates a file's lines, closing it at the end.
A trailing carriage return is removed, so a file written on either platform reads the same. The iterator stops at the end of the file; abandoning it early leaves the file open until it is collected. failure channel to answer through
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the file to read |
Returns
| Type | Description |
|---|---|
LineIterator? | an iterator answering each line, or nil on failure |
string? | a failure reason, when unsuccessful |
Raises
when a read fails once iteration has begun, since the iterator has no
listfunction#
Lists a directory's immediate children.
Examples#
Count the modules beside a file:
local entries = assert(nupp.io.files.list("src"))
for _, entry in ipairs(entries) do
print(entry.kind, entry.name)
endArguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the directory to list |
Returns
| Type | Description |
|---|---|
{Entry}? | the children in the platform's order, or nil on failure |
string? | a failure reason, when unsuccessful |
openfunction#
Opens a file and hands over the obligation to close it.
Examples#
Read a header without holding the whole file:
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the file to open |
mode | Mode? | how it may be used, defaulting to reading |
Returns
| Type | Description |
|---|---|
affine(File?, destroyOwner) | the open file, or nil on failure |
string? | a failure reason, when unsuccessful |
Raises
when mode names no open mode
pendingTransfersfunction#
function pendingTransfers(): integerHow many whole-file transfers this program is still holding.
Whole-file reads, writes and copies settle on worker threads, and the lane that runs them is bounded. This answers what it holds, which is what a program that submitted more than it consumed needs to see.
Returns
| Type | Description |
|---|---|
integer | the number of live transfers |
readfunction#
function read(path: string | paths.Path): string?, string?Reads a whole file.
Examples#
Read a file's bytes, or report why not:
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the file to read |
Returns
| Type | Description |
|---|---|
string? | the contents, or nil on failure |
string? | a failure reason, when unsuccessful |
readLinkfunction#
function readLink(path: string | paths.Path): string?, string?Reads a symbolic link's target without resolving it.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the link to read |
Returns
| Type | Description |
|---|---|
string? | the target text, or nil on failure |
string? | a failure reason, when unsuccessful |
removefunction#
function remove(path: string | paths.Path, recursive: boolean?): boolean, string?Removes a file, a symbolic link, or a directory.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the path to remove |
recursive | boolean? | whether to remove a directory's contents with it |
Returns
| Type | Description |
|---|---|
boolean | whether the path was removed |
string? | a failure reason, when unsuccessful |
renamefunction#
Renames a path, replacing an existing destination.
Arguments
| Name | Type | Description |
|---|---|---|
from | string | paths.Path | the path to rename |
to | string | paths.Path | the new path |
Returns
| Type | Description |
|---|---|
boolean | whether the rename happened |
string? | a failure reason, when unsuccessful |
setReadOnlyfunction#
function setReadOnly(path: string | paths.Path, readOnly: boolean): boolean, string?Sets or clears a path's read-only attribute.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the path to change |
readOnly | boolean | whether to refuse writes |
Returns
| Type | Description |
|---|---|
boolean | whether the attribute was set |
string? | a failure reason, when unsuccessful |
userFolderfunction#
function userFolder(which: UserFolder): string?, string?Answers a well-known user folder.
Arguments
| Name | Type | Description |
|---|---|---|
which | UserFolder | the folder to locate |
Returns
| Type | Description |
|---|---|
string? | the folder, or nil when the platform has no such folder |
string? | a failure reason, when unsuccessful |
Raises
when which names no user folder
writefunction#
Writes a whole file, replacing its contents.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the file to write |
bytes | string | io.ByteView | the contents |
Returns
| Type | Description |
|---|---|
boolean | whether the write succeeded |
string? | a failure reason, when unsuccessful |
writeAtomicfunction#
Writes a whole file through a temporary beside it, so an interrupted write leaves the destination as it was rather than half replaced.
Prefer this to write wherever a reader may be looking at the destination while it is being replaced, since write truncates in place.
Arguments
| Name | Type | Description |
|---|---|---|
path | string | paths.Path | the file to write |
bytes | string | io.ByteView | the contents |
Returns
| Type | Description |
|---|---|
boolean | whether the write succeeded |
string? | a failure reason, when unsuccessful |