Expand description
Schema-directed deserialization: make a freshly-read RawNode conform
to a Schema, or report every reason it doesn’t.
Ported from ~/dev/omnist/omnist/deserialize.py (issue #14). Readers
hand back text-shaped values: JSON/YAML/TOML have no date/time
type, so a temporal field arrives as an ISO-8601 string; a whole-number
float may need to become an int (or vice versa) to match what the
schema declares. materialize walks the node together with the
schema, upgrading each leaf only when the conversion is value-exact
(1.0 -> 1 for an integer field, 1 -> 1.0 for a number field –
see the module’s scalar-upgrade table below) and checking every
record’s shape (closed fields, cardinality) in the same pass – not a
second top-down walk delegating to crate::schema::Schema::validate
afterward. That would mean re-walking the same tree twice with
different traversal code for no reason: materialize already knows, at
every node, exactly which field/type the schema expects there, so
upgrading and shape-checking happen together in one pass, matching the
Python reference’s stated rationale.
§No native temporal Scalar variant
crate::document::Scalar has no date/time/datetime variant (see
document.rs’s module doc) – every value this port ever materializes
is Null/Bool/Int/Float/Str. So unlike the Python reference
(which actually constructs a datetime.date/time/datetime object),
“upgrading” a temporal field here can only ever mean “is this string
shaped like, and a semantically valid, ISO date/time/datetime” – it
stays a Str either way. That check is exactly
crate::schema::is_iso_date/is_iso_time/is_iso_datetime, reused here rather
than reimplemented – the exact “validate and materialize must share
one strict parser” pitfall the porting playbook calls out, and the same
functions crate::schema::matches_kind already uses.
§Scalar upgrade table (value-exact only)
| field kind | accepts as-is | upgrades |
|---|---|---|
string | Str | (none) |
boolean | Bool | (none) |
integer | Int | Float with a zero fractional part |
number | Float | Int (always promoted to Float, matching the Python reference’s float(value)) |
date/time/datetime | Str shaped per the shared temporal check | (none – see above) |
null is accepted only when the field’s Scalar is nullable,
regardless of kind.
§No strict= switch
There’s no separate strict/non-strict mode: materialize takes
schema: Option<&Schema> – None is the well-defined “opt out of
validation entirely” case (the node is returned exactly as read,
untouched), matching the Python reference’s schema=None convention at
the reader call sites.
§The any type
An Any-typed field passes its node through completely untouched –
no shape check, no scalar upgrade – mirroring Python’s
_materialize_type: if isinstance(d, AnyType): return node.
Functions§
- materialize
- A copy of
nodewith leaf values upgraded to matchschema, guaranteed to conform to it – or every reason it can’t, collected into oneMaterializeError(never just the first problem found).