# `HexPort.Dispatch`
[🔗](https://github.com/mccraigmccraig/hex_port/blob/main/lib/hex_port/dispatch.ex#L1)

Dispatch resolution for HexPort contracts.

Two dispatch paths are available, selected at compile time by the
`:test_dispatch?` option on `use HexPort.Facade`:

### `call/4` — test-aware dispatch (default in non-prod)

1. **Test handler** — process-scoped via `NimbleOwnership`
   (only checked if the ownership server is running)
2. **Application config** — `Application.get_env(otp_app, contract)[:impl]`
3. **Raise** — no handler configured

### `call_config/4` — config-only dispatch (default in prod)

1. **Application config** — `Application.get_env(otp_app, contract)[:impl]`
2. **Raise** — no handler configured

No `NimbleOwnership` code is referenced in the generated facade
functions, eliminating the `GenServer.whereis` lookup entirely.

# `call`

```elixir
@spec call(atom() | nil, module(), atom(), [term()]) :: term()
```

Dispatch a port operation to the resolved implementation.

Called by generated facade functions when `test_dispatch?: true`
(the default in non-production environments). Checks for a
process-scoped test handler via `NimbleOwnership` before falling
back to application config.

# `call_config`

```elixir
@spec call_config(atom() | nil, module(), atom(), [term()]) :: term()
```

Dispatch a port operation directly from application config.

Called by generated facade functions when `test_dispatch?: false`
(the default in production). Skips the `NimbleOwnership` test
handler lookup entirely — zero overhead beyond `Application.get_env`.

# `key`

```elixir
@spec key(module(), atom(), [term()]) :: term()
```

Build a canonical key for test stub matching.

Keys are normalized so that map/keyword argument order doesn't affect matching.

---

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