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

A document's map form and JSON form, exact inverses for valid values (spec/01 §6).

The **value** is the document as Elixir holds it: atom keys where the type has a key,
string names elsewhere, atoms, times. 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. For a valid value:

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

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}`.

# `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.

# `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()) ::
  {: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.

# `from_json`

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

The value a JSON document holds, read through every gate (`Macaw.Layers.Document.read/3`)
and then from its map form. `type` implements this behavior, `Macaw.Layers.Version` and
`Macaw.Layers.Document`.

# `from_map`

```elixir
@spec from_map(map(), module()) ::
  {: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; `{:ok, value}` or every fault.

# `to_json`

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

The value's JSON form.

# `to_map`

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

The value's map form: `{:ok, map}`, or every value and key without one, by path.

---

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