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

Where documents live (spec/03 §1): the store behavior, the assign's functions, typed ids
(§2) and import as a relocation (§3.3).

A store holds **entries**, `%{id, name, fragment}`, of the fragment types it was given
(spec/02 §1). An entry's kind is derived from its fragment, never stored; its name is an
attribute, never an identity. A store is reached through an **assign**, `{module,
context}` or a bare module, so every call carries who is acting and from where.

    Store.put({Macaw.Layers.Store.Memory, agent}, %{id: nil, name: "Styles:thick", fragment: %{style: %{width: 3}}})
    #=> {:ok, {:style, 1}}

Ported from visualize's `Visualize.Chart.Builder.Store`, its callbacks answering `{:ok, …}`
or `{:error, …}` as every Macaw contract does.

# `assign`

```elixir
@type assign() :: module() | {module(), term()}
```

A store's module and its context, or a bare module, whose context is `nil`.

# `entry`

```elixir
@type entry() :: %{id: id() | nil, name: String.t(), fragment: map()}
```

A stored fragment: its id, `nil` before the store issues one, and its name.

# `id`

```elixir
@type id() :: {atom(), pos_integer()}
```

An id: a kind of the store's types and a positive integer the store issued.

# `imported`

```elixir
@type imported() :: %{
  ids: %{required(id()) =&gt; id()},
  outside: [{id(), Macaw.Layers.Fault.path()}]
}
```

What an import answers: the `old => new` table, and each reference outside the bundle.

# `summary`

```elixir
@type summary() :: %{id: id(), name: String.t(), kind: atom()}
```

What a picker lists.

# `delete`

```elixir
@callback delete(id(), context :: term()) :: :ok | {:error, term()}
```

An entry removed.

# `get`

```elixir
@callback get(id(), context :: term()) :: {:ok, entry()} | {:error, :not_found | term()}
```

The entry of an id.

# `import`

```elixir
@callback import(Macaw.Layers.Bundle.t(), context :: term()) ::
  {:ok, imported()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

A bundle relocated into the store, whole or not at all (§3.3).

# `list`

```elixir
@callback list(context :: term()) :: {:ok, [summary()]} | {:error, term()}
```

Every entry's summary.

# `put`

```elixir
@callback put(entry(), context :: term()) :: {:ok, id()} | {:error, term()}
```

An entry stored: a new id issued to one without, a held one replaced.

# `types`

```elixir
@callback types(context :: term()) :: [module()]
```

The fragment types the store holds.

# `check_types!`

```elixir
@spec check_types!([module()]) :: [module()]
```

The types, checked when a store is configured: each a fragment type, and no kind
declared by two of them. Raises `ArgumentError` naming both types and the kind.

# `decode_id`

```elixir
@spec decode_id([atom()], term()) :: {:ok, id()} | :error
```

The id a string spells, its kind matched against `kinds` by its string, so no atom is
made from a client's string: `{:ok, id}` or `:error`.

    iex> Macaw.Layers.Store.decode_id([:style, :mark], "style:11")
    {:ok, {:style, 11}}
    iex> Macaw.Layers.Store.decode_id([:style, :mark], "widget:11")
    :error

# `delete`

```elixir
@spec delete(assign(), id()) :: :ok | {:error, term()}
```

An entry removed.

# `encode_id`

```elixir
@spec encode_id(id()) :: String.t()
```

An id as one string, for the DOM and the wire.

    iex> Macaw.Layers.Store.encode_id({:style, 11})
    "style:11"

# `get`

```elixir
@spec get(assign(), id()) :: {:ok, entry()} | {:error, term()}
```

The entry of an id.

# `group`

```elixir
@spec group(String.t()) :: {[String.t()], String.t()}
```

A name's filing, `<group>:<sub-group>:<name>`: the segments before the last, empty ones
dropped, and the last. Nothing references it.

    iex> Macaw.Layers.Store.group("Styles::thick")
    {["Styles"], "thick"}

# `import`

```elixir
@spec import(assign(), Macaw.Layers.Bundle.t()) ::
  {:ok, imported()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

A bundle imported.

# `kind`

```elixir
@spec kind(assign() | [module()], map()) :: {module(), atom()} | nil
```

`{type, kind}` of the first of the store's types that derives the fragment a kind
(spec/02 §6.1), or `nil`: a fragment about more than one thing is not storable.

# `kinds`

```elixir
@spec kinds(assign() | [module()]) :: [atom()]
```

Every kind of the store's types (an assign, or the types themselves), sorted.

# `list`

```elixir
@spec list(assign()) :: {:ok, [summary()]} | {:error, term()}
```

Every entry's summary.

# `put`

```elixir
@spec put(assign(), entry()) :: {:ok, id()} | {:error, term()}
```

An entry stored.

# `ref_kind`

```elixir
@spec ref_kind(id(), atom()) :: :ok | {:error, {:ref_kind, atom(), atom()}}
```

A reference held to the kind its site takes, before it is resolved (§2): `:ok`, or
`{:error, {:ref_kind, wanted, given}}` for an id of another kind, for a type's reader to
report at the site's path.

# `relocate`

```elixir
@spec relocate(assign(), Macaw.Layers.Bundle.t(), keyword()) ::
  {:ok, imported()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

A bundle imported through the store's own `put/2` (§3.3): every check first, then new
ids issued, then every reference rewritten through one table and each entry put again.

The checks: the bundle read again through its forms (`Macaw.Layers.Bundle`), so its
version and every fragment go through their readers, and the host's `check:` over each
entry. A fault anywhere writes nothing; every fault is reported at `[:entries, i | path]`.

- `check:` — `(entry -> :ok | {:error, [fault]})`, the host's own check, such as names
  its registry does not know;
- `transaction:` — a function given `run`, which it calls as `run.(state, put)` inside its
  own transaction, `put.(entry, state)` answering `{:ok, id, state}` or `{:error, reason}`;
  `run` answers `{:ok, imported, state}` or `{:error, faults}`, and the transaction
  function `{:ok, imported}` or `{:error, faults}`, committing only the first. Without it,
  entries are written through the store's `put/2`, which is whole only where those writes
  cannot fail.

A reference to an id outside the bundle is left as it is and reported in `outside` by the
entry that holds it: it resolves as a mask (spec/02 §7).

# `split`

```elixir
@spec split(assign()) :: {module(), term()}
```

The assign as `{module, context}`; a bare module has a `nil` context.

# `type_of`

```elixir
@spec type_of(assign() | [module()], atom()) :: module() | nil
```

The type a kind belongs to, or `nil`.

# `types`

```elixir
@spec types(assign()) :: [module()]
```

The store's types.

---

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