# `Firkin.GetOpts`
[🔗](https://harton.dev/james/firkin)

Options for GetObject, including range and conditional request headers.

# `range`

```elixir
@type range() ::
  {non_neg_integer(), non_neg_integer() | nil} | {nil, non_neg_integer()}
```

A single byte range as described by RFC 7233 §2.1.

  * `{first, last}` — the bytes from `first` to `last`, inclusive.
  * `{first, nil}` — from `first` to the end of the object.
  * `{nil, suffix}` — the final `suffix` bytes of the object.

The open bounds cannot be resolved when the request is parsed because
the object's size is not known until the backend looks it up, so
backends must call `resolve_range/2` before applying the range.

# `t`

```elixir
@type t() :: %Firkin.GetOpts{
  if_match: String.t() | nil,
  if_modified_since: DateTime.t() | nil,
  if_none_match: String.t() | nil,
  if_unmodified_since: DateTime.t() | nil,
  range: range() | nil
}
```

# `resolve_range`

```elixir
@spec resolve_range(range() | nil, non_neg_integer()) ::
  {:ok, {non_neg_integer(), non_neg_integer()}} | :none | :unsatisfiable
```

Resolves a byte range against the size of the object it applies to.

Returns `{:ok, {first, last}}` with both bounds present and clamped to
the object, `:none` when the request had no range, or `:unsatisfiable`
when the range names no bytes of the object — in which case the backend
should return `{:error, %Firkin.Error{code: :invalid_range}}` and the
Plug will respond with `416 Requested Range Not Satisfiable`.

## Examples

    iex> Firkin.GetOpts.resolve_range({2, 5}, 11)
    {:ok, {2, 5}}

    iex> Firkin.GetOpts.resolve_range({2, 99}, 11)
    {:ok, {2, 10}}

    iex> Firkin.GetOpts.resolve_range({6, nil}, 11)
    {:ok, {6, 10}}

    iex> Firkin.GetOpts.resolve_range({nil, 5}, 11)
    {:ok, {6, 10}}

    iex> Firkin.GetOpts.resolve_range({nil, 99}, 11)
    {:ok, {0, 10}}

    iex> Firkin.GetOpts.resolve_range({11, nil}, 11)
    :unsatisfiable

    iex> Firkin.GetOpts.resolve_range({nil, 0}, 11)
    :unsatisfiable

    iex> Firkin.GetOpts.resolve_range(nil, 11)
    :none

---

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