# `Formata.Codec`
[🔗](https://github.com/gsmlg-dev/formata/blob/v0.1.0/lib/formata/codec.ex#L1)

Behaviour for format adapters and the portable value domain they share.

Built-in codecs:

| Format  | Module                | Decoder          | Encoder                     |
| ------- | --------------------- | ---------------- | --------------------------- |
| `:json` | `Formata.Codec.JSON`  | `Formata.Jason`          | `Formata.Jason`                     |
| `:toml` | `Formata.Codec.TOML`  | `Formata.Toml` (gsmlg_toml) | `Formata.Codec.TOML.Encoder` |
| `:yaml` | `Formata.Codec.YAML`  | `Formata.YamlElixir`     | `Formata.Ymlr`                      |

A custom codec is any module implementing this behaviour; pass the module
wherever a format atom is accepted.

## Portable value domain

Decoders return ordinary Elixir terms:

| Value                 | Elixir representation                                  |
| --------------------- | ------------------------------------------------------ |
| null                  | `nil`                                                  |
| boolean               | `true` / `false`                                       |
| integer               | integer                                                |
| float                 | float                                                  |
| +infinity / -infinity / NaN | `:infinity` / `:negative_infinity` / `:nan`      |
| string                | UTF-8 binary                                           |
| array / sequence      | list                                                   |
| object / table / mapping | map with string keys                                |
| local date / local time | `Date` / `Time`                                      |
| local date-time       | `NaiveDateTime`                                        |
| offset date-time      | `DateTime`                                             |

Keys from documents stay strings; Formata never creates atoms from document
content. A field that is absent is absent from the map; an explicit null is
present with the value `nil`.

Each encoder accepts only the subset its format can represent and rejects
everything else with an `:encode` error (`:unrepresentable`, `:invalid_key`,
`:invalid_root`, `:out_of_range`). Nothing is dropped, stringified, or
rounded implicitly; use `Formata.Transform` for explicit conversions.

# `format`

```elixir
@type format() :: :json | :toml | :yaml | module()
```

# `result`

```elixir
@type result(t) :: {:ok, t} | {:error, [Formata.Error.t(), ...]}
```

# `decode`

```elixir
@callback decode(input :: binary(), opts :: keyword()) :: result(term())
```

Decodes a complete document.

# `encode`

```elixir
@callback encode(data :: term(), opts :: keyword()) :: result(binary())
```

Encodes a term as a complete document.

# `format`

```elixir
@callback format() :: atom()
```

Returns the format name used in errors.

# `fetch`

```elixir
@spec fetch(format()) :: {:ok, module()} | {:error, [Formata.Error.t(), ...]}
```

Resolves a format atom or codec module to a codec module.

    iex> Formata.Codec.fetch(:json)
    {:ok, Formata.Codec.JSON}

    iex> {:error, [%Formata.Error{kind: :unsupported, code: :unknown_format}]} = Formata.Codec.fetch(:xml)

# `formats`

```elixir
@spec formats() :: [atom()]
```

The built-in format names.

---

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