# `Host.File`
[🔗](https://github.com/aaronrussell/beamlet/blob/v0.1.0/lib/host/file.ex#L1)

`File`, scoped to your beamlet's files.

Paths are relative to the files root, and a leading `/` means that
same root: `"notes.md"` and `"/notes.md"` are the same file, and
`..` cannot leave it. Names, arguments and results mirror `File`'s.
A function returns `:ok`, `{:ok, value}` or `{:error, reason}` with
a posix reason such as `:enoent`; its bang variant returns the
value or raises what `File`'s does, naming the path as you wrote
it. Code written for `File` works here. The one addition: writing,
copying and renaming create the parent directories they need.

    Host.File.write!("journal/2026/today.md", "entry")
    Host.File.read!("journal/2026/today.md")
    #=> "entry"

    case Host.File.read("settings.json") do
      {:ok, json} -> JSON.decode!(json)
      {:error, :enoent} -> %{}
    end

There is one filesystem for the whole beamlet: every agent, every
defined module and every process sees the same files, including
code serving web routes, so organise shared work with directories.
Files persist across evals and restarts. Nothing here runs as
anyone, so it works the same from an eval, a controller or a
LiveView. `ls_r/1` shows everything there is.

# `cp`

```elixir
@spec cp(String.t(), String.t()) :: :ok | {:error, File.posix()}
```

Copies the file at `source` to `dest`, creating parent directories
as needed, e.g. `cp("draft.md", "archive/draft.md")`.

Files only; see `cp_r/2` for a directory.

# `cp!`

```elixir
@spec cp!(String.t(), String.t()) :: :ok
```

Like `cp/2`, returning `:ok` or raising `File.CopyError`.

# `cp_r`

```elixir
@spec cp_r(String.t(), String.t()) ::
  {:ok, [String.t()]} | {:error, File.posix(), String.t()}
```

Copies `source` to `dest` recursively, creating parent directories
as needed, e.g. `cp_r("notes", "backup/notes")`.

Returns the paths copied, relative to the root, or the error with
the path it happened on.

# `cp_r!`

```elixir
@spec cp_r!(String.t(), String.t()) :: [String.t()]
```

Like `cp_r/2`, returning the paths copied or raising `File.CopyError`.

# `dir?`

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

Whether `path` is a directory.

# `exists?`

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

Whether a file or directory exists at `path`.

# `ls`

```elixir
@spec ls(String.t()) :: {:ok, [String.t()]} | {:error, File.posix()}
```

Returns the names in the directory at `path`, sorted, e.g.
`ls("journal")`.

The root when `path` is omitted. Files and directories alike, names
only; for every file beneath a directory use `ls_r/1`.

# `ls!`

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

Like `ls/1`, returning the names or raising `File.Error`.

# `ls_r`

```elixir
@spec ls_r(String.t()) :: {:ok, [String.t()]} | {:error, File.posix()}
```

Returns every file under the directory at `path`, sorted, as paths
relative to the root, ready to pass to `read/1`.

The whole filesystem when `path` is omitted.

Directories appear through the files they contain; an empty
directory is not listed. A fresh filesystem lists as `{:ok, []}`.

# `ls_r!`

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

Like `ls_r/1`, returning the paths or raising `File.Error`.

# `mkdir`

```elixir
@spec mkdir(String.t()) :: :ok | {:error, File.posix()}
```

Creates the directory at `path`; its parent must exist.

Writing a file creates its parents on its own, so this is for a
directory wanted ahead of any file. See `mkdir_p/1` for missing
parents.

# `mkdir!`

```elixir
@spec mkdir!(String.t()) :: :ok
```

Like `mkdir/1`, returning `:ok` or raising `File.Error`.

# `mkdir_p`

```elixir
@spec mkdir_p(String.t()) :: :ok | {:error, File.posix()}
```

Creates the directory at `path`, including missing parents, e.g. `mkdir_p("a/b/c")`.

# `mkdir_p!`

```elixir
@spec mkdir_p!(String.t()) :: :ok
```

Like `mkdir_p/1`, returning `:ok` or raising `File.Error`.

# `read`

```elixir
@spec read(String.t()) :: {:ok, binary()} | {:error, File.posix()}
```

Returns the contents of the file at `path`, e.g.
`read("notes.md")`.

# `read!`

```elixir
@spec read!(String.t()) :: binary()
```

Like `read/1`, returning the contents or raising `File.Error`.

# `regular?`

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

Whether `path` is a regular file.

# `rename`

```elixir
@spec rename(String.t(), String.t()) :: :ok | {:error, File.posix()}
```

Renames or moves `source` to `dest`, file or directory, creating
parent directories as needed, e.g.
`rename("draft.md", "posts/final.md")`.

# `rename!`

```elixir
@spec rename!(String.t(), String.t()) :: :ok
```

Like `rename/2`, returning `:ok` or raising `File.RenameError`.

# `rm`

```elixir
@spec rm(String.t()) :: :ok | {:error, File.posix()}
```

Removes the file at `path`.

Files only: a directory answers `{:error, :eisdir}`. Use `rmdir/1`
for an empty directory.

# `rm!`

```elixir
@spec rm!(String.t()) :: :ok
```

Like `rm/1`, returning `:ok` or raising `File.Error`.

# `rmdir`

```elixir
@spec rmdir(String.t()) :: :ok | {:error, File.posix()}
```

Removes the empty directory at `path`.

# `rmdir!`

```elixir
@spec rmdir!(String.t()) :: :ok
```

Like `rmdir/1`, returning `:ok` or raising `File.Error`.

# `write`

```elixir
@spec write(String.t(), iodata(), [File.mode()]) :: :ok | {:error, File.posix()}
```

Writes `content` to the file at `path`, creating parent directories
as needed and overwriting an existing file, e.g.
`write("journal/today.md", text)`.

Pass `[:append]` to add to the end instead.

# `write!`

```elixir
@spec write!(String.t(), iodata(), [File.mode()]) :: :ok
```

Like `write/3`, returning `:ok` or raising `File.Error`.

---

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