# `Macaw.Layers.Document`
[🔗](https://github.com/dcoai/macaw_layers/blob/v0.3.0/lib/macaw/layers/document.ex#L1)

A document type's validity, judged on its value (spec/01 §5).

A document type implements this behavior: `c:validate/1`, which judges the type's
**value**, the terms Elixir holds (atom keys where the type has a key, string names
elsewhere, atoms, times, the type's own structs), never a representation of it. `check/2`
is how any value is judged, whether built in Elixir or read from an encoding:

    {:ok, value} = Macaw.Layers.Document.check(value, MyApp.Page)

The kernel knows no encoding here. Reading one is an adapter's: `Macaw.Layers.Form` is the
JSON adapter, whose `read/3` takes a JSON document through the gates (the bytes, the
scan, the version, the migration, the recount), reads its value from the map form, and
then judges the value by `check/2`. The limits bound what reading an encoding costs, so
they are the adapter's; a value is judged with none.

# `validate`

```elixir
@callback validate(value :: term()) :: :ok | {:error, [Macaw.Layers.Fault.t(), ...]}
```

`:ok`, or every fault of the type's value. Unknown keys are refused by name (see
`unknown_keys/3`).

# `check`

```elixir
@spec check(term(), module()) ::
  {:ok, term()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The value judged by its type's `c:validate/1`: `{:ok, value}`, or every fault, ordered by
path. No encoding is involved: a value holding a term no adapter can write is judged as
the type's own rules say, and fails only at that adapter.

# `digest`

```elixir
@spec digest(term()) :: String.t()
```

The canonical digest of a value (spec/01 §7): 64 lowercase hex characters, the SHA-256
of the value's canonical encoding. The encoding is of the term itself, never of a
representation of it, so a value has one digest however it is written or read back.

The encoding is a version byte, then the term, each part tagged by its type and
self-delimiting:

| Term | Encoding |
|---|---|
| an atom (`nil`, `true` and `false` too) | `a`, its name's byte size, its name in UTF-8 |
| a binary | `b`, its byte size, its bytes |
| a bitstring of another size | `s`, its bit size, its bits padded with zeros to a byte |
| an integer | `i`, the byte size of its decimal spelling, that spelling |
| a float | `f`, its IEEE 754 64-bit form |
| a list | `l`, its length, each element |
| a tuple | `t`, its size, each element |
| a map | `m`, its size, each key's encoding then its value's, in the bytewise order of the keys' encodings |

Sizes are 64-bit, and every number is big-endian. So key order never matters, and
different terms have different encodings: `1` and `1.0`, `0.0` and `-0.0` (as under
`===`), `:a` and `"a"`, `{:a}` and `[:a]` all differ. A struct is its map, `__struct__`
and all, so a `DateTime` carries its zone and precision, and a struct differs from its
bare map.

The encoding is part of the kernel's contract: changing it changes every digest, so it
changes only with a new major version.

Raises `ArgumentError` for a term that is not data: a function, a PID, a reference, a
port or an improper list.

    iex> Macaw.Layers.Document.digest(%{b: 1, a: [true, nil]}) ==
    ...>   Macaw.Layers.Document.digest(%{a: [true, nil], b: 1})
    true

    iex> Macaw.Layers.Document.digest(%{v: 1}) == Macaw.Layers.Document.digest(%{v: 1.0})
    false

# `unknown_keys`

```elixir
@spec unknown_keys(map(), [term()], Macaw.Layers.Fault.path()) :: [
  Macaw.Layers.Fault.t()
]
```

The faults of `map`'s keys that are not among `known`, each at its own key under `path`:
`{path ++ [key], :unknown_key}`, ordered by path.

`known` is the type's keys as its value holds them, atoms in a value. A key the type
knows stays the type's to read; one it does not is refused by name, never dropped. A key
a reader kept at its own spelling, a string, is named as it was written.

    iex> Macaw.Layers.Document.unknown_keys(%{:title => "t", "ttle" => "x"}, [:title], [:page])
    [{[:page, "ttle"], :unknown_key}]

---

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