# `Zizq.Codec`
[🔗](https://github.com/zizq-labs/zizq-elixir/blob/v0.8.0/lib/zizq/codec.ex#L4)

Serialization format used for request and response bodies.

The Zizq server speaks both JSON and MessagePack on every endpoint,
and the two are freely interchangeable: a job enqueued as MessagePack
can be read back as JSON, so a producer and a consumer need not be
configured with the same codec — or even be written in the same
language. Choose `Zizq.Codec.MessagePack` (the default) for
compactness, or `Zizq.Codec.JSON` when you want human-readable
traffic.

## Payloads

Whichever codec is in use, a job payload must be JSON-compatible.
The server types payloads as a JSON value, so MessagePack's binary
(`bin`) type is rejected. Encode raw bytes as base64 in a string,
exactly as you would with JSON. Both codecs also hand payloads back
with **string keys**, since neither preserves atoms, so `perform/2`
pattern matches identically either way.

## Custom codecs

The behaviour is public so a project stuck on an Elixir older than
1.18 (before the built-in `JSON` module) can theoretically supply a
`Jason`-backed codec without this library carrying the dependency.

# `format`

```elixir
@type format() :: :json | :msgpack
```

Shorthand names for the built-in codecs.

# `t`

```elixir
@type t() :: module()
```

A module implementing this behaviour.

# `content_type`

```elixir
@callback content_type() :: String.t()
```

Media type for request/response bodies.

# `decode`

```elixir
@callback decode(iodata()) :: {:ok, term()} | {:error, Exception.t()}
```

Deserialize a response body. Accepts iodata.

# `encode`

```elixir
@callback encode(term()) :: {:ok, iodata()} | {:error, Exception.t()}
```

Serialize a term into a request body.

Returns iodata rather than a binary so the HTTP layer can write it
without a final concatenation.

# `framing`

```elixir
@callback framing() :: :line_delimited | :length_prefixed
```

How streaming endpoints delimit records.

  * `:line_delimited` — one record per line, terminated by `\n`; an
    empty line is a heartbeat.
  * `:length_prefixed` — each record is a big-endian `u32` byte count
    followed by that many bytes; a zero length is a heartbeat.

Declared here rather than inferred from the codec module so a custom
codec can say which framing its format uses.

# `stream_content_type`

```elixir
@callback stream_content_type() :: String.t()
```

Media type for streaming endpoints.

Streaming is framed differently from a request/response body
(newline-delimited JSON, or length-prefixed MessagePack), so it
negotiates a distinct media type.

# `fetch!`

```elixir
@spec fetch!(format() | t()) :: t()
```

Resolve a codec from a shorthand name, or validate a module.

## Examples

    iex> Zizq.Codec.fetch!(:msgpack)
    Zizq.Codec.MessagePack

    iex> Zizq.Codec.fetch!(Zizq.Codec.JSON)
    Zizq.Codec.JSON

# `from_content_type`

```elixir
@spec from_content_type(String.t()) :: {:ok, t()} | :error
```

Resolve a codec from a `Content-Type` header value.

Recognises both the request/response media types and the streaming
ones, ignores any parameters (`; charset=utf-8`), and matches
case-insensitively as RFC 9110 requires — a proxy is free to rewrite
the casing. Returns `:error` for anything unrecognised so callers can
fall back to the configured codec.

## Examples

    iex> Zizq.Codec.from_content_type("application/json; charset=utf-8")
    {:ok, Zizq.Codec.JSON}

    iex> Zizq.Codec.from_content_type("application/vnd.zizq.msgpack-stream")
    {:ok, Zizq.Codec.MessagePack}

    iex> Zizq.Codec.from_content_type("text/plain")
    :error

---

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