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

The JSON adapter (spec/01 §5, §6): a document's map form and JSON form, and reading a JSON
document through the gates to its value.

The **value** is the document as Elixir holds it: atom keys where the type has a key,
string names elsewhere, atoms, times. It is the one true form: the type judges it
(`Macaw.Layers.Document.check/2`), and it is what is built, stored and compared. Its
**map form** is JSON-ready: string keys, and a tag (`Macaw.Layers.Tags`) for every value
JSON can't express. Its **JSON form** is that map encoded. Both are representations, made
here at the edge.

**The codec**, `to_map/2` and `from_map/2`, converts between the value and the map form,
exact inverses with no gates and no validation. For a value with a form:

    from_map(to_map(value)) == {:ok, value}
    to_map(from_map(map))   == {:ok, map}    # for a map to_map/2 wrote

**The adapter** reads an encoding that comes from elsewhere: `gates/3` takes a JSON
document, or its decoded map form, through the gates (the bytes, the scan, the version,
the migration, the recount) to the map form at the type's current version, and `read/3`
then reads the value from it and judges the value by its type. `to_json/2` writes the
value's JSON form, stamped with the type's version. A value with no form fails here, at
its path, never where it was built.

A document type implements this behavior: its tag table, the keys it reads as atoms, and
the atoms a `$atom` may spell, each a closed table (`Macaw.Layers.Vocabulary`), so no
atom is ever made from a document. A type with values of its own (a struct, a tuple)
writes and reads them through `c:encode_value/1` and `c:decode_value/2`, under its own
live tags.

The kernel's terms:

| Value | Map form |
|---|---|
| `nil`, a boolean, a number, a string | as it is |
| another atom | `{"$atom": "spelling"}`, read back through `c:atoms/0` |
| a `DateTime` in `Etc/UTC` | `{"$time": "ISO 8601"}` |
| a list | a list |
| a map | an object: an atom key by its spelling, a string key as it is, escaped when it starts with `$` |

A function, a PID, a reference, a port, a tuple or a struct the type does not write has
no form: it is refused at its path as `{:type, :term}`.

**A name map** is a map whose keys are names, never the type's keys (a fragment type's
`:union` and `:deep` keys hold one, spec/02 §9). A type says where its name maps are by
`c:name_map?/1`; at one, a string key is written and read as the name it is, escaped
when it starts with `$`, whatever its spelling, and an atom key is `{:type, :name}`,
since names are strings. Every other map is a node, keyed as the table says. A type
without `c:name_map?/1` has no name maps. Below a tag, a map's place is its place in
the payload: `{:tag, tag}` and then the path within the payload, which a type writing a
payload part that sits at the type's positions (a use site's local fragment) gives
`encode_term/3` as `at`.

# `option`

```elixir
@type option() :: {:limits, keyword() | map()} | {:vocabulary, vocabulary()}
```

`gates/3`'s and `read/3`'s options; the codec takes `vocabulary:`.

# `vocabulary`

```elixir
@type vocabulary() :: %{optional(:keys) =&gt; [atom()], optional(:atoms) =&gt; [atom()]}
```

