# `VFS.Path`
[🔗](https://github.com/ivarvong/vfs/blob/v0.1.0/lib/vfs/path.ex#L1)

Pure path utilities for `VFS`.

All public ops on `VFS` and the `VFS.Mountable` protocol expect absolute,
already-normalized paths with a leading `/`. This module is the canonical
place for path manipulation; backends should not roll their own.

Paths are POSIX-shaped: `/`-separated, with `.` (current) and `..` (parent)
segments collapsed by `normalize/1`. `..` at the root is a no-op (root is
its own parent), matching POSIX semantics.

# `t`

```elixir
@type t() :: String.t()
```

An absolute, normalized path with a leading `/` and no trailing slash (except root).

# `absolute?`

```elixir
@spec absolute?(String.t()) :: boolean()
```

Returns true if `path` starts with `/`.

## Examples

    iex> VFS.Path.absolute?("/foo")
    true

    iex> VFS.Path.absolute?("foo")
    false

# `basename`

```elixir
@spec basename(t()) :: String.t()
```

Last segment of a path. The basename of the root is the empty string.

## Examples

    iex> VFS.Path.basename("/foo/bar")
    "bar"

    iex> VFS.Path.basename("/")
    ""

# `dirname`

```elixir
@spec dirname(t()) :: t()
```

Parent directory. The parent of `/` is `/` (POSIX semantics).

## Examples

    iex> VFS.Path.dirname("/foo/bar")
    "/foo"

    iex> VFS.Path.dirname("/foo")
    "/"

    iex> VFS.Path.dirname("/")
    "/"

# `join`

```elixir
@spec join(t(), String.t()) :: t()
```

Join an absolute base with a relative or absolute path. The result is
normalized. If `path` is itself absolute, the base is ignored (matches
`Path.join/2` behavior).

## Examples

    iex> VFS.Path.join("/foo", "bar")
    "/foo/bar"

    iex> VFS.Path.join("/foo/bar", "../baz")
    "/foo/baz"

    iex> VFS.Path.join("/foo", "/abs/ignored-base")
    "/abs/ignored-base"

# `normalize`

```elixir
@spec normalize(String.t()) :: t()
```

Normalize an absolute path: collapse `.` and `..` segments and remove
consecutive slashes. The result has a leading `/` and no trailing slash
except for the root.

Raises `ArgumentError` on non-absolute input.

## Examples

    iex> VFS.Path.normalize("/foo/./bar/../baz")
    "/foo/baz"

    iex> VFS.Path.normalize("//foo//bar/")
    "/foo/bar"

    iex> VFS.Path.normalize("/")
    "/"

    iex> VFS.Path.normalize("/../foo")
    "/foo"

# `relative_to`

```elixir
@spec relative_to(t(), t()) :: {:ok, t()} | :error
```

Re-root `path` under `prefix`, returning the remainder as an absolute path
under the new root. Returns `:error` when `prefix` is not a path-segment
prefix of `path`. Used for mount-prefix stripping.

## Examples

    iex> VFS.Path.relative_to("/foo/bar/baz", "/foo")
    {:ok, "/bar/baz"}

    iex> VFS.Path.relative_to("/foo/bar", "/foo/bar")
    {:ok, "/"}

    iex> VFS.Path.relative_to("/foo/bar", "/")
    {:ok, "/foo/bar"}

    iex> VFS.Path.relative_to("/foobar", "/foo")
    :error

# `split`

```elixir
@spec split(t()) :: [String.t()]
```

Split a normalized path into its segments. The root splits to `[]`.

## Examples

    iex> VFS.Path.split("/foo/bar/baz")
    ["foo", "bar", "baz"]

    iex> VFS.Path.split("/")
    []

---

*Consult [api-reference.md](api-reference.md) for complete listing*
