# `Fil.Disk`
[🔗](https://github.com/pehbehbeh/fil/blob/v0.1.0/lib/fil/disk.ex#L1)

A disk is a plain value that describes where files are stored.

Build one with `Fil.disk/1` (short for `new/1`) and pass it around. There's no global configuration, no registry and
no process behind it:

    disk = Fil.disk(adapter: Fil.Adapter.Local, root: "priv/storage")

The struct holds the adapter module together with the state its `c:Fil.Adapter.init/1` returned, and the plugins
attached with `Fil.attach/4` or the `:plugins` option of `new/1`. The adapter state may contain
credentials, so `%Fil.Disk{}` has a custom `Inspect` implementation that prints only the adapter label:

    iex> inspect(Fil.disk(adapter: Fil.Adapter.Local, root: "/tmp/fil"))
    "#Fil.Disk<local>"

Refs and errors contain the disk too. Logging them is safe, because logs use `inspect`, but anything that stores the
raw term (`:erlang.term_to_binary/1`, a crash dump) stores the credentials with it.

# `t`

```elixir
@type t() :: %Fil.Disk{
  adapter: {module(), term()},
  plugins: [{atom(), Fil.plugin_callback(), keyword()}]
}
```

# `adapter`

```elixir
@spec adapter(t()) :: module()
```

Returns the adapter module backing `disk`.

    iex> Fil.Disk.adapter(Fil.disk(adapter: Fil.Adapter.Local, root: "/tmp/fil"))
    Fil.Adapter.Local

# `label`

```elixir
@spec label(t()) :: String.t()
@spec label(module()) :: String.t()
```

A short, human readable label for the disk's adapter.

Used by the `Inspect` implementations of `Fil.Disk` and `Fil.Ref`.

    iex> Fil.Disk.label(Fil.disk(adapter: Fil.Adapter.Local, root: "/tmp/fil"))
    "local"

# `new`

```elixir
@spec new(keyword()) :: t()
```

Builds a disk. `Fil.disk/1` is the same function.

    iex> Fil.Disk.new(adapter: Fil.Adapter.Local, root: "/tmp/fil")
    #Fil.Disk<local>

The adapter validates its options here, once. Invalid options raise `ArgumentError` when the disk is built instead of
failing on first use.

## Options

* `:adapter` (`t:atom/0`) - Required. The adapter module. Every option other than `:adapter` and `:plugins` is passed to the adapter, which
  validates it against its own schema.

* `:plugins` (list of tuple of `t:atom/0`, `t:atom/0`, `t:keyword/0` values) - Plugins to attach, in order, so the first one is the outermost. Each is a plugin callback as
  `{module, function, opts}`, attached under the name `module` (see `Fil.attach/4`). They're plain data, so
  the plugins can come from config along with the adapter options:
  `plugins: [{Fil.Plugin.ContentType, :call, default: "text/plain"}]`. The default value is `[]`.

