Skip to main content

lib/image/plug/provider.ex

defmodule Image.Plug.Provider do
  @moduledoc """
  Behaviour for URL-API providers.

  A provider parses an inbound `Plug.Conn` into a canonical
  `Image.Plug.Pipeline` plus an `Image.Plug.Source` reference. It
  may also resolve the request to a named variant lookup or to a
  passthrough that streams the source unchanged.

  Providers are stateless. All configuration is passed in by
  `Image.Plug` as the second argument to `c:parse/2`.
  """

  alias Image.Plug.{Error, Pipeline, Source}

  @typedoc """
  An override applied on top of a resolved variant. The provider
  produces these from per-request options that should win over the
  stored variant's defaults.
  """
  @type override :: {atom(), term()}

  @typedoc """
  The shape returned by `c:parse/2` on success.

  * `{:pipeline, pipeline, source}` — fully-formed pipeline; resolve
    the source and execute.

  * `{:variant, name, overrides, source}` — look up the variant by
    name in the configured `Image.Plug.VariantStore`, then merge
    overrides on top before executing.

  * `{:passthrough, source}` — no transforms. The plug streams the
    source unchanged.

  * `{:info, info_kind, source}` — the request is for a metadata
    document (currently only `:iiif_image_info` for the IIIF Image
    API 3.0 `info.json`). The plug builds the document by reading
    the source's dimensions and serialises as JSON.
  """
  @type info_kind :: :iiif_image_info

  @type result ::
          {:pipeline, Pipeline.t(), Source.t()}
          | {:variant, name :: String.t(), [override()], Source.t()}
          | {:passthrough, Source.t()}
          | {:info, info_kind(), Source.t()}

  @doc """
  Parses a request into one of the `t:result/0` shapes.

  Implementations must be pure URL/header parsing. They must not fetch
  source bytes or look up variants — those are the responsibilities of
  `Image.Plug.SourceResolver` and `Image.Plug.VariantStore`.

  Implementations must not raise on malformed input; return a tagged
  `Image.Plug.Error` instead.
  """
  @callback parse(Plug.Conn.t(), options :: keyword()) ::
              {:ok, result()} | {:error, Error.t()}
end