Validates a design map against the schema (spec/14 §10).
validate/1 walks the map with Visualize.Chart.Schema and returns every fault it
finds as {path, reason}: the path is the list of keys and indexes from the design to
the faulty value, [:marks, 1, :channels, :y], and the reason one of the terms of
spec/14 §10.1. References — scales, styles, sources, variables, fields, theme slots —
resolve against the design itself, so a design is self-contained or it does not
validate. format/1 renders an error as a line a person reads.
Examples
iex> Visualize.Chart.Validator.validate(%{version: 2})
{:error, [{[:frames], :required}]}
iex> Visualize.Chart.Validator.format({[:marks, 1, :channels, :y], :required})
"marks[1].channels.y: is required"
Summary
Types
Keys and list indexes from the design to a value; [] is the design.
Why a value is at fault (spec/14 §10.1).
Functions
One error as path: reason in words; the path in marks[1].channels.y form.
A path as a person reads it — design for the root, else frame.scales.x,
marks[0].channels — the one spelling every error, node label and rendered data-node
uses (spec/14 §4.7, §18.5).
:ok, or every fault of the design ordered by path.
:ok, or every fault of a fragment read as a kind — per-kind validity (spec/14 §19.8).
Types
@type path() :: [atom() | non_neg_integer()]
Keys and list indexes from the design to a value; [] is the design.
@type reason() :: :required | :unknown_key | {:type, Visualize.Chart.Schema.type()} | {:enum, [atom()]} | {:undeclared, :scale | :style | :source | :var | :field | :slot | :paint, term()} | {:ref_kind, atom(), atom()} | {:invalid_for, atom()} | {:unsupported_version, term()} | {:json, String.t()} | {:missing_dependency, :jason} | {:unresolved, :var, atom()} | {:var_binding, atom()} | {:unbound, :source, atom()} | {:missing_field, atom()} | :conflict | :cycle | {:cycle, [term()]} | {:template, :unterminated | {:hole, String.t()}} | :many_kinds | :sweep | :radius | {:zone, String.t(), Visualize.Scale.Time.zone_error()} | {:sync, :frame_kind | :x_scale, atom() | nil} | {:tiles, :projection, atom()} | {:tiles, :attribution | :url | :subdomains} | {:outside_domain, atom(), number() | {:column, atom(), :number | :text | :time}} | {:feeds_domain, atom(), number()}
Why a value is at fault (spec/14 §10.1).
Functions
One error as path: reason in words; the path in marks[1].channels.y form.
A path as a person reads it — design for the root, else frame.scales.x,
marks[0].channels — the one spelling every error, node label and rendered data-node
uses (spec/14 §4.7, §18.5).
iex> Visualize.Chart.Validator.format_path([:marks, 0, :channels])
"marks[0].channels"
iex> Visualize.Chart.Validator.format_path([])
"design"
:ok, or every fault of the design ordered by path.
:ok, or every fault of a fragment read as a kind — per-kind validity (spec/14 §19.8).
A design is validated whole. A composite must be %{composite: %{uses, vars}} and nothing
else, its uses sites. Any other kind is the singular body under its key — %{style: %{…}},
%{frames: %{main: %{…}}} — walked with that kind's schema and no design around it, so a reference
to a scale, style, source, variable or field the fragment does not declare is not a fault:
a fragment is a piece, and what it refers to is bound where it is composed. A fragment
whose one key is not the kind's is refused as {:type, {:node, kind}}.