# `Narabe.Rules`
[🔗](https://github.com/hattoriai/narabe/blob/v0.1.0/lib/narabe/rules.ex#L1)

What may go where: the same rules in the browser, as people drag (a drop
that breaks them is not offered), and on the server, as `Narabe.Ecto`
applies a move (one that breaks them is refused).

    rules = Narabe.Rules.new(nest: true, max_depth: 4, leaves: ["file"])

Options:

  * `:nest` - whether an item may hold others (a tree). A list's and a
    board's items do not nest (`false`, the default for them).
  * `:max_depth` - the deepest level an item may be at, the top level
    being 1. A moved item's own children count: a page with two levels
    under it cannot go to the deepest level.
  * `:leaves` - the kinds of items that hold nothing (`["file"]`).
  * `:accepts` - which kinds of items a kind of parent takes:
    `%{"section" => ["page"], "root" => ["section"]}`. `"root"` is the
    top level; a board's columns are parents of their kind (`"column"`).
    A kind with no entry takes anything.

An item's kind is its `kind` (`data-kind` in the browser, the `:kind`
field of `Narabe.Ecto`'s schema); without one it is `"item"`.

The browser and the server give the same answers, held by
`test/fixtures/rules.json`.

# `move`

```elixir
@type move() :: %{
  kinds: [String.t()],
  parent_kind: String.t() | nil,
  parent_depth: non_neg_integer(),
  height: pos_integer(),
  into_itself: boolean()
}
```

A move, as the rules see it: the kinds of the items, the kind of their new
parent (`nil` at the top level) and its depth (0 for the top level or a
board's column), the height of the tallest item's subtree (1 for an item
with no children), and whether the parent is one of the items or under
one.

# `reason`

```elixir
@type reason() :: :into_itself | :not_accepted | :too_deep
```

Why a move is refused.

# `t`

```elixir
@type t() :: %Narabe.Rules{
  accepts: %{required(String.t()) =&gt; [String.t()]} | nil,
  leaves: [String.t()],
  max_depth: pos_integer() | nil,
  nest: boolean()
}
```

# `check`

```elixir
@spec check(t(), move()) :: :ok | {:error, reason()}
```

Checks a move: `:ok`, or `{:error, reason}`.

    iex> rules = Narabe.Rules.new(nest: true, max_depth: 2)
    iex> Narabe.Rules.check(rules, %{kinds: ["page"], parent_kind: "page", parent_depth: 1, height: 1, into_itself: false})
    :ok
    iex> Narabe.Rules.check(rules, %{kinds: ["page"], parent_kind: "page", parent_depth: 1, height: 2, into_itself: false})
    {:error, :too_deep}

# `new`

```elixir
@spec new(t() | keyword() | module() | nil) :: t()
```

Rules from options (see the moduledoc), the rules themselves, or those
of a schema with `use Narabe.Ordered` (so that a component and
`Narabe.Ecto` have the same: `<.tree rules={Page} ...>`).

    iex> Narabe.Rules.new(nest: true, max_depth: 3)
    %Narabe.Rules{nest: true, max_depth: 3, leaves: [], accepts: nil}

# `to_client`

```elixir
@spec to_client(t()) :: map()
```

The rules as the browser takes them (`data-config`), with keys in camel
case and nothing that is a default.

    iex> Narabe.Rules.to_client(Narabe.Rules.new(nest: true, max_depth: 3))
    %{"nest" => true, "maxDepth" => 3}

---

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