# `F3.Registry`

Bounded ETS table of `F3.Extension` modules.

The caller starts the process and passes that pid as `extensions:` to
`F3.read/2` or `F3.stream/2`. Two reads share registrations only when they
are given the same pid. The key is the footer encoding and its
`%{major, minor, patch}` version. `wasm_id` is part of the key only when
the registration sets it.

The table is public so a read can look up a module without taking the
owner lock. `register/3` and `unregister/2` go through the owner. A full
table returns `:resource_limit`. The default capacity is 64. The ceiling
is 1024.

# `key`

```elixir
@type key() :: %{
  :encoding =&gt; atom(),
  :version =&gt; %{
    major: non_neg_integer(),
    minor: non_neg_integer(),
    patch: non_neg_integer()
  },
  optional(:wasm_id) =&gt; non_neg_integer() | nil
}
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `lookup`

```elixir
@spec lookup(pid() | nil, map()) :: {:ok, module()} | :error
```

Finds a module for one encoding unit. `:error` when the pid is dead, the
table is gone, or nothing matches. A registration that set `wasm_id`
matches only that id. A registration that omitted it matches any id,
after the exact id is tried. `nil` is `:error` and does not raise.

## Examples

    iex> F3.Registry.lookup(nil, %{encoding: :cascade})
    :error

# `register`

```elixir
@spec register(pid(), key(), module()) :: :ok | {:error, F3.Error.t()}
```

Registers `module` under the footer key. Replacing an existing key does
not consume another slot.

`key` needs `:encoding` and `:version` with `:major`, `:minor`, and
`:patch`. `:wasm_id` is optional. A bad argument is `:invalid_argument`.

## Examples

    iex> {:ok, pid} = F3.Registry.start_link(capacity: 1)
    iex> key = %{encoding: :custom_wasm, version: %{major: 1, minor: 0, patch: 0}}
    iex> :ok = F3.Registry.register(pid, key, F3.Column)
    iex> F3.Registry.lookup(pid, Map.put(key, :wasm_id, 0))
    {:ok, F3.Column}

# `start_link`

```elixir
@spec start_link(keyword()) :: {:ok, pid()} | {:error, F3.Error.t()}
```

Starts an empty registry. `capacity:` defaults to 64
and must be from 1 to 1024. Any other value is
`:invalid_argument` and does not start a process.

## Examples

    iex> {:ok, pid} = F3.Registry.start_link(capacity: 1)
    iex> is_pid(pid)
    true

    iex> {:error, error} = F3.Registry.start_link(capacity: 0)
    iex> error.category
    :invalid_argument

# `unregister`

```elixir
@spec unregister(pid(), key()) :: :ok | {:error, F3.Error.t()}
```

Removes one key. A missing key is `:ok`.

## Examples

    iex> {:ok, pid} = F3.Registry.start_link()
    iex> key = %{encoding: :cascade, version: %{major: 0, minor: 21, patch: 0}}
    iex> F3.Registry.unregister(pid, key)
    :ok

---

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