Checks a value's fields against a Dextrin.Schema.Compiled schema
(required fields and declared types) — shared by decode (resolve/3,
whose result Dextrin.Schema.Reader builds a value from) and encode-time validation
(validate_for_encode/3/validate_tree_for_encode/2, which never
transforms value). Both directions reuse the exact same
resolve_fields/3/TypeExpr.matches?/3 field-checking — encode's
own values are wrapped in the same internal Dextrin.Schema. Validated marker decode already uses, via wrap_and_check/2, so
there's one type-checking implementation, not two that could drift.
Enforcement is decode-time and fail-fast, with no lenient escape hatch to get the materialized value anyway — a violation is always an error, on the same channel an ordinary syntax error already uses.
Every violation is a Dextrin.Error carrying its DXN.md §5
category -- schema/field-count, schema/missing-field,
schema/type-mismatch, schema/shadowed-field -- which the text and
binary decoders and the encoder pass on unchanged, so a violation is
the same error whichever channel it travelled on.
Summary
Functions
fields checked against compiled: every declared field, in the
schema's own order, as {name, value} pairs keyed by string.
Checks value — ordinary Elixir data you're about to encode —
against compiled. Never materializes; value itself is never
transformed.
Walks value looking for every Dextrin.Struct or registered
application struct anywhere in the tree and checks each one against
its own schema, if the registry has one — regardless of what any
enclosing field's declared type is, and recursing fully into
whatever it finds (not just confirming type identity).
Dextrin.encode/2/encode_binary/2 call this automatically (opt
out with validate: false).
Types
Functions
@spec resolve(Dextrin.Schema.Compiled.t(), fields(), Dextrin.Registry.t()) :: {:ok, [{String.t(), term()}]} | {:error, Dextrin.Error.t()}
fields checked against compiled: every declared field, in the
schema's own order, as {name, value} pairs keyed by string.
A required field that is missing is an error, and so is a present
field whose value does not match its declared type. An optional field
that is missing takes its default, or stays absent when it has none --
DXN tells an absent field from one written nil. Nested values have
already been read, bottom-up; their internal Validated markers are
stripped here, so nothing past this point sees one.
Which schema the fields are read with, and what is built from them,
is Dextrin.Schema.Reader's business.
@spec validate_for_encode(Dextrin.Schema.Compiled.t(), term(), Dextrin.Registry.t()) :: :ok | {:error, Dextrin.Error.t()}
Checks value — ordinary Elixir data you're about to encode —
against compiled. Never materializes; value itself is never
transformed.
@spec validate_tree_for_encode(term(), Dextrin.Registry.t()) :: :ok | {:error, Dextrin.Error.t()}
Walks value looking for every Dextrin.Struct or registered
application struct anywhere in the tree and checks each one against
its own schema, if the registry has one — regardless of what any
enclosing field's declared type is, and recursing fully into
whatever it finds (not just confirming type identity).
Dextrin.encode/2/encode_binary/2 call this automatically (opt
out with validate: false).