path
import path from "path";
let p: string = path.join("assets", "logo.png"); // "assets/logo.png" ("assets\logo.png" on Windows)
let name: []const u8 = path.basename(p); // "logo.png"
let kind: []const u8 = path.ext(p); // ".png"
let thumb: string = path.with_ext(p, ".thumb.png");path works on paths as text. It never looks at the file system, so it can
tell you that a/b/../c is a/c but not whether a/c exists (that’s
fs). It is a pure module.
Separators
Paths follow the rules of the platform you compile for:
- Linux, macOS, Uusi:
/separates; a path starting with/is absolute. - Windows:
\and/both separate, andpathwrites\. A path can start with a drive (C:\) or a network share (\\server\share\).
| Item | Is |
|---|---|
path.SEP: u8 | The separator path writes: \ (92) on Windows, / (47) elsewhere |
path.is_separator(c: u8) -> bool | true for /, and on Windows also for \ |
path.is_absolute(p) -> bool | true when p doesn’t depend on the current directory |
On Windows, C:\x and \\server\share\x are absolute, but \x (the
current drive’s root) and C:x (relative to drive C’s current directory)
are not.
let abs: bool = path.is_absolute("/usr/lib"); // true on Linux and macOS
let rel: bool = path.is_absolute("src/main.tape"); // false everywhereTaking paths apart
| Function | Returns |
|---|---|
basename(p) -> []const u8 | The last component: "b.txt" for "a/b.txt" |
dirname(p) -> []const u8 | Everything before it: "a" for "a/b.txt" |
ext(p) -> []const u8 | The extension with its dot: ".txt" |
stem(p) -> []const u8 | The last component without its extension: "b" |
components(p) -> ComponentIter | The root, then each component, for for |
The results are views into p. A trailing separator doesn’t matter:
"a/b/" has the basename "b". When there’s no component left the
result is empty, and dirname never goes above the root:
path.dirname("a/b/c"); // "a/b"
path.dirname("a"); // ""
path.dirname("/a"); // "/"
path.basename("/"); // ""
path.ext("archive.tar.gz"); // ".gz"
path.stem("archive.tar.gz");// "archive.tar"
path.ext(".bashrc"); // "" (a leading dot isn't an extension)components skips empty parts (from // or a trailing /) but keeps .
and ..:
for (part in path.components("/usr//local/./bin/")) {
// "/", "usr", "local", ".", "bin"
}Building paths
| Function | Returns |
|---|---|
join(a, b) -> string | b relative to a, with one separator between them |
with_ext(p, e) -> string | p with its extension replaced by e |
normalize(p) -> string | p with ., .. and repeated separators resolved |
join adds a separator only when a doesn’t already end with one. When
b is absolute (or has any root), the result is just b; when either side
is empty, it’s the other one.
path.join("src", "main.tape"); // "src/main.tape"
path.join("src/", "main.tape"); // "src/main.tape"
path.join("src", "/etc/hosts"); // "/etc/hosts"with_ext takes the extension with its dot (as ext returns it), and ""
removes it. It stops the program with a
trap if e doesn’t start with .,
contains a separator, or if p has no file name ("", "/", "..").
path.with_ext("notes/todo.txt", ".md"); // "notes/todo.md"
path.with_ext("build/app", ".exe"); // "build/app.exe"
path.with_ext("photo.jpeg", ""); // "photo"normalize cleans a path up without the file system: it removes ., lets
.. cancel the component before it, collapses repeated separators, drops a
trailing separator, and on Windows writes every separator as \. A ..
that would go above the root is dropped; at the start of a relative path it
stays. An empty result is ".".
path.normalize("a/./b/../c/"); // "a/c"
path.normalize("../x/../y"); // "../y"
path.normalize("/../etc"); // "/etc"
path.normalize("a/.."); // "."Because it doesn’t read the disk, normalize("link/..") is "." even if
link is a symbolic link to another directory.
Last modified: