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

A parameter: the one shape for every typed option, a source's or a component's
(spec/01 §2.2).

    %{type: type, default: value, values: [value] | nil,
      legal_for: {slot, field, fact} | nil, bindable: false, doc: "…"}

A parameter's `type` is a data-model type (`:time`, `:number`, `:category`, `:text`,
`:color` or `:point`, spec/01 §1.2), `{:document, type}` for a nested document of a
document type, or `:term`, a value its owner validates itself, allowed only for
options migrated from untyped forms. `values` is a closed list of legal values;
`legal_for` says the legal values follow from a bound slot's described schema, which
the runtime that binds slots resolves (macaw_compose spec 05 §1.1); `bindable` says
whether the value may be bound to state, closed by default.

**A parameter with no `default` is required.** `validate/2` checks a map of given values
against a map of parameters: every fault at once, by name, a missing one filled from its
default.

macaw_data owns the shape (the owner's decision, 2026-10-27): sources take parameters
before any component exists, and macaw_compose's components use the same shape.

# `fault`

```elixir
@type fault() :: {list(), term()}
```

A fault: the path to the value, and why.

# `t`

```elixir
@type t() :: %{
  :type =&gt; type(),
  optional(:default) =&gt; term(),
  optional(:values) =&gt; [term()] | nil,
  optional(:legal_for) =&gt; {term(), term(), atom()} | nil,
  optional(:bindable) =&gt; boolean(),
  optional(:doc) =&gt; String.t()
}
```

A parameter.

# `type`

```elixir
@type type() ::
  :time
  | :number
  | :category
  | :text
  | :color
  | :point
  | {:document, atom()}
  | :term
```

A parameter's type.

# `check`

```elixir
@spec check(term()) :: :ok | {:error, [fault(), ...]}
```

`:ok`, or every fault of a parameter declaration, each `{path, reason}` from the
parameter: `[:type]` required and one of the types, `[:default]` and each of `[:values, i]`
a value of the type, the default among `values` when both are given, `[:legal_for]` a
`{slot, field, fact}`, `[:bindable]` a boolean, `[:doc]` a string, and no other key.

    iex> Macaw.Data.Parameter.check(%{type: :number, default: 5, doc: "bins"})
    :ok
    iex> Macaw.Data.Parameter.check(%{type: :category, values: ["a", "b"], default: "c"})
    {:error, [{[:default], :not_in_values}]}

# `conforms?`

```elixir
@spec conforms?(type(), term()) :: boolean()
```

Whether a value is one of a type: a `DateTime` for `:time`, a number for `:number`, a
string, an atom (other than `nil`, `true` and `false`) or a number for `:category`, a
string for `:text`, a data color for `:color` (`Macaw.Data.Schema.color?/1`), a
`{longitude, latitude}` pair for `:point`, a map for `{:document, type}`, anything for
`:term`. `nil` is no value of any type but `:term`.

# `keys`

```elixir
@spec keys() :: [atom(), ...]
```

The keys a parameter takes, `:type` first.

# `validate`

```elixir
@spec validate(%{required(String.t()) =&gt; t()}, map()) ::
  {:ok, map()} | {:error, [fault(), ...]}
```

The given values checked against a map of parameters by name (strings), every fault at
once by name: `{:ok, values}` with each missing parameter's default filled in, or
`{:error, faults}`. A name no parameter has is `{[name], :unknown_param}`; a missing
parameter with no default `{[name], :required}`; a value not of its type
`{[name], {:type, type}}`; one outside its `values` `{[name], {:enum, values}}`.

    iex> params = %{"bins" => %{type: :number, default: 10}, "agg" => %{type: :category, values: ["max", "mean"]}}
    iex> Macaw.Data.Parameter.validate(params, %{"agg" => "max"})
    {:ok, %{"agg" => "max", "bins" => 10}}
    iex> Macaw.Data.Parameter.validate(params, %{"agg" => "sum", "zoom" => 2})
    {:error, [{["agg"], {:enum, ["max", "mean"]}}, {["zoom"], :unknown_param}]}

---

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