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

One version line per document type, and stepwise migration (spec/01 §4).

A document type implements this behavior: its current version, and one pure migration
step per version. A document carries its version as the stamp `"$version"`, a string key
at the top of its map, in the map form and the JSON form alike. Two types' lines are
independent: a new step for one moves no other.

    defmodule MyApp.Layout do
      @behaviour Macaw.Layers.Version

      @impl true
      def current, do: 2

      # 1 -> 2: `cols` became `columns`.
      @impl true
      def step(%{"cols" => cols} = doc, 1),
        do: {:ok, doc |> Map.delete("cols") |> Map.put("columns", cols)}

      def step(doc, 1), do: {:ok, doc}
    end

    Macaw.Layers.Version.migrate(%{"$version" => 1, "cols" => 3}, MyApp.Layout)
    #=> {:ok, %{"$version" => 2, "columns" => 3}}

The generic part of visualize's `Visualize.Chart.Migration` (`current/0`, `readable/1`,
`migrate/1`), with the line per type rather than one shared by every type.

# `current`

```elixir
@callback current() :: pos_integer()
```

The type's current version: a positive integer, starting at 1.

# `step`

```elixir
@callback step(document :: map(), from :: pos_integer()) ::
  {:ok, map()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The document at `from` migrated to `from + 1`, purely: no I/O, no clock, no process.
The kernel writes the new stamp; a step need not.

# `check`

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

The document's version, read before anything else of it: `{:ok, version}`, the current
one when the document has no stamp, or the fault that refuses it.

A stamp above the type's current version is `{[], {:newer_schema, needs, this}}`, and a
stamp that is not a positive integer is `{["$version"], {:type, :pos_integer}}`.

# `migrate`

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

The document at the type's current version, carrying the current stamp.

A document with no stamp reads as current. An older one is migrated one step at a time,
each `c:step/2` in turn; a step's faults stop the migration and are returned. A newer or
malformed stamp is refused by `check/2` before any step runs.

# `stamp`

```elixir
@spec stamp() :: String.t()
```

The stamp's key, `"$version"`.

---

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