
# tecs.io.Path


Immutable, platform-native filesystem paths.

`Path` is an immutable platform-native UTF-8
filesystem path. Methods interpret roots, separators, drive prefixes, and UNC
paths according to the operating system running Tecs. They return new paths
rather than mutating their receiver:

```teal
local root <const> = tecs.io.Path.new("assets")
local shader <const> = root:join("shaders", "sprite.glsl")

print(shader:fileName())
print(shader:withExtension("spv"))
```

`normalize` is lexical. It removes redundant separators and `.` components
and resolves `..` without reading the filesystem. That is useful for output
paths which do not exist yet, but it can change which object a path denotes
when a traversed component is a symbolic link. `canonicalize` reads the
filesystem, requires the path to exist, and follows symbolic links:
use `resolve` for a normalized absolute spelling of a configured output and
`canonicalize` only when an existing object's filesystem identity matters.

Paths contain valid UTF-8 and no NUL byte, matching the string contract used
by Tecs file and process APIs. Unix filenames containing invalid UTF-8 cannot
be represented. A path object owns only its immutable Lua string and needs no
`close`.

## Module contents

### Constructors

| Constructor | Description |
| --- | --- |
| [`new`](/modules/io/Path/#tecs.io.Path.new) | Creates an immutable path from platform-native components. |

### Functions

| Function | Kind | Description |
| --- | --- | --- |
| [`currentDirectory`](/modules/io/Path/#tecs.io.Path.currentDirectory) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the process's current working directory. |
| [`separator`](/modules/io/Path/#tecs.io.Path.separator) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the platform's preferred path separator. |
| [`absolute`](/modules/io/Path/#tecs.io.Path.absolute) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns an absolute spelling without requiring the path to exist. |
| [`canonicalize`](/modules/io/Path/#tecs.io.Path.canonicalize) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Resolves filesystem identity and follows symbolic links. |
| [`extension`](/modules/io/Path/#tecs.io.Path.extension) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns the final component's last extension without its dot. |
| [`fileName`](/modules/io/Path/#tecs.io.Path.fileName) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns the final path component. |
| [`isAbsolute`](/modules/io/Path/#tecs.io.Path.isAbsolute) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns whether this path carries a platform root. |
| [`isRelative`](/modules/io/Path/#tecs.io.Path.isRelative) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns whether this path needs a base directory. |
| [`join`](/modules/io/Path/#tecs.io.Path.join) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Appends path components and returns the result. |
| [`normalize`](/modules/io/Path/#tecs.io.Path.normalize) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns a lexically normalized path without filesystem access. |
| [`parent`](/modules/io/Path/#tecs.io.Path.parent) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns this path without its final component. |
| [`relativeTo`](/modules/io/Path/#tecs.io.Path.relativeTo) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Expresses this path relative to a base path. |
| [`resolve`](/modules/io/Path/#tecs.io.Path.resolve) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns a normalized absolute spelling without filesystem access. |
| [`stem`](/modules/io/Path/#tecs.io.Path.stem) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns the final component without its last extension. |
| [`toString`](/modules/io/Path/#tecs.io.Path.toString) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Returns this path's platform-native UTF-8 spelling. |
| [`withExtension`](/modules/io/Path/#tecs.io.Path.withExtension) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Replaces or removes the final component's extension. |
| [`withFileName`](/modules/io/Path/#tecs.io.Path.withFileName) | <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span> | Replaces the final component. |

## Constructors

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

Creates an immutable path from platform-native components.

Construction joins the components lexically and does not require the
path to exist. A later absolute component replaces the accumulated
prefix according to the host platform's rules.


```teal
function tecs.io.Path.new(first: string, ...: string): Path
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `first` | `string` | The caller supplies the first valid UTF-8 path component or complete path, without a NUL byte. |
| `...` | `string` | The caller supplies additional path components in order. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable path. |

#### Examples

```teal
local shader <const> = tecs.io.Path.new(
    "assets", "shaders", "sprite.glsl"
)

assert(shader:fileName() == "sprite.glsl")
```

## Functions

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

Returns the process's current working directory.



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

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable normalized absolute path. |
| `string` | Returns the platform reason when the first return is nil. |

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

Returns the platform's preferred path separator.

Paths accept the forms supported by the host platform; this value is
only for text that must display or emit a separator itself.


```teal
function tecs.io.Path.separator(): string
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns `"\\"` on Windows and `"/"` elsewhere. |

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

Returns an absolute spelling without requiring the path to exist.

A relative receiver is based on the process's current directory.
Unlike `resolve`, this preserves platform-significant parent
components where the native absolute operation preserves them.


```teal
function tecs.io.Path.absolute(self): Path, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path to make absolute. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable absolute path. |
| `string` | Returns the platform reason when the current directory cannot be determined. |

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

Resolves filesystem identity and follows symbolic links.

The receiver must exist. The result is absolute and normalized by the
operating system.


```teal
function tecs.io.Path.canonicalize(self): Path, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The existing path to canonicalize. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable canonical path. |
| `string` | Returns the filesystem reason when the first return is nil. |

#### Examples

```teal
local executable, reason = tecs.io.Path.new("."):canonicalize()
if executable == nil then
    error(reason)
end

print(executable)
```

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

Returns the final component's last extension without its dot.



```teal
function tecs.io.Path.extension(self): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path to inspect without filesystem access. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the extension, or nil when none exists. |

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

Returns the final path component.



```teal
function tecs.io.Path.fileName(self): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path to inspect without filesystem access. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the final component, or nil for a root or terminal parent component. |

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

Returns whether this path carries a platform root.



```teal
function tecs.io.Path.isAbsolute(self): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path to inspect without filesystem access. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true when the path is absolute on this platform. |

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

Returns whether this path needs a base directory.



```teal
function tecs.io.Path.isRelative(self): boolean
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path to inspect without filesystem access. |

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true when the path is relative on this platform. |

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

Appends path components and returns the result.

A later absolute argument replaces everything accumulated before it,
following the host platform's path rules. The result is not normalized
and no component needs to exist.


```teal
function tecs.io.Path.join(self, ...: string | Path): Path
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The base path left unchanged. |
| `...` | <code>string &#124; </code>[`Path`](/modules/io/Path/) | The caller supplies strings or paths to append in order. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable path. |

#### Examples

```teal
local assets <const> = tecs.io.Path.new("assets")
local shader <const> = assets:join("shaders", "sprite.glsl")

assert(tostring(assets) == "assets")
assert(
    shader:toString() == tecs.io.Path.new(
        "assets", "shaders", "sprite.glsl"
    ):toString()
)
```

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

Returns a lexically normalized path without filesystem access.

Redundant separators and `.` are removed and `..` is resolved. A
symbolic link can make that lexical result denote a different object.


```teal
function tecs.io.Path.normalize(self): Path
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path to normalize. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable path. |

#### Examples

```teal
local configured <const> = tecs.io.Path.new(
    "assets", ".", "shaders", "..", "sprite.glsl"
)
local normalized <const> = configured:normalize()

assert(normalized == tecs.io.Path.new("assets", "sprite.glsl"))
```

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

Returns this path without its final component.



```teal
function tecs.io.Path.parent(self): Path
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path to inspect without filesystem access. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable parent, or nil when the path has none. |

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

Expresses this path relative to a base path.

Neither path is accessed. On Windows, paths on different drives or UNC
roots have no relative spelling.


```teal
function tecs.io.Path.relativeTo(
    self, base: string | Path
): Path, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The destination path. |
| `base` | <code>string &#124; </code>[`Path`](/modules/io/Path/) | The caller supplies the directory from which to describe the destination. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable relative path. |
| `string` | Returns a reason when the two paths cannot share a coordinate system. |

#### Examples

```teal
local root <const> = tecs.io.Path.new("assets")
local sprite <const> = root:join("sprites", "hero.png")
local relative, reason = sprite:relativeTo(root)
if relative == nil then
    error(reason)
end

assert(relative == tecs.io.Path.new("sprites", "hero.png"))
```

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

Returns a normalized absolute spelling without filesystem access.

Arguments are joined first. The process's current directory supplies
the base when the joined path remains relative.


```teal
function tecs.io.Path.resolve(
    self, ...: string | Path
): Path, string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The first path in the resolution. |
| `...` | <code>string &#124; </code>[`Path`](/modules/io/Path/) | The caller supplies strings or paths to append in order. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable normalized absolute path. |
| `string` | Returns the platform reason when the current directory cannot be determined. |

#### Examples

```teal
local output, reason = tecs.io.Path.new("build", "game.pack"):resolve()
if output == nil then
    error(reason)
end

assert(output:isAbsolute())
```

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

Returns the final component without its last extension.



```teal
function tecs.io.Path.stem(self): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path to inspect without filesystem access. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the stem, or nil when the path has no file name. |

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

Returns this path's platform-native UTF-8 spelling.

`tostring(path)` returns the same string.


```teal
function tecs.io.Path.toString(self): string
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The immutable path to render. |

#### Returns

| Type | Description |
| --- | --- |
| `string` | Returns the complete path without accessing the filesystem. |

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

Replaces or removes the final component's extension.



```teal
function tecs.io.Path.withExtension(self, extension: string): Path
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path whose final component changes. |
| `extension` | `string` | The caller supplies one component without a leading dot, or an empty string to remove the extension. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable path. |

#### Examples

```teal
local source <const> = tecs.io.Path.new("shaders", "sprite.glsl")
local compiled <const> = source:withExtension("spv")

assert(source:extension() == "glsl")
assert(compiled:fileName() == "sprite.spv")
```

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

Replaces the final component.



```teal
function tecs.io.Path.withFileName(self, name: string): Path
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Path` | The path whose parent is retained. |
| `name` | `string` | The caller supplies one non-empty platform path component. |

#### Returns

| Type | Description |
| --- | --- |
| [`Path`](/modules/io/Path/) | Returns a new immutable path. |