-
-
Notifications
You must be signed in to change notification settings - Fork 91
Open
Labels
⚙️ corefacet-core crate, core types and traitsfacet-core crate, core types and traits✨ enhancementNew feature or requestNew feature or request📄 xmlfacet-xml crate, XML serialization/deserializationfacet-xml crate, XML serialization/deserialization🔵 jsonfacet-json crate, JSON serialization/deserializationfacet-json crate, JSON serialization/deserialization
Description
Problem
Currently, .inner on Shape is overloaded for multiple purposes:
- Variance computation:
Vec<T>,Map<K,V>, etc. set.inner(T::SHAPE)to avoid monomorphizing a variance function per generic instantiation - Transparent wrappers: Types that deserialize as their inner type (e.g., newtypes)
- Immutable collections with builders:
BytesandArc<[T]>use.innerto specify they build throughBytesMutandVec<T>respectively
This conflation causes problems for deserializers. After commit d7479d1 added .inner(T::SHAPE) to Vec for variance, deserializers broke because they couldn't distinguish:
- "I'm
Vec<T>with.innerfor variance only - deserialize me as a List" - "I'm
Byteswith.innerfor building - deserialize me as BytesMut then convert"
Current Workarounds
Deserializers check Def::List and look for init_in_place_with_capacity() to distinguish:
- Lists WITH
init_in_place_with_capacity→ use List deserializer (Vec) - Lists WITHOUT
init_in_place_with_capacity→ use.innerpath (Bytes)
This is fragile and encodes too much type-specific knowledge in deserializers.
Proposed Solution
Add a new .builder_shape field to Shape:
pub struct Shape {
// ... existing fields ...
/// Optional builder type for immutable collections.
/// If set, deserializers should build this type first, then convert to the target type.
pub builder_shape: Option<&'static Shape>,
}Usage
Bytes:
.builder_shape(Some(BytesMut::SHAPE))
.inner(u8::SHAPE) // Still set for "element type" semantics if neededArc<[T]>:
.builder_shape(Some(Vec::<T>::SHAPE))
.inner(T::SHAPE) // For varianceVec:
.builder_shape(None) // Builds directly
.inner(T::SHAPE) // For varianceDeserializer Logic
// 1. Check builder_shape first
if let Some(builder) = shape.builder_shape {
partial = deserialize_into(partial, builder)?;
partial = convert_from_builder(partial)?; // Uses TryFrom or vtable
return Ok(partial);
}
// 2. Check Def for fundamental collection types
match &shape.def {
Def::List(_) => deserialize_list(partial)?,
Def::Map(_) => deserialize_map(partial)?,
Def::Set(_) => deserialize_set(partial)?,
Def::Array(_) => deserialize_array(partial)?,
// ...
}
// 3. Check .inner for transparent wrappers
if let Some(inner) = shape.inner {
partial = partial.begin_inner()?;
partial = deserialize_into(partial, inner)?;
partial = partial.end()?;
return Ok(partial);
}
// 4. Check Type (Struct, Enum, etc.)
// ...Benefits
-
Clearer separation of concerns:
.builder_shape: "build me through this type".inner: "for variance or transparent wrapping"Def: "what I am semantically"
-
Generic solution: Works for any immutable collection, not just Lists
-
No type-specific knowledge in deserializers: No checking for
init_in_place_with_capacitypresence -
Explicit intent: Code clearly shows "this builds through a builder"
Implementation Scope
This affects:
facet-core: Add.builder_shapefield to Shape and ShapeBuilderfacet-json,facet-xml,facet-value, etc.: Update deserializer routing logicBytes,Arc<[T]>: Set.builder_shapeinstead of relying on.innerfor buildingVec,Map,Set,Array: Keep.innerfor variance onlyfacet-reflect: AddPartialsupport for builder conversion
Related
- Commit d7479d1: Added
.innerto collections for variance, exposing this issue - Current workaround in deserializers checks
init_in_place_with_capacity()presence
Metadata
Metadata
Assignees
Labels
⚙️ corefacet-core crate, core types and traitsfacet-core crate, core types and traits✨ enhancementNew feature or requestNew feature or request📄 xmlfacet-xml crate, XML serialization/deserializationfacet-xml crate, XML serialization/deserialization🔵 jsonfacet-json crate, JSON serialization/deserializationfacet-json crate, JSON serialization/deserialization