# `F3.IO.FileSystem`

Operating-system calls used by `F3.IO.Local`.

Production code uses `F3.IO.FileSystem.OS`. Tests pass a Mox double as
`:file_system` on `F3.IO.Local.open/2` so read and close failures can be
returned without changing a real file.

A double returns the same tagged tuples as `File` and `:file`:

    defmodule StubFiles do
      @behaviour F3.IO.FileSystem

      @impl true
      def stat(_path), do: {:ok, %File.Stat{type: :regular, size: 2}}

      @impl true
      def open(_path, _modes), do: {:ok, :fd}

      @impl true
      def pread(_fd, _offset, length), do: {:ok, String.duplicate("a", length)}

      @impl true
      def close(_fd), do: :ok
    end

# `close`

```elixir
@callback close(term()) :: :ok | {:error, atom()}
```

Closes a descriptor. `:ok` is success. `{:error, reason}` is an OS error.

# `open`

```elixir
@callback open(Path.t(), [atom()]) :: {:ok, term()} | {:error, atom()}
```

Opens `path` with `modes`. The modes used by `F3.IO.Local` are
`[:read, :raw, :binary]`.

# `pread`

```elixir
@callback pread(term(), non_neg_integer(), non_neg_integer()) ::
  {:ok, binary()} | :eof | {:error, atom()}
```

Reads `length` bytes at `offset` from an open descriptor.
`:eof` means the position is past the file.

# `stat`

```elixir
@callback stat(Path.t()) :: {:ok, File.Stat.t()} | {:error, atom()}
```

File metadata for `path`. A missing path is `{:error, :enoent}`.

---

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