Reference

std/path/path

std/path/src/path.trb

Path, a root plus the components between its separators - never a string - and Path.from, the infallible parse that builds one out of text.

Everything else follows from the type: Path.joined cannot be given a root, Path.parent cannot fall off the top, and .. is only ever resolved on purpose, by Path.normalized or Path.resolved. show() is the one text form, always with /; the operating system's own form is std/fs's business, not this package's.

Path is a capsule: its fields are private and without a default, so the only way in from outside the package is Path.from, and the way out is Path.show. Path.root and Path.components read what a value holds.

type Path

type Path with Show, Equals, Hash, Compare

A file path: where it starts, and the components between the separators.

A Path is a value, built from text with Path.from and read back with Path.show. It knows nothing about a disk - whether it names a real file is a question for std/fs - and it is Equals, Hash and Compare, all three lexical and case sensitive on every platform: two paths that differ only in case are two different values, because whether they name the same file is a question for a file system and not for this type.

It is a capsule: both fields are private and neither has a default, so nothing outside this package reaches the constructor and Path.from is the one way in. A component list can therefore not be handed in wholesale, and a parsed path never shows with a doubled separator. Encode and Decode come from the one conversion pair with String, so a path in a configuration file or in a JSON document is its text.

Examples

const module = Path.from "std/path/src/lib.trb"
print "{module.name()} {module.parent()}"

Pitfalls

  • .. is a component like any other, because dropping it would be a guess: Path.from("a/../b") keeps both, and Path.normalized is where a caller asks for it to be resolved as text.

Related

fn root

fn root(): Root?

Where the path starts, and None for a relative path.

fn components

fn components(): List<String>

The components between the separators, outermost first. A parsed path has neither an empty one nor a ..

fn isAbsolute

fn isAbsolute(): Bool

Whether the path starts at a root. Its opposite is Path.isRelative.

fn isRelative

fn isRelative(): Bool

Whether the path has no root, so that it means something only against a base.

fn name

fn name(): String?

The last component: the name of the file or of the directory. None for a root and for the empty path.

fn extension

fn extension(): String?

The extension of the name, without the dot. None where the name has none, and for .gitignore, whose only dot is the first character of the name.

fn nameWithoutExtension

fn nameWithoutExtension(): String?

The name without its extension and without the dot. main for src/main.trb.

fn parent

fn parent(): Path?

The path one component up. None at a root and for the empty path: there is nothing above them.

fn joined

fn joined(relative: Path): Path

relative below this path. The root of relative is dropped, so the result never leaves self.

fn withName

fn withName(name: String): Path?

The same path with its last component replaced. None where there is no name to replace.

fn withExtension

fn withExtension(extension: String): Path?

The same path with a different extension, added where there was none. An empty extension removes it.

fn startsWith

fn startsWith(prefix: Path): Bool

Whether this path starts at the same root and with the same components as prefix.

fn relativeTo

fn relativeTo(base: Path): Path?

This path as seen from base, with a .. per component of base that is not shared. None where the two roots differ - today's equivalent answers the absolute path there, and None lets the caller decide instead of guessing for it.

fn normalized

fn normalized(): Path

. and empty components dropped and .. resolved as text: against the root it vanishes, at the front of a relative path it stays. It reads no disk and follows no link, so a/../b normalized to b is a different file wherever a is a symlink.

fn resolved

fn resolved(inside: Path): Result<Path, PathError>

This path resolved below base, refusing to leave it. base is inside.normalized(); a relative self is joined under it and an absolute one is judged on its own, both normalized; the answer is Ok where the result Path.startsWith base and Fail otherwise.

Errors

Pitfalls

  • This is a lexical check: it says the text of this path does not leave base, not that the file it opens is under base. A symlink inside base that points outside it, a hard link, a bind mount and a race between the check and the open all defeat it. Closing that gap needs the operating system asked while it opens, which is std/fs's business and not this package's.

fn show

fn show(): String

The path with / as the separator, on every platform. . for the empty path.

fn compare

fn compare(other: Path): Ordering

Root first, then component by component, then by the number of components. Case sensitive everywhere.

extend Path with From<String>

extend Path with From<String>

A path is what the text says it is. Whether it names a file is what the file system says when it is opened.

Both / and \ separate, on every platform, so the same program behaves the same everywhere. Empty components and . are dropped, .. is kept, a trailing separator is dropped, and C:foo reads as C:/foo - drive-relative is the one form this type cannot hold, and an infallible parse cannot reject it either.

Examples

print Path.from("src/../main.trb")
print Path.from("C:foo")

Pitfalls

  • A POSIX file name that literally contains a backslash cannot be named through this conversion, because \ is a separator on every platform here, and a capsule has no second way in: such a name cannot be written down as a Path at all, and the file it names is reached through std/fs with its text.

Related

  • Path.show - the way back out, and the other half of the conversion pair Encode and Decode go through.

fn from

static fn from(value: String): Path

extend String with From<Path>

extend String with From<Path>

The way back out of Path, and with Path.from it is the conversion pair of the capsule: Path has From<String> and String has From<Path>, so the derived Encode writes a path as its text and the derived Decode reads a text and hands it to Path.from.

Examples

const path = Path.from "std/path"
print String.from(path)

Related

  • Path.show - the same text, and what this conversion is written in terms of.

fn from

static fn from(value: Path): String