# `Macaw.Data.Operations`
[🔗](https://github.com/dcoai/macaw_data/blob/v0.5.3/lib/macaw/data/operations.ex#L1)

`macaw_data`'s own package of operations, the registry, and running them (spec/04).

**The package.** `__members__/0` is the table operations of spec/04 §4 and the
statistics of §5, each a module implementing `Macaw.Data.Transform` or
`Macaw.Data.Aggregate`.

**The registry** (`registry/1`) is built from an explicit list of packages, this one
always among them: default closed, and a module that is merely loaded is not reachable
(spec/04 §2).

**Running.** `validate/3` checks a list of nodes against the registry and the context,
answering every fault at once; `apply/4` runs checked nodes in order over a table;
`aggregate/4` reduces values by a named aggregate; `outputs/4` names the columns after
every node. Each fault is `{path, reason}` (spec/04 §3.1) and `format_error/1` words one.

    iex> alias Macaw.Data.Operations
    iex> registry = Operations.registry([Macaw.Data.Test.Ops])
    iex> {:ok, table} = Operations.apply(registry, [%{op: "double", field: "v"}], [%{v: 2}], %{})
    iex> Macaw.Data.Table.rows(table)
    [%{:v => 2, "double" => 4}]
    iex> Operations.validate(registry, [%{op: "doubel"}], %{})
    {:error, [{[0, "op"], {:unknown, :transform, "doubel"}}]}

# `fault`

```elixir
@type fault() :: {[non_neg_integer() | String.t()], term()}
```

A fault: the path (a node's index, then its key) and the reason (spec/04 §3.1).

# `__members__`

```elixir
@spec __members__() :: [module()]
```

The modules of `macaw_data`'s package: its table operations and statistics.

# `aggregate`

```elixir
@spec aggregate(Macaw.Data.Operations.Registry.t(), String.t() | atom()) ::
  {:ok, module()} | :error
```

The aggregate named `name`, by spelling: `{:ok, module}` or `:error`.

# `aggregate`

```elixir
@spec aggregate(
  Macaw.Data.Operations.Registry.t(),
  String.t() | atom(),
  Enumerable.t(),
  map()
) ::
  {:ok, term()} | {:error, [fault(), ...]}
```

The value of `values` by the aggregate named `name`, its `options` checked against its
declaration and every `nil` reading dropped: `{:ok, value}` or `{:error, faults}`, each
fault at its option's key (`[]` for the name itself).

# `aggregates`

```elixir
@spec aggregates(Macaw.Data.Operations.Registry.t()) :: [String.t()]
```

Every aggregate's name, sorted.

# `apply`

```elixir
@spec apply(
  Macaw.Data.Operations.Registry.t(),
  [map()],
  Macaw.Data.Table.source() | Macaw.Data.Table.t(),
  map()
) :: {:ok, Macaw.Data.Table.t()} | {:error, [fault(), ...]}
```

The nodes run in order over `source`, a `Macaw.Data.Table` or anything
`Macaw.Data.Table.from/2` reads, each step's table the next one's input:
`{:ok, table}`, or `validate/3`'s `{:error, faults}` before any step runs. A step whose
reading is wrong only at run time raises `ArgumentError` naming the step (spec/04 §3.1).

# `apply!`

```elixir
@spec apply!(
  Macaw.Data.Operations.Registry.t(),
  [map()],
  Macaw.Data.Table.source() | Macaw.Data.Table.t(),
  map()
) :: Macaw.Data.Table.t()
```

As `apply/4`, raising `ArgumentError` naming every fault.

# `columns`

```elixir
@spec columns(Macaw.Data.Table.t()) :: Macaw.Data.Transform.columns()
```

A table's columns in order, each with its type: what `outputs/2` is given.

# `digest`

```elixir
@spec digest(Macaw.Data.Operations.Registry.t()) :: String.t()
```

A digest of every member's name and module, which a compile cache keys by.

# `format_error`

```elixir
@spec format_error(fault()) :: String.t()
```

One fault as a sentence.

    iex> Macaw.Data.Operations.format_error({[1, "order"], {:type, {:enum, [:ascending, :descending]}}})
    "step 2, order: is one of ascending or descending"
    iex> Macaw.Data.Operations.format_error({[0, "op"], {:unknown, :transform, "sankey"}})
    "step 1, op: transform \"sankey\" is not available"

# `outputs`

```elixir
@spec outputs(
  Macaw.Data.Operations.Registry.t(),
  [map()],
  Macaw.Data.Transform.columns(),
  map()
) ::
  {:ok, Macaw.Data.Transform.columns()} | {:error, [fault(), ...]}
```

The columns after every node, by each transform's `outputs/2`, over input `columns`
(`[{name, type}]`): `{:ok, columns}` or `validate/3`'s `{:error, faults}`.

# `packages`

```elixir
@spec packages(Macaw.Data.Operations.Registry.t()) :: [module()]
```

The registry's packages, this one first.

# `registry`

```elixir
@spec registry([module()]) :: Macaw.Data.Operations.Registry.t()
```

The registry of this package and `packages`, in order, each once (spec/04 §2). Raises
`ArgumentError` for a module that is not a package, a member implementing no behavior,
a name two members of one behavior share, and an aggregate declaring a step's key.

# `same?`

```elixir
@spec same?(term(), term()) :: boolean()
```

Whether two readings are one value: equal, or an atom and a string spelling it, so a
category is one value whichever spelling a row or a node gives it (spec/04 §4.1).

    iex> Macaw.Data.Operations.same?(:ok, "ok")
    true
    iex> Macaw.Data.Operations.same?(1, 1.0)
    true
    iex> Macaw.Data.Operations.same?(nil, "nil")
    false

# `step_keys`

```elixir
@spec step_keys() :: [String.t()]
```

The keys of the steps that name an aggregate, which an aggregate may not declare.

# `table`

```elixir
@spec table([map()], Macaw.Data.Transform.columns()) :: Macaw.Data.Table.t()
```

A table of `rows` with `columns`, in their order and of their types, each read from a
row by either spelling (`Macaw.Data.Table.get/2`): how a member that works on rows
answers the table its `outputs/2` names.

# `transform`

```elixir
@spec transform(Macaw.Data.Operations.Registry.t(), String.t() | atom()) ::
  {:ok, module()} | :error
```

The transform named `name`, by spelling: `{:ok, module}` or `:error`.

# `transforms`

```elixir
@spec transforms(Macaw.Data.Operations.Registry.t()) :: [String.t()]
```

Every transform's name, sorted.

# `validate`

```elixir
@spec validate(Macaw.Data.Operations.Registry.t(), [map()], map()) ::
  {:ok, [map()]} | {:error, [fault(), ...]}
```

Every node checked (spec/04 §3.1): `{:ok, checked}`, each node's options by their
strings with defaults filled in, enum values their atoms, a named aggregate its module
and a frame-bound `"plot"` the frame's width in whole pixels; or `{:error, faults}`,
every fault of every node, ordered by path.

---

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