# `nupp.io.path`
`nupp.io.path` answers an immutable path value whose joining, normalizing and
splitting follow the platform's own rules. Reach for it when a program builds a
filesystem name or takes one apart, rather than when it reads the bytes behind
one.
```nupp:playground
const path = nupp.io.path
local source = path.newPath("src", "app", "..", "main.nupp"):normalize()
assert(source:toString() == "src" .. path.separator() .. "main.nupp")
assert(source:fileName() == "main.nupp")
```
Every operation answers a new path, and two paths are equal when their native
text is. `tostring(path)` and `path:toString()` return that same text.
## Building a path
`path.newPath(first, parts...)` joins its components using the current platform's
rules. `join` appends more to a path already built, and both accept a string or
another path. `Path` has no public constructor; creation goes through `newPath`,
which interns recent paths in a bounded LRU cache.
```nupp
const path = nupp.io.path
local native = path.newPath("out"):join("lib", "native")
assert(native:fileName() == "native")
```
`currentDirectory` and `separator` are functions on the module beside the
record, because neither builds a path out of components:
```nupp
const path = nupp.io.path
local current, reason = path.currentDirectory()
assert(current, reason)
local log = current:join("var", "app.log"):withExtension("jsonl")
assert(log:extension() == "jsonl")
```
## Normalizing and resolving
`normalize` removes lexical `.` and `..` components without touching the
filesystem, so it answers even for a path that does not exist:
```nupp
const path = nupp.io.path
assert(path.newPath("src", "app", "..", "main.nupp"):normalize():stem() == "main")
```
The operations that consult the process or filesystem can fail, as can a
relative path whose base uses a different coordinate system. Each answers nil
with a reason when it does.
- `absolute()` resolves the path against the process's working directory.
- `resolve(parts...)` makes it absolute, appends the parts, and normalizes.
- `canonicalize()` asks the filesystem for the real path, following every
symbolic link, which requires the path to exist.
- `relativeTo(base)` expresses the path against another one, when both share a
coordinate system. A path expressed against itself answers `.`, the path
that stays where the base already is.
```nupp
const path = nupp.io.path
local real, reason = path.newPath("src"):canonicalize()
assert(real, reason)
assert(real:isAbsolute())
```
## Path components
`parent` answers another path or nil. `fileName`, `stem` and `extension` answer
the final component, that component without its extension, and the extension
without its dot:
```nupp
const path = nupp.io.path
local source = path.newPath("src", "main.nupp")
assert(source:fileName() == "main.nupp")
assert(source:stem() == "main")
assert(source:extension() == "nupp")
assert(assert(source:parent()):toString() == "src")
```
`withFileName` and `withExtension` replace one component. Each takes one path
component and raises when handed text that is not one, so a separator smuggled
into a file name is reported where it was written rather than carried into the
path it would have produced:
```nupp
const path = nupp.io.path
local report = path.newPath("out", "report.tmp"):withExtension("json")
assert(report:fileName() == "report.json")
```
`isAbsolute` and `isRelative` classify a path without accessing the filesystem.
::: seealso
- `nupp.io.files` for reading, writing and listing what a path names
- `nupp.io.uri` for network and resource identity, which is URI text rather
than a filesystem name
- `nupp.io` for the byte buffers, readers and writers a file's contents move
through
:::
## Submodules
| Module | Description |
| --- | --- |
| `nupp.io.path.provider` | |
## Constructors
### `newPath` _constructor_
```nupp
function newPath(first: string | Path, ...: string | Path): Path
```
Builds a path by joining one or more components using the platform's rules.
Repeatedly building the same path reuses its immutable value from a bounded LRU.
#### Examples
```nupp
const path = nupp.io.path
local source = path.newPath("src", "main.nupp")
```
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `first` | `string | Path` | the first path component |
| `...` | `string | Path` | the remaining path components |
#### Returns
| Type | Description |
| --- | --- |
| `Path` | the joined path |
## Types
### `Path` _record_
```nupp
record Path
function toString(self): string end
function join(self, ...: string | Path): Path end
function normalize(self): Path end
function absolute(self): (Path?, string?) end
function resolve(self, ...: string | Path): (Path?, string?) end
function canonicalize(self): (Path?, string?) end
function relativeTo(self, base: string | Path): (Path?, string?) end
function parent(self): Path? end
function fileName(self): string? end
function stem(self): string? end
function extension(self): string? end
function withFileName(self, name: string): Path end
function withExtension(self, extension: string): Path end
function isAbsolute(self): boolean end
function isRelative(self): boolean end
metamethod __tostring: function(self): string
metamethod __eq: function(self, any): boolean
end
```
A filesystem path.
#### Methods
##### `toString`
```nupp
toString: function toString(self): string
```
Returns the native UTF-8 path text.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `string` | the path text |
##### `join`
```nupp
join: function join(self, ...: string | Path): Path
```
Appends path components using the current platform's rules.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
| `...` | `string | Path` | the components to append |
###### Returns
| Type | Description |
| --- | --- |
| `Path` | the joined path |
##### `normalize`
```nupp
normalize: function normalize(self): Path
```
Removes lexical `.` and `..` components without accessing the filesystem.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `Path` | the normalized path |
##### `absolute`
```nupp
absolute: function absolute(self): Path?, string?
```
Resolves this path against the process's current working directory.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `Path?` | the absolute path, or nil on failure |
| `string?` | a failure reason, when unsuccessful |
##### `resolve`
```nupp
resolve: function resolve(self, ...: string | Path): Path?, string?
```
Makes this path absolute, appends components, and normalizes the result.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
| `...` | `string | Path` | the components to append |
###### Returns
| Type | Description |
| --- | --- |
| `Path?` | the resolved path, or nil on failure |
| `string?` | a failure reason, when unsuccessful |
##### `canonicalize`
```nupp
canonicalize: function canonicalize(self): Path?, string?
```
Resolves every symbolic link and `..`, which requires the path to exist.
This reads the filesystem, where `normalize` works on the text alone, so
it fails on a path that is not there.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `Path?` | the canonical path, or nil on failure |
| `string?` | a failure reason, when unsuccessful |
##### `relativeTo`
```nupp
relativeTo: function relativeTo(self, base: string | Path): Path?, string?
```
Expresses this path relative to another. A path expressed against
itself answers `.`, the path that stays where the base already is.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
| `base` | `string | Path` | the path to express this one against |
###### Returns
| Type | Description |
| --- | --- |
| `Path?` | the relative path, or nil when there is no relative form |
| `string?` | a failure reason, when unsuccessful |
##### `parent`
```nupp
parent: function parent(self): Path?
```
The path without its final component.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `Path?` | the parent, or nil when there is none |
##### `fileName`
```nupp
fileName: function fileName(self): string?
```
The final component, extension included.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `string?` | the file name, or nil when the path ends in a directory component |
##### `stem`
```nupp
stem: function stem(self): string?
```
The final component without its extension.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `string?` | the stem, or nil when there is no file name |
##### `extension`
```nupp
extension: function extension(self): string?
```
The final component's extension, without the dot.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `string?` | the extension, or nil when there is none |
##### `withFileName`
```nupp
withFileName: function withFileName(self, name: string): Path
```
The path with its final component replaced.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
| `name` | `string` | the file name to use |
###### Returns
| Type | Description |
| --- | --- |
| `Path` | the new path |
##### `withExtension`
```nupp
withExtension: function withExtension(self, extension: string): Path
```
The path with its extension replaced.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
| `extension` | `string` | the extension to use, without the dot |
###### Returns
| Type | Description |
| --- | --- |
| `Path` | the new path |
##### `isAbsolute`
```nupp
isAbsolute: function isAbsolute(self): boolean
```
Whether the path is anchored at a root.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `boolean` | whether the path is absolute |
##### `isRelative`
```nupp
isRelative: function isRelative(self): boolean
```
Whether the path is interpreted against a working directory.
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | this path |
###### Returns
| Type | Description |
| --- | --- |
| `boolean` | whether the path is relative |
##### `__tostring`
```nupp
__tostring: function(self): string
```
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
###### Returns
| Type | Description |
| --- | --- |
| `string` | |
##### `__eq`
```nupp
__eq: function(self, any): boolean
```
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `?` | `any` | |
###### Returns
| Type | Description |
| --- | --- |
| `boolean` | |
## Functions
### `currentDirectory` _function_
```nupp
function currentDirectory(): Path?, string?
```
Reads the process's current working directory.
#### Returns
| Type | Description |
| --- | --- |
| `Path?` | the current directory, or nil on failure |
| `string?` | a failure reason, when unsuccessful |
### `separator` _function_
```nupp
function separator(): string
```
Returns the current platform's primary path separator.
#### Returns
| Type | Description |
| --- | --- |
| `string` | the path separator |