Words a reader takes beyond the type's own tables, merged over them: `keys`, read as
atom keys, and `atoms`, the atoms a `$atom` may spell. Lists of atoms, given by code
(an adopter's registered words), never by a document.

# `atoms`

```elixir
@callback atoms() :: Macaw.Layers.Vocabulary.table()
```

The atoms a `$atom` may spell in this type's documents.

# `decode_value`
*optional* 

```elixir
@callback decode_value(tag :: String.t(), payload :: term()) :: {:ok, term()} | :default
```

The value of one of the type's own live tags, its payload already read, or `:default`
when the tag or payload is not one it reads.

# `encode_value`
*optional* 

```elixir
@callback encode_value(term()) :: {:ok, term()} | :default
```

A value of the type's own, in its map form, or `:default` for the kernel's rules.

# `keys`

```elixir
@callback keys() :: Macaw.Layers.Vocabulary.table()
```

The keys the type reads as atoms, by spelling.

# `name_map?`
*optional* 

```elixir
@callback name_map?(path :: [term()]) :: boolean()
```

Whether the map at `path` is a name map: its keys are names, never the type's keys.
`path` is from the document's root, as the value holds it: atom keys, string names and
list indexes. Below a tag it is `{:tag, tag}` followed by the path within the tag's
payload, as it is read. A fragment type answers with `Macaw.Layers.Fragment.name_map?/2`.

# `tags`

```elixir
@callback tags() :: module()
```

The type's tag table: a module that uses `Macaw.Layers.Tags`.

# `encode_term`

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

One term's map form, for a type writing its own tag's payload (spec/01 §6): `{:ok, form}`,
or every value and key without one, by path from the term.

`at` is the term's place, as `c:name_map?/1` is asked it below a tag: `{:tag, tag}` and
the path of the term within the payload (`[{:tag, "$use"}, "local"]`), so its maps are
written as the reader reads them there. `nil`, the default, is no place: every map in
the term is a node.

# `from_map`

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

The value a map form holds: its tags held to the type's table, its keys read against the
type's keys, its `$atom`s against the type's atoms, each with the `vocabulary:` given
(see `read/3`); `{:ok, value}` or every fault.

# `gates`

```elixir
@spec gates(binary() | map(), module(), [option()]) ::
  {:ok, map()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

A document from elsewhere through the gates, in order (spec/01 §5): `{:ok, map}`, its
map form at the type's current version, stamp included, or every fault of the first gate
that refuses it.

1. **bytes**: a JSON document longer than the `bytes` limit is refused;
2. **decode** with the `depth` and `items` limits, by `Macaw.Layers.Scan`, which stops at
   a limit before any value is built; only then is it decoded, with Elixir's `JSON`, and
   its root must be an object. A map form given directly skips the bytes and is walked
   for `depth` and `items` instead (`Macaw.Layers.Limits.structure/2`);
3. **version**: a newer stamp is refused by name before anything else is read;
4. **migrate**, step by step;
5. **recount** `depth` and `items`, since a migration can grow a document.

`type` implements `Macaw.Layers.Version`. `limits:` tightens the limits, or raises one by
name, as `Macaw.Layers.Limits.new/1` takes them. Nothing in the document is evaluated,
and its value is not judged: that is `read/3`.

# `read`

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

The value a document from elsewhere holds: through `gates/3`, then read from its map form
(`from_map/2`, the stamp dropped), then judged by its type
(`Macaw.Layers.Document.check/2`); `{:ok, value}`, or the faults of the first step that
refuses it.

`input` is a JSON document, a binary, or its decoded map form. `type` implements this
behavior, `Macaw.Layers.Version` and `Macaw.Layers.Document`. `limits:` as `gates/3`
takes them.

**The vocabulary is the reader's.** The words a document may spell as atoms are the
type's tables (`c:keys/0`, `c:atoms/0`) and, with `vocabulary: %{keys: [...], atoms:
[...]}`, the words given there, merged over them: an adopter's registered words, given
by code. No atom is made from the document beyond those tables. A key outside them is
read as its string, and a `$atom` outside them is `{:enum, atoms}` at its path.

# `to_json`

```elixir
@spec to_json(map(), module(), [option()]) ::
  {:ok, binary()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The value's JSON form, stamped `"$version"` with the type's current version where the
type has a version line (`c:Macaw.Layers.Version.current/0`): `{:ok, json}`, or every
value and key without a form, by path. The value is not judged here: that is
`Macaw.Layers.Document.check/2`. `vocabulary:` as `to_map/3` takes it.

# `to_map`

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

The value's map form: `{:ok, map}`, or every value and key without one, by path.
`vocabulary:` the words the type's keys are taken with (see `read/3`).

---

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