# `ImagePipe.Source`
[🔗](https://github.com/hlindset/image_pipe/blob/v0.2.0/image_pipe/lib/image_pipe/source.ex#L1)

Behaviour for source adapters, and the shape of the `:sources` option of
`ImagePipe.config/1`.

## Configuring sources

`:sources` is a keyword list of sources. Each has a name, an adapter, the
image paths it serves, and the adapter's options:

    sources: [
      media: [
        adapter: ImagePipe.Source.File,
        match: [prefix: "media"],
        options: [root: "/srv/images", root_id: "media"]
      ],
      web: [
        adapter: ImagePipe.Source.HTTP,
        match: [scheme: ["http", "https"]],
        options: [allowed_hosts: ["assets.example.com"]]
      ]
    ]

  * `:adapter` - `ImagePipe.Source.File`, `ImagePipe.Source.HTTP`,
    `ImagePipe.Source.S3`, or a module implementing this behaviour.
  * `:match` - which image paths reach the source: `:path`, or a keyword
    list of `:prefix` and `:scheme` rules. The rules are listed in
    [routing image paths to sources](sources.md#routing-image-paths-to-sources).
  * `:options` - the adapter's options, checked by its
    `c:validate_options/1`. The default value is `[]`.

Building a configuration with an invalid source raises `ArgumentError`.
The source's name appears in
[telemetry events](telemetry-events.md#common-metadata) as `:source_name`.

## Adapters

An adapter's callbacks receive its own validated options and a set of
runtime limits, never the rest of the configuration. Writing one is covered
in [Writing a custom source](custom-sources.md).

## Errors

An adapter fails a request by returning `{:error, {:source, reason}}`. Each
reason below gives the listed status, as it does for the built-in adapters:

  * `:not_found` - `404`.
  * `{:bad_status, code}` - the origin's HTTP status. `401`, `403`, `404`,
    and `410` give `404`. Any other status gives `502`.
  * `:body_too_large` - `413`.
  * `:receive_timeout` - `504`.
  * `:unreadable` and `:credentials_unavailable` - `500`.

Any other reason gives `502`. To choose the status, lead the reason with
a class: `{:source, {class, detail}}`, where `class` is `:bad_request`
(`400`), `:not_found` (`404`), `:payload_too_large` (`413`),
`:unsupported_media` (`415`), `:server_error` (`500`), `:not_implemented`
(`501`), `:bad_gateway` (`502`), or `:gateway_timeout` (`504`). The detail
never reaches the response body. [Error responses](errors.md) lists every
status.

# `error`

```elixir
@type error() :: {:source, atom() | tuple()}
```

# `parsed`

```elixir
@type parsed() ::
  ImagePipe.Source.Path.t()
  | ImagePipe.Source.URL.t()
  | ImagePipe.Source.Object.t()
```

A parsed image source: a path, an absolute URL, or a storage object.

# `fetch`

```elixir
@callback fetch(ImagePipe.Source.Resolved.t(), keyword(), keyword()) ::
  {:ok, ImagePipe.Source.Response.t()}
  | {:not_modified, ImagePipe.Source.Origin.t()}
  | {:error, error()}
```

Fetches the original's bytes as an `ImagePipe.Source.Response`, with
exactly one of `stream` or `path`. A stream is subject to the
`:max_body_bytes` limit.

The third argument holds runtime limits: `:max_body_bytes`, the transport
timeouts, and the telemetry prefix. `{:not_modified, origin}` answers a
revalidation of origin headers the adapter returned before.

# `identifiers`

```elixir
@callback identifiers(options :: keyword()) :: [module()]
```

Receives the options returned by `c:validate_options/1` and returns the
identifier structs `c:resolve/3` accepts with them, from
`ImagePipe.Source.Path`, `ImagePipe.Source.URL`, and
`ImagePipe.Source.Object`. A source whose match rules would route another
identifier to the adapter fails configuration.

# `resolve`

```elixir
@callback resolve(parsed(), keyword(), keyword()) ::
  {:ok, ImagePipe.Source.Resolved.t()} | {:error, error()}
```

Describes an image without fetching it.

The first argument is the requested image as one of the structs from
`c:identifiers/1`, with the source's matching prefix or custom scheme
removed. The returned `ImagePipe.Source.Resolved`
holds:

  * `identity` - a keyword list that names the original, with atom keys
    and strings, numbers, booleans, `nil`, non-module atoms, or lists of
    them as values. It must include everything that selects different
    bytes, or two originals share cache entries.
  * `internal_cache`, `http_cache`, and `cache_semantics` - built with
    `ImagePipe.Source.CacheSettings.fields/2`.
  * `fetch` - any data `c:fetch/3` needs.

The third argument holds runtime limits. Return
`{:error, {:source, reason}}` for a source the adapter can't serve. The
status each reason gives is listed under [errors](#module-errors). Any
other return value fails the request with `500`.

# `validate_options`

```elixir
@callback validate_options(keyword()) :: {:ok, keyword()} | {:error, term()}
```

Checks the source's `:options` when the configuration is built. The options
it returns are passed to `c:resolve/3` and `c:fetch/3`. An error fails the
configuration with `ArgumentError`.

---

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