pub trait JsonMapObjectEntryExt {
// Required method
fn object_slot_mut_or(
&mut self,
slot: &'static str,
) -> Result<&mut Map<String, Value>>;
}Expand description
Substrate extension trait over serde_json::Map<String, Value> —
the ONE substrate owner of the .entry(<key>).or_insert_with(|| Value::Object(<empty>)) seed-then-guard shape every JSON-mutating
helper hand-authored at the “walk into this object slot on the
parent map, seeding an empty object if the slot is absent, or fail
loud if the slot exists but is a non-object” boundary.
Peer of ValueObjectExt::as_object_mut_or and
JsonMapStrExt::insert_str on the JSON-mutation axis; split by
SHAPE + SITE. ValueObjectExt::as_object_mut_or owns the “guard
a Value handle into its object interior” step at ONE level;
JsonMapStrExt::insert_str owns the “stamp a Value::String at
a string-typed key” write shape; this trait owns the compound
“get-or-seed the object at a slot, then guard” step every SSA-time
re-injection walks when the caller intends to reach a nested
object slot without asserting whether the parent has already
populated it (a caller composing a fresh resource-body carries
no metadata / metadata.annotations slot pre-seed; a caller
composing atop a pre-populated resource does — both paths reach
the same primitive).
Pre-lift the compound shape was hand-authored at TWO adjacent
private helpers in tatara-reconciler::ssapply past the ★★
PRIME-DIRECTIVE ≥ 2 duplication threshold, both walking the SAME
3-step let X = <map>.entry(<slot>).or_insert_with(|| Value::Object (<empty>)); X.as_object_mut_or(<slot>)? incantation:
metadata_object_mut(resource)— themetadataslot seed-then- guard step at the root of every SSA-time re-injection walk (inject_owner_reference+inject_annotationsreach it).inject_annotations(resource, process)— theannotationsslot seed-then-guard step nested one level deeper under themetadataobject the primitive above returned.
Both restated the SAME 3-line shape verbatim: .entry(<slot>) on
a Map<String, Value> handle known to be an object, then
.or_insert_with(|| Value::Object(<empty>)) to synthesize an
empty object at the slot when absent, then a .as_object_mut_or (<slot>)? guard on the returned &mut Value to fail loud when
the existing slot is a non-object. TWO byte-for-byte identical
blocks past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold,
differing only in the &'static str slot name each callsite
stamped ("metadata" / "annotations") — and the slot name is
used at BOTH the entry key AND the guard error message so a
regression that drifted the two apart at one callsite (a typo
stamping "metadata" into the entry key + "metadatas" into
the error message) would silently pass one pin and fail the
other. Post-lift each callsite reads <map>.object_slot_mut_or (<slot>)? and the compound shape lives at ONE substrate owner
here — the slot name is stamped ONCE per call and reaches both
the entry key and the guard error slot mechanically.
§Composability
- Slot name is
&'static str— pre-lift both callsites stamped&'static strliterals ("metadata"/"annotations"); a dynamic-slot caller (a callsite that reached this primitive with aStringkey computed at runtime) has no pre-lift precedent in the ssapply/render axis, so the&'static strbound stays honest to the pre-lift shape. A future caller needing a runtime slot name can widen this toimpl Into<String>at the substrate; the pre-lift consumers inherit it mechanically. - Returns
anyhow::Result<&mut Map<String, Value>>— matches the siblingValueObjectExt::as_object_mut_orshape so the downstream.entry(...).or_insert_with(...)/.insert(...)mutation threads through?onto the caller’sResult<_, anyhow::Error>return exactly as pre-lift. - Ok-arm returns the SAME
&mut Map<String, Value>the pre-lift.as_object_mut_or(<slot>)step returned — no clone, no key- order reshape, no synthesis.
§Naming — object_slot_mut_or, not entry_object or
get_or_insert_object_mut
Same discipline as the two sibling traits above — the trait method
deliberately does NOT collide with the inherent Map::entry /
Map::get_mut / Map::insert methods (any of which a caller who
has this trait in scope could resolve to by accident, silently
dropping the type-guard step). The _or suffix names the intent
(guard the Option → Result step at the same call, matching the
pre-lift .as_object_mut_or(<slot>)? guard); object_slot_mut
names the target shape (return an &mut object-typed Map at
the slot). Together they read as “guard the slot into a mutable
object interior or fail loud”, matching the pre-lift semantics
exactly.
§#[must_use]
Every consumer threads the ? short-circuit onto its handler’s
Result<_, anyhow::Error> return — dropping the guard swallows
the underlying type-mismatch entirely, which is never the intended
semantic at either pre-lift consumer (each downstream
.entry(...).or_insert_with(...) / .insert(...) mutation
depends on the returned &mut Map reference).
Theory anchor: THEORY.md §VI.1 (generation over composition — the
3-line .entry(<slot>).or_insert_with(|| Value::Object(<empty>)) .as_object_mut_or(<slot>)? compound shape recurred at two
hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
trigger, and is lifted to ONE substrate owner here). THEORY.md
§II.1 invariant 5 (composition preserves proofs — a regression
that drifted the entry-key slot vs. the guard-error slot at ONE
site would silently pass one downstream pin and fail the other;
post-lift the primitive stamps the slot ONCE per call so the
substrate itself owns the entry-key ↔ guard-error name coherence).
Required Methods§
Sourcefn object_slot_mut_or(
&mut self,
slot: &'static str,
) -> Result<&mut Map<String, Value>>
fn object_slot_mut_or( &mut self, slot: &'static str, ) -> Result<&mut Map<String, Value>>
Get-or-seed the object at slot in this JSON map, then guard
that the resulting handle is an object; returns
&mut Map<String, Value> on the object arm, and an
anyhow::Error whose Display reads
"<slot> is not an object" on the non-object arm (byte-
identical to the pre-lift .as_object_mut_or(<slot>)? guard,
sourced from the sibling ValueObjectExt::as_object_mut_or).
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".