# `DoubleDown.Contract`
[🔗](https://github.com/mccraigmccraig/double_down/blob/main/lib/double_down/contract.ex#L1)

Macro for defining contract behaviours with `defcallback` declarations.

`use DoubleDown.Contract` imports the `defcallback` macro and registers a
`@before_compile` hook that generates:

  * `@callback` declarations on the contract module itself — the
    contract module *is* the behaviour
  * `__callbacks__/0` — introspection metadata used by `DoubleDown.ContractFacade`
    to generate dispatch functions

Contracts are purely static interface definitions. They do **not**
generate a dispatch facade — that is the concern of
`DoubleDown.ContractFacade`, which the consuming application uses separately to
bind a contract to an OTP application's config.

## Usage

    defmodule MyApp.Todos do
      use DoubleDown.Contract

      defcallback get_todo(tenant_id :: String.t(), id :: String.t()) ::
        {:ok, Todo.t()} | {:error, term()}

      defcallback list_todos(tenant_id :: String.t()) :: [Todo.t()]
    end

This generates `@callback` declarations on `MyApp.Todos` and
`MyApp.Todos.__callbacks__/0`.

Implementations use `@behaviour MyApp.Todos` directly:

    defmodule MyApp.Todos.Ecto do
      @behaviour MyApp.Todos
      # ...
    end

Compatible with `Mox.defmock(Mock, for: MyApp.Todos)`.

To generate a dispatch facade, use `DoubleDown.ContractFacade` in a separate module:

    defmodule MyApp.Todos do
      use DoubleDown.ContractFacade, contract: MyApp.Todos.Contract, otp_app: :my_app
    end

See `DoubleDown.ContractFacade` for dispatch configuration and `DoubleDown` for an overview.

# `defcallback`
*macro* 

Define a typed callback operation.

`defcallback` uses a superset of the standard `@callback` syntax,
with mandatory parameter names and optional metadata. If your
existing `@callback` declarations include parameter names, you can
replace `@callback` with `defcallback` and you're done:

    # Standard @callback — already valid as a defcallback
    @callback get_todo(id :: String.t()) :: {:ok, Todo.t()} | {:error, term()}

    # Equivalent defcallback
    defcallback get_todo(id :: String.t()) :: {:ok, Todo.t()} | {:error, term()}

## Why `defcallback` instead of plain `@callback`?

  * **Parameter names are mandatory.** Plain `@callback` allows
    unnamed parameters like `@callback get(term(), term()) :: term()`.
    `defcallback` requires `name :: type()` for every parameter — these
    are used to generate meaningful `@spec` and `@doc` on the facade.

  * **Combined contract + facade.** `Code.Typespec.fetch_callbacks/1`
    only works on pre-compiled modules with beam files on disk, ruling
    out the combined contract + facade pattern entirely. `defcallback`
    captures metadata at macro expansion time via `__callbacks__/0`,
    so the contract and facade can live in the same module.

  * **LSP-friendly docs.** Plain `@callback` declarations don't
    support `@doc` — the best you can do is `#` comments that won't
    appear in hover docs. With `defcallback`, `@doc` placed above the
    declaration resolves on both the declaration and on any call site
    that goes through the facade.

  * **Additional metadata.** `defcallback` supports options like
    `pre_dispatch:` (argument transforms before dispatch). Plain
    `@callback` has no mechanism for this.

See [Why `defcallback` instead of plain `@callback`?](docs/getting-started.md#why-defcallback-instead-of-plain-callback)
in the Getting Started guide for the full rationale.

## Syntax

    defcallback function_name(param :: type(), ...) :: return_type()
    defcallback function_name(param :: type(), ...) :: return_type(), opts

## Options

### Pre-dispatch transform (`:pre_dispatch`)

  * **`:pre_dispatch`** — a function `(args, facade_module) -> args` that
    transforms the argument list before dispatch. The function receives the
    args as a list and the facade module atom, and must return the
    (possibly modified) args list. This is useful for injecting
    facade-specific context into arguments at the dispatch boundary.
    Most contracts don't need this — the canonical example is
    `DoubleDown.Repo`'s `transact` operation.

### Typespec mismatch severity (`:warn_on_typespec_mismatch?`)

  * **omitted / `false`** (default) — raise `CompileError` when the
    `defcallback` type spec doesn't match the production impl's `@spec`.
  * **`true`** — emit a warning instead of an error. Use this during
    migration when you know the specs differ and want to defer fixing them.

See `DoubleDown.Contract.SpecWarnings` for details on compile-time
spec mismatch detection.

---

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