
# tecs.io.files


Resolves game paths and performs complete filesystem operations.

Use `assetPath` for shipped content, `writablePath` for persistent mutable
state, and `cachePath` for data the application can reconstruct.
Set the publisher and game names before resolving the first writable path:

```teal
local files <const> = tecs.io.files
files.setPreferenceIdentity("Ex Nihilo", "Starfarer")

local directory <const> = files.writablePath("saves")
local ok, reason = files.createDirectory(directory)
if not ok then
    error(reason)
end

local path <const> = files.writablePath("saves/slot1.json")
local bytes <const> = tecs.data.encodeJSON({level = 3, hp = 100})
ok, reason = files.writeAtomic(path, bytes)
if not ok then
    error(reason)
end
```

Filesystem outcomes return a status and a platform reason. Invalid arguments
raise because they indicate a defect in the calling program. Operations are
synchronous except `writeAtomicAsync`, whose worker-backed future is advanced
by `tecs.runtime.poll`. `glob` exposes a caller-owned pull stream so recursive
enumeration does not retain a complete tree. Temporary files,
temporary directories, and streams are [`Closeable`](/modules/#tecs.Closeable)
and fit directly in `tecs.scoped`.

Every operation that accepts a filesystem location accepts either a string or
an immutable [`Path`](/modules/io/Path/). String results remain useful
at Lua and platform boundaries; wrap one with `tecs.io.Path.new` when the next
operation benefits from component-aware manipulation.

`tecs.io.watcher` polls paths recorded by `read` and the built-in asset
loaders. It does not walk the whole asset tree.

`open` uses the standard Lua modes and returns one seekable file cursor. The
SDL backend operates on the file directly. A storage backend without random
access falls back to its required whole-file operations and retains the bytes
until the file closes.

## Module contents

### Types

| Type | Kind | Description |
| --- | --- | --- |
| [`DirectoryEntry`](/modules/io/files/#tecs.io.files.DirectoryEntry) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | DirectoryEntry describes one streamed child. |
| [`DirectoryStream`](/modules/io/files/#tecs.io.files.DirectoryStream) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | DirectoryStream is a caller-owned pull cursor over directory entries. |
| [`File`](/modules/io/files/#tecs.io.files.File) | <span class="tealdoc-kind-badge tealdoc-kind-interface">interface</span> | File is the seekable cursor returned by open. |
| [`FileMode`](/modules/io/files/#tecs.io.files.FileMode) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | FileMode selects standard read, write, append, or update behavior. |
| [`GlobOptions`](/modules/io/files/#tecs.io.files.GlobOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | GlobOptions controls pattern matching and traversal depth. |
| [`Info`](/modules/io/files/#tecs.io.files.Info) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Info describes one resolved path. |
| [`LineIterator`](/modules/io/files/#tecs.io.files.LineIterator) | <span class="tealdoc-kind-badge tealdoc-kind-type">type</span> | LineIterator yields each line in a file and returns nil at the end. |
| [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | <span class="tealdoc-kind-badge tealdoc-kind-type">type</span> | PathInput accepts either a platform path string or an immutable path object. |
| [`PathType`](/modules/io/files/#tecs.io.files.PathType) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | PathType identifies a file, directory or other platform object after following symbolic links. |
| [`SymlinkKind`](/modules/io/files/#tecs.io.files.SymlinkKind) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | SymlinkKind selects a file or directory symbolic link. |
| [`TemporaryOptions`](/modules/io/files/#tecs.io.files.TemporaryOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | TemporaryOptions selects generated-name details. |
| [`TemporaryPath`](/modules/io/files/#tecs.io.files.TemporaryPath) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | TemporaryPath owns an automatically removed file or directory. |
| [`UserFolder`](/modules/io/files/#tecs.io.files.UserFolder) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | UserFolder identifies a well-known platform folder. |

### Functions

| Function | Kind | Description |
| --- | --- | --- |
| [`assetPath`](/modules/io/files/#tecs.io.files.assetPath) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Resolves relative against the asset root. |
| [`assetRoot`](/modules/io/files/#tecs.io.files.assetRoot) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the root from which the engine reads content. |
| [`basePath`](/modules/io/files/#tecs.io.files.basePath) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the directory that contains the executable. |
| [`cachePath`](/modules/io/files/#tecs.io.files.cachePath) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the system cache directory for this application and creates it if absent. |
| [`copy`](/modules/io/files/#tecs.io.files.copy) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Copies the file at from to to, replacing whatever was at to. |
| [`createDirectory`](/modules/io/files/#tecs.io.files.createDirectory) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Creates path, and any parents it needs. |
| [`createSymlink`](/modules/io/files/#tecs.io.files.createSymlink) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Creates a symbolic link to a file or directory. |
| [`createTemporaryDirectory`](/modules/io/files/#tecs.io.files.createTemporaryDirectory) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Creates an empty temporary directory owned by the returned resource. |
| [`createTemporaryFile`](/modules/io/files/#tecs.io.files.createTemporaryFile) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Creates an empty temporary file owned by the returned resource. |
| [`currentDirectory`](/modules/io/files/#tecs.io.files.currentDirectory) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the process working directory. |
| [`exists`](/modules/io/files/#tecs.io.files.exists) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns whether anything occupies path. |
| [`glob`](/modules/io/files/#tecs.io.files.glob) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Opens a pull stream over entries under path that match pattern. |
| [`info`](/modules/io/files/#tecs.io.files.info) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns information about path, or nil when nothing occupies it. |
| [`isDirectory`](/modules/io/files/#tecs.io.files.isDirectory) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns whether path resolves to a directory. |
| [`isFile`](/modules/io/files/#tecs.io.files.isFile) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns whether path resolves to a regular file. |
| [`isSymlink`](/modules/io/files/#tecs.io.files.isSymlink) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns whether the final object named by path is a symbolic link. |
| [`lines`](/modules/io/files/#tecs.io.files.lines) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Iterates over the lines in a whole file. |
| [`load`](/modules/io/files/#tecs.io.files.load) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Compiles a Lua file without running it. |
| [`open`](/modules/io/files/#tecs.io.files.open) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Opens a file through one seekable cursor. |
| [`preferenceIdentity`](/modules/io/files/#tecs.io.files.preferenceIdentity) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the publisher and game names used for writable user data. |
| [`preferencePath`](/modules/io/files/#tecs.io.files.preferencePath) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the writable directory for this application and creates it if absent. |
| [`read`](/modules/io/files/#tecs.io.files.read) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Reads a whole file and returns its bytes, or nil when there is none there. |
| [`readLink`](/modules/io/files/#tecs.io.files.readLink) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the target spelling stored in a symbolic link. |
| [`remove`](/modules/io/files/#tecs.io.files.remove) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Removes a file, or an empty directory. |
| [`rename`](/modules/io/files/#tecs.io.files.rename) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Moves from to to, replacing whatever was at to. |
| [`setAssetRoot`](/modules/io/files/#tecs.io.files.setAssetRoot) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Overrides the asset root, for a test or a tool. |
| [`setPreferenceIdentity`](/modules/io/files/#tecs.io.files.setPreferenceIdentity) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Sets the publisher and game names used for writable user data. |
| [`setReadOnly`](/modules/io/files/#tecs.io.files.setReadOnly) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Changes the portable read-only state of a path. |
| [`userFolder`](/modules/io/files/#tecs.io.files.userFolder) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns one of the platform's well-known folders. |
| [`writablePath`](/modules/io/files/#tecs.io.files.writablePath) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Resolves relative against the writable root. |
| [`write`](/modules/io/files/#tecs.io.files.write) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Writes bytes to path, replacing whatever was there. |
| [`writeAtomic`](/modules/io/files/#tecs.io.files.writeAtomic) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Durably writes bytes and atomically replaces path. |
| [`writeAtomicAsync`](/modules/io/files/#tecs.io.files.writeAtomicAsync) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Durably and atomically writes a complete file on a worker. |

## Types

<a id="tecs.io.files.DirectoryEntry"></a>
### tecs.io.files.DirectoryEntry <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`DirectoryEntry` describes one streamed child.


```teal
record tecs.io.files.DirectoryEntry
    path: Path
    name: string
    kind: storagebackend.PathType
    depth: integer
    symlink: boolean
end
```

<a id="tecs.io.files.DirectoryEntry.path"></a>
#### tecs.io.files.DirectoryEntry.path <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the complete immutable path to this entry.


```teal
tecs.io.files.DirectoryEntry.path: Path
```

<a id="tecs.io.files.DirectoryEntry.name"></a>
#### tecs.io.files.DirectoryEntry.name <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the entry name relative to its immediate parent.


```teal
tecs.io.files.DirectoryEntry.name: string
```

<a id="tecs.io.files.DirectoryEntry.kind"></a>
#### tecs.io.files.DirectoryEntry.kind <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Identifies the resolved object's kind.


```teal
tecs.io.files.DirectoryEntry.kind: storagebackend.PathType
```

<a id="tecs.io.files.DirectoryEntry.depth"></a>
#### tecs.io.files.DirectoryEntry.depth <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports one for an immediate child and increases during a recursive glob.


```teal
tecs.io.files.DirectoryEntry.depth: integer
```

<a id="tecs.io.files.DirectoryEntry.symlink"></a>
#### tecs.io.files.DirectoryEntry.symlink <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports whether the final path itself is a symbolic link.


```teal
tecs.io.files.DirectoryEntry.symlink: boolean
```

<a id="tecs.io.files.DirectoryStream"></a>
### tecs.io.files.DirectoryStream <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`DirectoryStream` is a caller-owned pull cursor over directory entries.


```teal
record tecs.io.files.DirectoryStream is Closeable
    close: function(self): boolean, string
    next: function(self): DirectoryEntry, string
    skipDirectory: function(self): boolean
    toArray: function(self): {DirectoryEntry}, string
end
```

#### Interfaces

| Interface |
| --- |
| `Closeable` |

<a id="tecs.io.files.DirectoryStream.close"></a>
#### tecs.io.files.DirectoryStream:close <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

```teal
function tecs.io.files.DirectoryStream.close(self): boolean, string
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `DirectoryStream` |  |

##### Returns

| Type | Description |
| --- | --- |
| `boolean` |  |
| `string` |  |

<a id="tecs.io.files.DirectoryStream.next"></a>
#### tecs.io.files.DirectoryStream:next <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Returns the next entry.



```teal
function tecs.io.files.DirectoryStream.next(
    self
): DirectoryEntry, string
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `DirectoryStream` | The open stream to advance. |

##### Returns

| Type | Description |
| --- | --- |
| [`DirectoryEntry`](/modules/io/files/#tecs.io.files.DirectoryEntry) | Returns the next entry, or nil at the end or after close. |
| `string` | Returns a platform reason when traversal fails. Nil means end of stream. |

<a id="tecs.io.files.DirectoryStream.skipDirectory"></a>
#### tecs.io.files.DirectoryStream:skipDirectory <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Prevents descent into the directory returned by the preceding `next` call.

The method returns false when the preceding entry was not a directory,
was a symbolic link, had no deeper pattern component, or has already
been skipped. Calling `next` first makes an earlier directory no longer
eligible for pruning.



```teal
function tecs.io.files.DirectoryStream.skipDirectory(self): boolean
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `DirectoryStream` | The open stream whose pending descent the caller controls. |

##### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether a pending directory descent was removed. |

<a id="tecs.io.files.DirectoryStream.toArray"></a>
#### tecs.io.files.DirectoryStream:toArray <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Consumes the remaining entries into an array and closes the stream.

Entries returned by earlier `next` calls are not repeated. A traversal
failure discards the partial array and closes the stream.



```teal
function tecs.io.files.DirectoryStream.toArray(
    self
): {DirectoryEntry}, string
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `DirectoryStream` | The open stream to consume. |

##### Returns

| Type | Description |
| --- | --- |
| `{`[`DirectoryEntry`](/modules/io/files/#tecs.io.files.DirectoryEntry)`}` | Returns every remaining entry, or nil when traversal fails. |
| `string` | Returns the platform reason when the first return is nil. |

<a id="tecs.io.files.File"></a>
### tecs.io.files.File <span class="tealdoc-kind-badge tealdoc-kind-interface">interface</span>

`File` is the seekable cursor returned by `open`.


```teal
interface tecs.io.files.File is types.SeekableReader, types.SeekableWriter
end
```

#### Interfaces

| Interface |
| --- |
| [`types.SeekableReader`](/modules/io/#tecs.io.SeekableReader) |
| [`types.SeekableWriter`](/modules/io/#tecs.io.SeekableWriter) |

<a id="tecs.io.files.FileMode"></a>
### tecs.io.files.FileMode <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

`FileMode` selects standard read, write, append, or update behavior.


```teal
enum tecs.io.files.FileMode
    "a"
    "a+"
    "r"
    "r+"
    "w"
    "w+"
end
```

<a id="tecs.io.files.GlobOptions"></a>
### tecs.io.files.GlobOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`GlobOptions` controls pattern matching and traversal depth.


```teal
record tecs.io.files.GlobOptions
    caseInsensitive: boolean
    maxDepth: integer
end
```

<a id="tecs.io.files.GlobOptions.caseInsensitive"></a>
#### tecs.io.files.GlobOptions.caseInsensitive <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Matches ASCII letters without regard to case when
true. Defaults to false, which compares every UTF-8 codepoint exactly
on every platform including the ones whose filesystem is not.


```teal
tecs.io.files.GlobOptions.caseInsensitive: boolean
```

<a id="tecs.io.files.GlobOptions.maxDepth"></a>
#### tecs.io.files.GlobOptions.maxDepth <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Limits traversal to this many levels below the root.
Zero yields no entries, one yields immediate children, and nil permits
unlimited traversal. Defaults to nil.


```teal
tecs.io.files.GlobOptions.maxDepth: integer
```

<a id="tecs.io.files.Info"></a>
### tecs.io.files.Info <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Info` describes one resolved path.


```teal
record tecs.io.files.Info
    kind: PathType
    size: integer
    createdAt: number
    modifiedAt: number
    accessedAt: number
    readOnly: boolean
end
```

<a id="tecs.io.files.Info.kind"></a>
#### tecs.io.files.Info.kind <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Identifies the resolved platform object's kind.


```teal
tecs.io.files.Info.kind: PathType
```

<a id="tecs.io.files.Info.size"></a>
#### tecs.io.files.Info.size <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports size in bytes. Zero for a directory.


```teal
tecs.io.files.Info.size: integer
```

<a id="tecs.io.files.Info.createdAt"></a>
#### tecs.io.files.Info.createdAt <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports nanoseconds since the epoch as a double. Zero means
the platform or filesystem does not record a creation time.


```teal
tecs.io.files.Info.createdAt: number
```

<a id="tecs.io.files.Info.modifiedAt"></a>
#### tecs.io.files.Info.modifiedAt <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports modification time in nanoseconds since the epoch.


```teal
tecs.io.files.Info.modifiedAt: number
```

<a id="tecs.io.files.Info.accessedAt"></a>
#### tecs.io.files.Info.accessedAt <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports access time in nanoseconds since the epoch.


```teal
tecs.io.files.Info.accessedAt: number
```

<a id="tecs.io.files.Info.readOnly"></a>
#### tecs.io.files.Info.readOnly <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the portable read-only state of the resolved path.


```teal
tecs.io.files.Info.readOnly: boolean
```

<a id="tecs.io.files.LineIterator"></a>
### tecs.io.files.LineIterator <span class="tealdoc-kind-badge tealdoc-kind-type">type</span>

`LineIterator` yields each line in a file and returns nil at the end.


```teal
type tecs.io.files.LineIterator = function(): string
```

<a id="tecs.io.files.PathInput"></a>
### tecs.io.files.PathInput <span class="tealdoc-kind-badge tealdoc-kind-type">type</span>

`PathInput` accepts either a platform path string or an immutable path object.


```teal
type tecs.io.files.PathInput = string | Path
```

<a id="tecs.io.files.PathType"></a>
### tecs.io.files.PathType <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

`PathType` identifies a file, directory or other
platform object after following symbolic links.


```teal
enum tecs.io.files.PathType
    "directory"
    "file"
    "other"
end
```

<a id="tecs.io.files.SymlinkKind"></a>
### tecs.io.files.SymlinkKind <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

`SymlinkKind` selects a file or directory symbolic link.


```teal
enum tecs.io.files.SymlinkKind
    "directory"
    "file"
end
```

<a id="tecs.io.files.TemporaryOptions"></a>
### tecs.io.files.TemporaryOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`TemporaryOptions` selects generated-name details.


```teal
record tecs.io.files.TemporaryOptions
    directory: string | Path
    prefix: string
    suffix: string
end
```

<a id="tecs.io.files.TemporaryOptions.directory"></a>
#### tecs.io.files.TemporaryOptions.directory <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the parent directory. Nil uses the operating
system's temporary directory.


```teal
tecs.io.files.TemporaryOptions.directory: string | Path
```

<a id="tecs.io.files.TemporaryOptions.prefix"></a>
#### tecs.io.files.TemporaryOptions.prefix <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Prefixes the generated name. Defaults to `"tecs-"`.


```teal
tecs.io.files.TemporaryOptions.prefix: string
```

<a id="tecs.io.files.TemporaryOptions.suffix"></a>
#### tecs.io.files.TemporaryOptions.suffix <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Suffixes the generated name. Defaults to an empty string.


```teal
tecs.io.files.TemporaryOptions.suffix: string
```

<a id="tecs.io.files.TemporaryPath"></a>
### tecs.io.files.TemporaryPath <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`TemporaryPath` owns an automatically removed file or directory.


```teal
record tecs.io.files.TemporaryPath is Closeable
    path: Path

    close: function(self): boolean, string
    persist: function(self, destination: string | Path): boolean, string
end
```

#### Interfaces

| Interface |
| --- |
| `Closeable` |

<a id="tecs.io.files.TemporaryPath.path"></a>
#### tecs.io.files.TemporaryPath.path <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Contains the exclusively created path.


```teal
tecs.io.files.TemporaryPath.path: Path
```

<a id="tecs.io.files.TemporaryPath.close"></a>
#### tecs.io.files.TemporaryPath:close <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

```teal
function tecs.io.files.TemporaryPath.close(self): boolean, string
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `TemporaryPath` |  |

##### Returns

| Type | Description |
| --- | --- |
| `boolean` |  |
| `string` |  |

<a id="tecs.io.files.TemporaryPath.persist"></a>
#### tecs.io.files.TemporaryPath:persist <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Moves the resource to a permanent, absent destination and relinquishes cleanup.



```teal
function tecs.io.files.TemporaryPath.persist(
    self, destination: string | Path
): boolean, string
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `TemporaryPath` | The open temporary resource. |
| `destination` | <code>string &#124; </code>[`Path`](/modules/io/Path/) | The caller supplies a destination which must not exist. |

##### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether the resource became permanent. |
| `string` | Returns the platform reason when the first return is false. |

<a id="tecs.io.files.UserFolder"></a>
### tecs.io.files.UserFolder <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

`UserFolder` identifies a well-known platform
folder.


```teal
enum tecs.io.files.UserFolder
    "desktop"
    "documents"
    "downloads"
    "home"
    "music"
    "pictures"
    "publicShare"
    "savedGames"
    "screenshots"
    "templates"
    "videos"
end
```

## Functions

<a id="tecs.io.files.assetPath"></a>
### tecs.io.files.assetPath <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Resolves `relative` against the asset root.



```teal
function tecs.io.files.assetPath(relative: PathInput): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `relative` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies a path under the content root with `/` separators. The function joins it without checking existence. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the absolute path. |

<a id="tecs.io.files.assetRoot"></a>
### tecs.io.files.assetRoot <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the root from which the engine reads content.



```teal
function tecs.io.files.assetRoot(): string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns `TECS_ASSETS` when set, otherwise the host-configured root or `basePath`. The value stays cached until the platform changes. |

<a id="tecs.io.files.basePath"></a>
### tecs.io.files.basePath <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the directory that contains the executable.



```teal
function tecs.io.files.basePath(): string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the directory with a trailing separator. It returns an empty string rather than nil when the platform declines to say, which is the case on some targets and is not an error: a caller falls back to a relative path. |

<a id="tecs.io.files.cachePath"></a>
### tecs.io.files.cachePath <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the system cache directory for this application and creates it if
absent.

The operating system may empty this directory between any two runs. Store
only reconstructable downloads, compiled artifacts, thumbnails and similar
derived data here. Saves, settings and other durable state belong under
`preferencePath`.



```teal
function tecs.io.files.cachePath(): string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the directory with a trailing platform separator, named from the preference identity. |

<a id="tecs.io.files.copy"></a>
### tecs.io.files.copy <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Copies the file at `from` to `to`, replacing whatever was at `to`.

The function refuses directories and requires an existing destination
parent.



```teal
function tecs.io.files.copy(
    from: PathInput, to: PathInput
): boolean, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `from` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the existing source file. |
| `to` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the destination file to replace. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether the platform copied the file. |
| `string` | Returns the platform reason when the first return is false. |

<a id="tecs.io.files.createDirectory"></a>
### tecs.io.files.createDirectory <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Creates `path`, and any parents it needs.

It succeeds when the directory already exists.



```teal
function tecs.io.files.createDirectory(path: PathInput): boolean, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the directory to create. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether the platform created or found the directory. |
| `string` | Returns the platform reason when the first return is false. |

<a id="tecs.io.files.createSymlink"></a>
### tecs.io.files.createSymlink <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Creates a symbolic link to a file or directory.

The target spelling is stored unchanged, so a relative target is resolved
from the link's parent when later opened. `kind` is required because
Windows must choose a file or directory link even for a dangling target.



```teal
function tecs.io.files.createSymlink(
    target: PathInput, link: PathInput, kind: SymlinkKind
): boolean, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `target` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the target spelling to store. |
| `link` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the new link path. |
| `kind` | [`SymlinkKind`](/modules/io/files/#tecs.io.files.SymlinkKind) | The caller supplies `"file"` or `"directory"`. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether the platform created the link. |
| `string` | Returns the platform or unsupported reason when the first return is false. |

<a id="tecs.io.files.createTemporaryDirectory"></a>
### tecs.io.files.createTemporaryDirectory <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Creates an empty temporary directory owned by the returned resource.

Closing recursively removes its contents without following symbolic links.
Call `persist` to keep the complete tree under an absent destination.



```teal
function tecs.io.files.createTemporaryDirectory(
    options: TemporaryOptions
): TemporaryPath, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `options` | [`TemporaryOptions`](/modules/io/files/#tecs.io.files.TemporaryOptions) | The caller may select the parent, prefix, and suffix. |

#### Returns

| Type | Description |
| --- | --- |
| [`TemporaryPath`](/modules/io/files/#tecs.io.files.TemporaryPath) | Returns the caller-owned temporary directory. |
| `string` | Returns the platform or unsupported reason when creation fails. |

<a id="tecs.io.files.createTemporaryFile"></a>
### tecs.io.files.createTemporaryFile <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Creates an empty temporary file owned by the returned resource.

Creation is exclusive. Closing removes the file; garbage collection is a
leak safety net, while [`tecs.scoped`](/modules/#tecs.scoped) provides
deterministic cleanup. Call `persist` to keep the file under an absent
permanent destination.



```teal
function tecs.io.files.createTemporaryFile(
    options: TemporaryOptions
): TemporaryPath, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `options` | [`TemporaryOptions`](/modules/io/files/#tecs.io.files.TemporaryOptions) | The caller may select the parent, prefix, and suffix. |

#### Returns

| Type | Description |
| --- | --- |
| [`TemporaryPath`](/modules/io/files/#tecs.io.files.TemporaryPath) | Returns the caller-owned temporary file. |
| `string` | Returns the platform or unsupported reason when creation fails. |

<a id="tecs.io.files.currentDirectory"></a>
### tecs.io.files.currentDirectory <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the process working directory.

Command-line tools receive relative paths and resolve them against this
directory. It is not where a
game reads content from or writes state to: `assetRoot`, `preferencePath`
and `cachePath` answer that, and say why. A platform with no working
directory answers nil, which is most of the ones that are not a desktop.


```teal
function tecs.io.files.currentDirectory(): string, string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the directory with a trailing separator, or nil when the platform has no working directory. |
| `string` | Returns the platform reason when the first return is nil. |

<a id="tecs.io.files.exists"></a>
### tecs.io.files.exists <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns whether anything occupies `path`.



```teal
function tecs.io.files.exists(path: PathInput): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies an absolute or platform-resolvable path. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true when the platform finds any object. |

<a id="tecs.io.files.glob"></a>
### tecs.io.files.glob <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Opens a pull stream over entries under `path` that match `pattern`.

**Recursive unless the pattern stops it**: with no pattern this walks the
whole tree below `path`. `*` and `?` never match a separator, and `?`
consumes one UTF-8 codepoint, so `"*"` is one level, `"*/*"` is exactly
two, and `"*.png"` matches only immediate children. Results include
directories alongside files. Symbolic links are yielded but never followed.
The order is the platform's own and is not sorted.

The stream retains only the unvisited names in each directory on its
current descent. Close it when leaving early. After receiving a directory,
call `skipDirectory` before `next` to prune that complete subtree. Call
`toArray` to consume and close the stream when every remaining entry should
be retained.

A fixed bound belongs in `maxDepth`; `skipDirectory` remains useful when
descent depends on the entry itself. The checked example combines both.



```teal
function tecs.io.files.glob(
    path: PathInput, pattern: string, options: GlobOptions
): DirectoryStream, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the directory to enumerate. |
| `pattern` | `string` | The caller supplies `/`-separated `*` and `?` wildcards, or nil to include every descendant recursively. |
| `options` | [`GlobOptions`](/modules/io/files/#tecs.io.files.GlobOptions) | The caller supplies matching and depth options, or omits them for case-sensitive, unlimited traversal. |

#### Returns

| Type | Description |
| --- | --- |
| [`DirectoryStream`](/modules/io/files/#tecs.io.files.DirectoryStream) | Returns a caller-owned stream, or nil when enumeration cannot start. |
| `string` | Returns the platform reason when the first return is nil. |

#### Examples

```teal
local files <const> = tecs.io.files
tecs.scoped(function(scope: tecs.Scope)
    local stream, reason = files.glob(
        files.assetPath("levels"), nil, {maxDepth = 2}
    )
    if stream == nil then
        error(reason)
    end
    scope:own(stream)
    while true do
        local entry, nextReason = stream:next()
        if entry == nil then
            if nextReason ~= nil then
                error(nextReason)
            end
            break
        end
        print(entry.depth, entry.path)
        if entry.kind == "directory" and entry.name == "generated" then
            stream:skipDirectory()
        end
    end
end)
```

<a id="tecs.io.files.info"></a>
### tecs.io.files.info <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns information about `path`, or nil when nothing occupies it.

Nil is the answer for a path that does not exist, for a broken symbolic
link, and when the platform cannot search a directory; the second return
separates them.


```teal
function tecs.io.files.info(path: PathInput): Info, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies an absolute or platform-resolvable path. |

#### Returns

| Type | Description |
| --- | --- |
| [`Info`](/modules/io/files/#tecs.io.files.Info) | Returns the path information, or nil when unavailable. |
| `string` | Returns the platform reason when the first return is nil. |

<a id="tecs.io.files.isDirectory"></a>
### tecs.io.files.isDirectory <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns whether `path` resolves to a directory.



```teal
function tecs.io.files.isDirectory(path: PathInput): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies an absolute or platform-resolvable path. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true when the resolved object is a directory. |

<a id="tecs.io.files.isFile"></a>
### tecs.io.files.isFile <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns whether `path` resolves to a regular file.



```teal
function tecs.io.files.isFile(path: PathInput): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies an absolute or platform-resolvable path. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true when the resolved object is a regular file. |

<a id="tecs.io.files.isSymlink"></a>
### tecs.io.files.isSymlink <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns whether the final object named by `path` is a symbolic link.

Unlike `info`, this function does not follow the final link. It does follow
links in parent directories, so the result is path introspection and not a
security boundary. A path that is absent or cannot be inspected returns
false.



```teal
function tecs.io.files.isSymlink(path: PathInput): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies an absolute or platform-resolvable path. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true when the final path itself is a symbolic link. |

<a id="tecs.io.files.lines"></a>
### tecs.io.files.lines <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Iterates over the lines in a whole file.

The function reads and closes the file before it returns the iterator, so
breaking the loop retains only the immutable bytes and no file descriptor.
It strips LF and CRLF terminators, returns empty lines between adjacent
terminators, and does not invent another empty line after a final
terminator. Use `open` when the input is too large to hold whole.



```teal
function tecs.io.files.lines(path: PathInput): LineIterator, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the source file. |

#### Returns

| Type | Description |
| --- | --- |
| [`LineIterator`](/modules/io/files/#tecs.io.files.LineIterator) | Returns a [`LineIterator`](/modules/io/files/#tecs.io.files.LineIterator) that yields each line in order, or nil when the platform cannot read the file. |
| `string` | Returns the failure reason when the first return is nil. |

#### Examples

```teal
local files <const> = require("tecs.io.files")
local nextLine <const> = assert(files.lines("save.txt"))

for line in nextLine do
    print(line)
end
```

<a id="tecs.io.files.load"></a>
### tecs.io.files.load <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Compiles a Lua file without running it.

The function reads through the installed storage backend rather than
`loadfile`, records the source for file watching, and uses `@path` as the
compiler's chunk name. The default environment is the caller's global
environment. A supplied environment is installed exactly as given and is
not a security boundary unless the caller makes it one.



```teal
function tecs.io.files.load(
    path: PathInput, environment: {string: any}
): function(...: any): any..., string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the Lua source file. |
| `environment` | `{string : any}` | The caller supplies the globals visible when the chunk runs, or omits them to use the process globals. |

#### Returns

| Type | Description |
| --- | --- |
| `function(...: any): any...` | Returns the compiled chunk, or nil when reading or compilation fails. Calling the returned function executes the file. |
| `string` | Returns the read or compiler reason when the first return is nil. |

<a id="tecs.io.files.open"></a>
### tecs.io.files.open <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Opens a file through one seekable cursor.

The mode follows Lua's standard file modes. `"r"` opens an existing file
for reading and is the default. `"w"` creates or truncates for writing.
`"a"` creates or preserves for writes that always land at the end. Adding
`+` permits both reading and writing. The cursor starts at byte zero except
for `"a"`; `"a+"` starts at byte zero for reading while every write still
lands at the current end. Seeks may not move past the end.

Close the file even after its last write succeeds. `close` flushes buffered
bytes and can report a delayed storage failure. A backend without direct
random access retains the complete file until close.



```teal
function tecs.io.files.open(
    path: PathInput, mode: FileMode
): File, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the file as a string or immutable [`Path`](/modules/io/Path/). |
| `mode` | [`FileMode`](/modules/io/files/#tecs.io.files.FileMode) | The caller selects `"r"`, `"w"`, `"a"`, `"r+"`, `"w+"`, or `"a+"`, or omits it for `"r"`. |

#### Returns

| Type | Description |
| --- | --- |
| [`File`](/modules/io/files/#tecs.io.files.File) | Returns a caller-owned [`File`](/modules/io/files/#tecs.io.files.File), or nil when the platform cannot open it. |
| `string` | Returns the platform reason when the first return is nil. |

#### Examples

```teal
local files <const> = require("tecs.io.files")
local file, reason = files.open("world.pack", "w+")
if file == nil then
    error(reason)
end

local wrote, writeReason = file:write("HEADpayload bytes")
if not wrote then
    file:close()
    error(writeReason)
end
local position, seekReason = file:seek("start")
if position == nil then
    file:close()
    error(seekReason)
end
local header <const> = assert(file:read(4))
assert(header == "HEAD")

local closed, closeReason = file:close()
assert(closed, closeReason)
```

<a id="tecs.io.files.preferenceIdentity"></a>
### tecs.io.files.preferenceIdentity <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the publisher and game names used for writable user data.

The pair starts as `"tecs", "tecs"` and changes when
`setPreferenceIdentity` succeeds.



```teal
function tecs.io.files.preferenceIdentity(): string, string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the current publisher, studio, or organization name. |
| `string` | Returns the current game or application name. |

<a id="tecs.io.files.preferencePath"></a>
### tecs.io.files.preferencePath <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the writable directory for this application and creates it if
absent.



```teal
function tecs.io.files.preferencePath(): string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the directory with a trailing separator, named from the preference identity. This is where a build writes durable state; `cachePath` is the other writable root and holds only reconstructable data. |

<a id="tecs.io.files.read"></a>
### tecs.io.files.read <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Reads a whole file and returns its bytes, or nil when there is none there.

The binary string retains embedded NUL bytes. The synchronous call suits
small documents; use `tecs.assets.loadString` when a whole-file read should
run off the main thread, or its image and sound loaders for decoding.



```teal
function tecs.io.files.read(path: PathInput, kind: string): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies an absolute path or one from `assetPath`. |
| `kind` | `string` | The caller supplies the content kind for watching, or omits it to record a document. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the file bytes, or nil when the platform cannot read them. |

<a id="tecs.io.files.readLink"></a>
### tecs.io.files.readLink <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the target spelling stored in a symbolic link.

A relative target remains relative. The function does not resolve it
against the link's parent and fails when `path` is not a symbolic link.



```teal
function tecs.io.files.readLink(path: PathInput): Path, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the symbolic link to inspect. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns the stored target as an immutable [`Path`](/modules/io/Path/). |
| `string` | Returns the platform or unsupported reason when the first return is nil. |

<a id="tecs.io.files.remove"></a>
### tecs.io.files.remove <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Removes a file, or an empty directory.

It does not recurse and fails on a directory with anything in it. It
**succeeds when there is nothing at `path`**: the result says the path is
gone, not that this call is what removed it.

Emptying a tree requires a glob and a deepest-first loop:

local stream = assert(files.glob(root))
local paths = {}
while true do
local entry, reason = stream:next()
if entry == nil then
if reason ~= nil then error(reason) end
break
end
paths[#paths + 1] = entry.path
end
stream:close()
table.sort(paths, function(a, b)
return #(a:toString()) > #(b:toString())
end)
for _, path in ipairs(paths) do
files.remove(path)
end
files.remove(root)



```teal
function tecs.io.files.remove(path: PathInput): boolean, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the file or empty directory to remove. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true when nothing remains at `path`. |
| `string` | Returns the platform reason when the first return is false. |

<a id="tecs.io.files.rename"></a>
### tecs.io.files.rename <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Moves `from` to `to`, replacing whatever was at `to`.

Directories move as well as files, and an existing destination is
overwritten with no warning and nothing to undo it. Whether this works
across filesystems is the platform's business.



```teal
function tecs.io.files.rename(
    from: PathInput, to: PathInput
): boolean, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `from` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the existing source path. |
| `to` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the destination path to replace. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether the platform moved the path. |
| `string` | Returns the platform reason when the first return is false. |

<a id="tecs.io.files.setAssetRoot"></a>
### tecs.io.files.setAssetRoot <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Overrides the asset root, for a test or a tool.



```teal
function tecs.io.files.setAssetRoot(root: PathInput)
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `root` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the new content directory. The function adds a trailing separator only when needed. |

#### Returns

None.

<a id="tecs.io.files.setPreferenceIdentity"></a>
### tecs.io.files.setPreferenceIdentity <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Sets the publisher and game names used for writable user data.

SDL combines these values with the current user and platform conventions
to choose the directory returned by `preferencePath`. The defaults are
`"tecs"` and `"tecs"`. Later calls change which directory the next
`preferencePath`, `writablePath`, or `cachePath` resolves.



```teal
function tecs.io.files.setPreferenceIdentity(
    organization: string, application: string
)
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `organization` | `string` | The caller supplies a non-empty publisher, studio, or organization name. |
| `application` | `string` | The caller supplies a non-empty game or application name. |

#### Returns

None.

<a id="tecs.io.files.setReadOnly"></a>
### tecs.io.files.setReadOnly <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Changes the portable read-only state of a path.

On Unix, making a path read-only clears every write bit and making it
writable restores only the owner's write bit. The API deliberately does
not model platform ACLs, ownership, or executable bits.



```teal
function tecs.io.files.setReadOnly(
    path: PathInput, readOnly: boolean
): boolean, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the existing path to change. |
| `readOnly` | `boolean` | The caller supplies true to prevent ordinary writes. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether the platform changed the state. |
| `string` | Returns the platform or unsupported reason when the first return is false. |

<a id="tecs.io.files.userFolder"></a>
### tecs.io.files.userFolder <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns one of the platform's well-known folders.

**Often nil, and legitimately so.** A platform that has no such concept says
so rather than inventing a path: macOS has no saved-games, screenshots or
templates folder and answers nil for all three, and a platform that is not a
desktop may have none of them. Treat every one of these as absent until it
is not, and never as a place a build may write; `preferencePath` and
`cachePath` are the two writable roots, with different durability.


```teal
function tecs.io.files.userFolder(which: UserFolder): string, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `which` | [`UserFolder`](/modules/io/files/#tecs.io.files.UserFolder) | The caller supplies the well-known folder to resolve. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the folder with a trailing separator, or nil when the platform has no matching folder. |
| `string` | Returns the platform reason when the first return is nil. |

<a id="tecs.io.files.writablePath"></a>
### tecs.io.files.writablePath <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Resolves `relative` against the writable root.



```teal
function tecs.io.files.writablePath(relative: PathInput): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `relative` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies a path under the preference directory. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the absolute path. |

<a id="tecs.io.files.write"></a>
### tecs.io.files.write <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Writes `bytes` to `path`, replacing whatever was there.

The string's length controls the binary write, so embedded NUL bytes remain
data and an empty string creates an empty file. The parent directory must
already exist.



```teal
function tecs.io.files.write(
    path: PathInput, bytes: string
): boolean, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the destination file. |
| `bytes` | `string` | The caller supplies the complete binary contents. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether the platform wrote the complete file. |
| `string` | Returns the platform reason when the first return is false. |

<a id="tecs.io.files.writeAtomic"></a>
### tecs.io.files.writeAtomic <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Durably writes `bytes` and atomically replaces `path`.

The platform writes a uniquely named temporary file beside `path`, commits
its complete contents to durable storage, atomically replaces the
destination, and commits the containing directory where the operating
system provides that operation. An existing destination remains untouched
when anything fails before replacement, and no temporary file is retained.

A failure after replacement means the complete new file is visible but the
platform could not confirm that the directory update will survive a power
loss. The function returns false in that case rather than overstating
durability. Hardware can still violate an operating system's completed
flush request.

A storage backend without an atomic durable commit returns false and an
unsupported reason. The function never falls back to `write` plus `rename`.
The parent directory must already exist.



```teal
function tecs.io.files.writeAtomic(
    path: PathInput, bytes: ByteInput
): boolean, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the destination file. |
| `bytes` | `ByteInput` | The caller supplies a string or open [`Buffer`](/modules/io/#tecs.io.Buffer) or [`ByteView`](/modules/io/#tecs.io.ByteView) containing the complete binary contents. Buffers and views are borrowed only for this synchronous call and are not copied or closed. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns whether the platform confirmed the atomic durable write. |
| `string` | Returns the platform or unsupported reason when the first return is false. |

#### Examples

```teal
local save <const> = tecs.io.Path.new(
    tecs.io.files.writablePath("saves"), "slot1.bin"
)
local ok, reason = tecs.io.files.writeAtomic(save, "snapshot bytes")
if not ok then
    error(reason)
end
```

<a id="tecs.io.files.writeAtomicAsync"></a>
### tecs.io.files.writeAtomicAsync <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Durably and atomically writes a complete file on a worker.

The returned [`Future`](/modules/Future/) becomes ready with true after
the same commit performed by `writeAtomic`, or fails with its platform
reason. The call copies a buffer or byte view before returning, so the
caller may mutate or close it immediately. String bytes cross the worker
channel by value as well. `tecs.runtime.poll` advances the future each
frame; `Future:wait` advances it in headless code.

Canceling the future stops interest in its result but cannot safely
interrupt a filesystem commit already in progress. The shared worker exits
automatically after its last queued result is drained. A non-SDL storage
backend receives a failed future because a separate worker state cannot
inherit the main state's installed Lua backend.



```teal
function tecs.io.files.writeAtomicAsync(
    path: PathInput, bytes: ByteInput
): Future<boolean>
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `path` | [`PathInput`](/modules/io/files/#tecs.io.files.PathInput) | The caller supplies the destination file. |
| `bytes` | `ByteInput` | The caller supplies the complete binary contents. |

#### Returns

| Type | Description |
| --- | --- |
| [`Future`](/modules/Future/)`<boolean>` | Returns a future which becomes ready with true or fails. |