Fix M1, M2, L1, L3 from code review #002

Four of seven review findings resolved. 5 new tests (391 -> 396 crate
tests; 438 -> 443 total). cargo test, clippy, wasm32 all green.

M2 (data_access.rs): write_bytes now validates data_len fits in u32
before the length-prefix cast. A >4GiB blob returns Access error
instead of silently writing a truncated length prefix (silent data
corruption on read-back).

M1 (builder.rs): Definitions::merge_into rewritten to access self.defs
directly instead of round-tripping through self.build() with a double-
cloned/unwrap_or_default chain that could silently drop definitions
on a shape mismatch. 4 new tests: insert-when-absent, merge-into-
existing, overwrite-duplicate-keys, no-op-on-non-object-top.

L1 (materialize.rs): byte-offset discriminator arm of
materialize_union_packed now uses checked_add for offset+disc_offset
and disc_abs_offset+disc_size, returning Access error on overflow.
Mirrors the existing sequential_reader.rs::read_union_value pattern.

L3 (error.rs): AlkTypeError::source() now returns Some(inner) for the
Validation variant (jsonschema::ValidationError implements
std::error::Error). Existing source_returns_none_for_all_variants
test split into source_returns_none_for_schema_offset_access and
source_returns_some_for_validation_variant.

Deferred: L2 (unreachable! -> Err, defense-in-depth), N1 (non-strict
RFC 3339 validator, docs-only), N2 (FieldValue::Bytes for Record,
API asymmetry). Review doc updated with resolution section.
This commit is contained in:
glm-5.2 committed 2026-08-11 08:41:15 +00:00
1 parent ee6e773123
commit a975befdd1
5 files changed
+187 -15

No files matched your search

+70 -6
View File
@@ -489,14 +489,15 @@ impl Definitions {
/// Merge the `$defs` into a top-level schema `Value`. Convenience
/// for the common case of building a schema that references its
/// definitions.
/// definitions. If `top` already has a `$defs` object, the definitions
/// are merged into it (existing keys with the same name are
/// overwritten); otherwise a `$defs` key is inserted.
pub fn merge_into(self, top: &mut Value) {
if let Some(obj) = top.as_object_mut() {
if let Some(defs) = self.build().as_object() {
if let Some(existing) = obj.get_mut("$defs").and_then(Value::as_object_mut) {
existing.extend(defs.get("$defs").cloned().unwrap_or_default().as_object().cloned().unwrap_or_default());
} else {
obj.insert("$defs".to_string(), Value::Object(defs.get("$defs").cloned().unwrap_or_default().as_object().cloned().unwrap_or_default()));
match obj.get_mut("$defs").and_then(Value::as_object_mut) {
Some(existing) => existing.extend(self.defs),
None => {
obj.insert("$defs".to_string(), Value::Object(self.defs));
}
}
}
@@ -649,6 +650,69 @@ mod tests {
);
}
#[test]
fn builder_merge_into_inserts_defs_when_absent() {
let mut defs = Definitions::new();
defs.define("Read", Schema::struct_().field("id", Schema::uint32()));
let mut top = Schema::struct_().field("payload", Schema::ref_def("Read")).build();
defs.merge_into(&mut top);
assert!(top["$defs"].is_object(), "$defs should be inserted");
assert_eq!(
top["$defs"]["Read"],
json!({"AlkType:Struct": true, "properties": {"id": {"AlkType:Uint32": true}}})
);
// The top-level schema's own keys are preserved.
assert_eq!(top["AlkType:Struct"], json!(true));
}
#[test]
fn builder_merge_into_merges_into_existing_defs() {
// Top already has a $defs with "Write"; merging in "Read" should
// add "Read" and keep "Write".
let mut top = Schema::struct_()
.field("payload", Schema::ref_def("Read"))
.build();
top["$defs"] = json!({
"Write": {"AlkType:Struct": true, "properties": {"n": {"AlkType:Uint16": true}}}
});
let mut defs = Definitions::new();
defs.define("Read", Schema::struct_().field("id", Schema::uint32()));
defs.merge_into(&mut top);
assert_eq!(
top["$defs"]["Write"],
json!({"AlkType:Struct": true, "properties": {"n": {"AlkType:Uint16": true}}}),
"existing Write def should be preserved"
);
assert_eq!(
top["$defs"]["Read"],
json!({"AlkType:Struct": true, "properties": {"id": {"AlkType:Uint32": true}}}),
"new Read def should be merged in"
);
}
#[test]
fn builder_merge_into_overwrites_duplicate_keys() {
// If both top and defs define the same key, the merged value wins.
let mut top = json!({
"AlkType:Struct": true,
"$defs": {"X": {"old": true}}
});
let mut defs = Definitions::new();
defs.define_value("X", json!({"new": true}));
defs.merge_into(&mut top);
assert_eq!(top["$defs"]["X"], json!({"new": true}));
}
#[test]
fn builder_merge_into_is_noop_for_non_object_top() {
// If top is not an object, merge_into silently does nothing.
let mut top = json!("not an object");
let mut defs = Definitions::new();
defs.define("Read", Schema::uint32());
defs.merge_into(&mut top);
assert_eq!(top, json!("not an object"), "non-object top should be untouched");
}
#[test]
fn builder_ref_def_produces_json_pointer() {
assert_eq!(