feat: harden serde-shape for downstream use - #11
Merged
Conversation
BinaryHeap does not require Ord to serialize, and RefCell, Mutex, and RwLock can serialize unsized contents. Mirror those directional bounds in the shape implementations. Why: callers should not lose shape reflection for a type that Serde itself can serialize, especially when orphan rules prevent downstream crates from repairing standard-library implementations.
Implement Default for the two container attribute records and their Tagging and DefaultShape fields. The defaults represent Serde's ordinary externally tagged container with no optional behavior enabled. Why: manual Shape implementations currently repeat every false and None field, which makes straightforward implementations noisy and makes the intended baseline harder to discover.
Accept directional serde_shape hooks on enum variants and expose their result as VariantContent::Shape. Keep variant-level Serde custom functions opaque when no shape hook is present, and avoid inferring field bounds when the hook replaces those fields. Why: Serde supports custom variant representations, but callers had no way to describe a known representation and were forced to expose an opaque boundary even when they could describe it precisely.
Keep Box<T> transparent for sized inputs, but reflect Box<[T]> through Serde's owned sequence path. Add explicit unsized implementations for Box<str> and Box<Path> so their existing behavior remains available. Why: Serde deserializes borrowed &[u8] through the bytes data-model call but deserializes Box<[u8]> through a sequence; forwarding every Box to its inner unsized type incorrectly collapsed those distinct contracts.
Add no_std shape implementations for CStr serialization, CString in both directions, and Box<CStr> deserialization. These types use Serde's byte-buffer data-model call rather than a sequence. Why: C strings are native Serde-supported alloc types, and downstream crates cannot add the missing Shape implementations themselves because both the trait and types are foreign.
Add Rc and Arc shape implementations that serialize through their contents and deserialize through the same owned Box representation Serde uses. Reflect weak pointers as optional contents and retain target atomic gating for Arc. Why: shared pointers are common Serde-supported alloc wrappers, but orphan rules left users unable to reflect them; delegating deserialization through Box also preserves the owned-slice distinction fixed separately.
Reflect Saturating<T> transparently for generic serialization while providing deserialization shapes only for the integer primitives implemented by Serde. Why: a blanket bidirectional implementation would overstate Serde's API, while omitting the wrapper entirely prevents valid numeric configuration fields from deriving Shape and cannot be repaired downstream.
Reflect Range, RangeFrom, RangeInclusive, and RangeTo as their named Serde structs with the appropriate start and end fields. Deserialization records Serde's unknown-field rejection. Why: ranges are native Serde-supported core types, but missing foreign-type implementations forced every downstream range field to use a custom hook; a local macro keeps the four contracts consistent without copied implementations.
Reflect Bound<T> as the externally tagged Unbounded unit variant plus Included and Excluded newtype variants in both directions. Why: Bound is part of Serde's native core-type surface and cannot receive a downstream Shape implementation; modeling its tags and variant styles is necessary for consumers that document or traverse the wire contract.
Reflect SystemTime behind the std feature as the secs_since_epoch and nanos_since_epoch struct accepted and emitted by Serde. Share the fixed-field struct construction with Duration instead of duplicating two full manual graph definitions. Why: SystemTime is a common native Serde type that downstream crates cannot implement Shape for, and configuration or wire types containing it should derive without a field-level escape hatch.
Build ordinary Result and network enum attributes through their Default implementations, removing repeated false and None literals without changing their reflected contracts. Why: keeping one definition of the ordinary Serde container baseline prevents built-in shapes from drifting as metadata evolves and makes manual implementations demonstrate the shorter public API.
Document the exact Default values for public container metadata and clarify that a variant shape hook describes content inside, rather than replacing, the enum tagging representation. Why: both APIs are concise only if callers can tell what is implicit; leaving those semantics to source inspection makes manual shapes easy to initialize incorrectly.
Document that Box<[u8]> follows Serde's owned sequence path and record the narrowed Box<T> blanket implementation in the breaking-change section. Why: correcting the borrowed-versus-owned shape changes trait availability for shape-only custom DSTs, and a release-quality changelog must make that migration cost explicit rather than hiding it behind a bug-fix label.
Add cargo x package to assemble both crates together, enable every feature, and build the normalized archives through Cargo's temporary registry. Run the locked workflow in CI and document it for contributors. Why: workspace builds can hide missing packaged files and path-dependency mistakes; validating both interdependent archives catches failures that users would otherwise discover only after a release attempt.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The 14 commits are intentionally separated by concern. Each commit message includes the concrete rationale under
Why:so the history can be reviewed in order without relying on a long PR narrative.Validation
cargo x build --lockedcargo x testcargo x lintcargo x package --lockedcargo +1.85.0 x testcargo +1.85.0 check -p serde-shape --lib --no-default-features --target thumbv7em-none-eabihfcargo +1.85.0 check -p serde-shape-test-no-std --lib --target thumbv7em-none-eabihfcargo semver-checks check-release -p serde-shape --baseline-rev main --all-features --release-type patch