# `Mailglass.Repo`
[🔗](https://github.com/szTheory/mailglass/blob/v0.1.0/lib/mailglass/repo.ex#L1)

Thin facade over the host-configured `Ecto.Repo`.

Mailglass does not own a Repo — the host application does. Every context
module that needs Postgres routes through this facade, which resolves the
real Repo via `Application.get_env(:mailglass, :repo)` at call time.

Runtime resolution is deliberate: tests inject a test repo through
`config/test.exs`, host apps inject their Repo through
`config :mailglass, repo: MyApp.Repo`, and neither path requires
recompiling mailglass.

This module re-exports only what mailglass itself uses. Callers that need
lower-level operations call the host Repo directly.

## SQLSTATE 45A01 translation (D-06)

Every write that touches `mailglass_events` can raise the immutability
trigger (`BEFORE UPDATE OR DELETE` raises SQLSTATE 45A01). The facade
rescues `%Postgrex.Error{}` at every call site and reraises as
`Mailglass.EventLedgerImmutableError` so callers pattern-match a
mailglass-owned error, never the raw Postgrex struct. The translation
is centralized in `translate_postgrex_error/2` — adding new write
functions means wiring the same rescue clause.

# `all`
*since 0.1.0* 

```elixir
@spec all(
  Ecto.Queryable.t(),
  keyword()
) :: [struct()]
```

Delegates to the host Repo's `all/2`.

# `delete`
*since 0.1.0* 

```elixir
@spec delete(
  struct() | Ecto.Changeset.t(),
  keyword()
) :: {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

Delegates to the host Repo's `delete/2`, translating event-ledger immutability errors.

# `delete_all`
*since 0.1.0* 

```elixir
@spec delete_all(
  Ecto.Queryable.t(),
  keyword()
) :: {non_neg_integer(), nil | [term()]}
```

Delegates to the host Repo's `delete_all/2`. Used by
`Mailglass.Webhook.Pruner` (Phase 4 D-16) for retention-policy
DELETEs against `mailglass_webhook_events`.

Does NOT translate SQLSTATE 45A01 — that trigger fires only on
`mailglass_events` UPDATE/DELETE, not `mailglass_webhook_events`
(which is intentionally mutable + prunable per CONTEXT D-15 split).

# `get`
*since 0.1.0* 

```elixir
@spec get(Ecto.Queryable.t(), term(), keyword()) :: struct() | nil
```

Delegates to the host Repo's `get/3`.

# `insert`
*since 0.1.0* 

```elixir
@spec insert(
  Ecto.Changeset.t() | struct(),
  keyword()
) :: {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

Delegates to the host Repo's `insert/2`, translating event-ledger immutability errors.

# `multi`
*since 0.1.0* 

```elixir
@spec multi(
  Ecto.Multi.t(),
  keyword()
) :: {:ok, map()} | {:error, atom(), any(), map()}
```

Executes an `Ecto.Multi` against the host-configured repo and returns
the canonical `{:ok, changes}` / `{:error, step, reason, changes}` shape.

Added in Phase 3 Plan 01 so `Mailglass.Outbound` (Plan 05) can compose
two Multis (D-20) via a public function — `repo/0` is deliberately
private to keep the facade narrow.

Raises `Mailglass.ConfigError{type: :missing}` when `:repo` is not
configured (same path as `transact/2`). Event-ledger-immutability
errors (SQLSTATE 45A01) are translated via the same rescue as the
other write helpers.

# `one`
*since 0.1.0* 

```elixir
@spec one(
  Ecto.Queryable.t(),
  keyword()
) :: struct() | nil
```

Delegates to the host Repo's `one/2`.

# `query!`
*since 0.1.0* 

```elixir
@spec query!(String.t(), [term()]) :: %Postgrex.Result{
  columns: term(),
  command: term(),
  connection_id: term(),
  messages: term(),
  num_rows: term(),
  rows: term()
}
```

Delegates to the host Repo's `query!/2`. Raw passthrough — no SQLSTATE
translation.

Intentionally does NOT rescue `%Postgrex.Error{}`: the Phase 4 webhook
ingest Multi (Plan 06) calls `query!/2` from inside `Repo.transact/1`
to run `SET LOCAL statement_timeout = '2s'` + `SET LOCAL lock_timeout
= '500ms'` (D-29). Those `SET LOCAL` statements never produce SQLSTATE
45A01 — the immutability trigger fires only on UPDATE/DELETE against
`mailglass_events` rows — so translation would add latency for no gain
and muddle the semantics. Callers that need the trigger's translated
error use `insert/2`, `update/2`, `delete/2`, `transact/1`, or
`multi/1` instead.

# `transact`
*since 0.1.0* 

```elixir
@spec transact(
  (-&gt; {:ok, any()} | {:error, any()}),
  keyword()
) :: {:ok, any()} | {:error, any()}
```

Delegates to `c:Ecto.Repo.transact/2` on the host-configured Repo.

Preferred over `c:Ecto.Repo.transaction/2` because Ecto 3.13+ `transact/2`
accepts a zero-arity function that returns `{:ok, result}` or
`{:error, reason}` and rolls the transaction back on `:error` without
requiring `Ecto.Repo.rollback/1`.

Raises `Mailglass.ConfigError` of type `:missing` when `:repo` is not
configured. Raises `Mailglass.EventLedgerImmutableError` when the
mailglass_events immutability trigger fires (SQLSTATE 45A01).

## Examples

    Mailglass.Repo.transact(fn ->
      {:ok, inserted} = Mailglass.Events.append(multi)
      {:ok, inserted}
    end)

# `update`
*since 0.1.0* 

```elixir
@spec update(
  Ecto.Changeset.t(),
  keyword()
) :: {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

Delegates to the host Repo's `update/2`, translating event-ledger immutability errors.

---

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