1#![cfg_attr(docsrs, feature(doc_cfg))]
18#![deny(missing_docs)]
19
20use std::collections::BTreeMap;
21
22#[cfg(feature = "derive")]
23#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
24pub use serde_shape_derive::SerdeShape;
25
26pub trait SerdeShape {
28 fn shape_in(context: &mut ShapeContext) -> ShapeRef;
30
31 fn shape() -> Shape
33 where
34 Self: Sized,
35 {
36 Shape::for_type::<Self>()
37 }
38}
39
40#[derive(Clone, Debug, Eq, PartialEq)]
42pub struct Shape {
43 pub root: ShapeRef,
45 pub definitions: Vec<DefinitionShape>,
47}
48
49impl Shape {
50 pub fn for_type<T>() -> Self
52 where
53 T: SerdeShape + ?Sized,
54 {
55 let mut context = ShapeContext::default();
56 let root = T::shape_in(&mut context);
57 Self {
58 root,
59 definitions: context.finish(),
60 }
61 }
62
63 pub fn definition(&self, id: ShapeId) -> Option<&DefinitionShape> {
65 self.definitions.get(id.0)
66 }
67}
68
69#[derive(Debug, Default)]
71pub struct ShapeContext {
72 definitions: Vec<Option<DefinitionShape>>,
73 definitions_by_rust_name: BTreeMap<&'static str, ShapeId>,
74}
75
76impl ShapeContext {
77 pub fn define_named_type<F>(&mut self, type_name: TypeName, build: F) -> ShapeRef
79 where
80 F: FnOnce(&mut Self) -> DefinitionKind,
81 {
82 if let Some(id) = self.definitions_by_rust_name.get(type_name.rust_name) {
83 return ShapeRef::Definition(*id);
84 }
85
86 let id = ShapeId(self.definitions.len());
87 self.definitions_by_rust_name
88 .insert(type_name.rust_name, id);
89 self.definitions.push(None);
90
91 let kind = build(self);
92 self.definitions[id.0] = Some(DefinitionShape {
93 id,
94 type_name,
95 kind,
96 });
97 ShapeRef::Definition(id)
98 }
99
100 fn finish(self) -> Vec<DefinitionShape> {
101 self.definitions
102 .into_iter()
103 .map(|definition| definition.expect("shape definition was reserved but not filled"))
104 .collect()
105 }
106}
107
108#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
110pub struct ShapeId(pub usize);
111
112#[derive(Clone, Debug, Eq, PartialEq)]
114pub struct TypeName {
115 pub rust_name: &'static str,
117 pub serde_name: &'static str,
119}
120
121#[derive(Clone, Debug, Eq, PartialEq)]
123pub enum ShapeRef {
124 Unit,
126 Bool,
128 Char,
130 I8,
132 I16,
134 I32,
136 I64,
138 I128,
140 Isize,
142 U8,
144 U16,
146 U32,
148 U64,
150 U128,
152 Usize,
154 F32,
156 F64,
158 String,
160 Bytes,
162 Option(Box<ShapeRef>),
164 Seq(Box<ShapeRef>),
166 Array {
168 item: Box<ShapeRef>,
170 len: usize,
172 },
173 Map {
175 key: Box<ShapeRef>,
177 value: Box<ShapeRef>,
179 },
180 Tuple(Vec<ShapeRef>),
182 Definition(ShapeId),
184 Opaque(OpaqueShape),
186}
187
188impl ShapeRef {
189 pub fn is_signed_integer(&self) -> bool {
191 matches!(
192 self,
193 Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 | Self::Isize
194 )
195 }
196
197 pub fn is_unsigned_integer(&self) -> bool {
199 matches!(
200 self,
201 Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::U128 | Self::Usize
202 )
203 }
204
205 pub fn is_integer(&self) -> bool {
207 self.is_signed_integer() || self.is_unsigned_integer()
208 }
209
210 pub fn is_float(&self) -> bool {
212 matches!(self, Self::F32 | Self::F64)
213 }
214
215 pub fn is_number(&self) -> bool {
217 self.is_integer() || self.is_float()
218 }
219}
220
221#[derive(Clone, Debug, Eq, PartialEq)]
223pub struct DefinitionShape {
224 pub id: ShapeId,
226 pub type_name: TypeName,
228 pub kind: DefinitionKind,
230}
231
232#[derive(Clone, Debug, Eq, PartialEq)]
234pub enum DefinitionKind {
235 Struct(StructShape),
237 Enum(EnumShape),
239 Opaque(OpaqueShape),
241}
242
243#[derive(Clone, Debug, Eq, PartialEq)]
245pub struct ContainerAttributes {
246 pub tagging: Tagging,
248 pub deny_unknown_fields: bool,
250 pub default: DefaultShape,
252 pub has_flatten: bool,
254 pub transparent: bool,
256 pub expecting: Option<&'static str>,
258 pub non_exhaustive: bool,
260}
261
262#[derive(Clone, Debug, Eq, PartialEq)]
264pub enum Tagging {
265 External,
267 Internal {
269 tag: &'static str,
271 },
272 Adjacent {
274 tag: &'static str,
276 content: &'static str,
278 },
279 Untagged,
281}
282
283#[derive(Clone, Debug, Eq, PartialEq)]
285pub struct StructShape {
286 pub style: FieldsStyle,
288 pub fields: Vec<FieldShape>,
290 pub attributes: ContainerAttributes,
292}
293
294#[derive(Clone, Debug, Eq, PartialEq)]
296pub struct EnumShape {
297 pub repr: Tagging,
299 pub variants: Vec<VariantShape>,
301 pub attributes: ContainerAttributes,
303}
304
305#[derive(Clone, Copy, Debug, Eq, PartialEq)]
307pub enum FieldsStyle {
308 Struct,
310 Tuple,
312 Newtype,
314 Unit,
316}
317
318#[derive(Clone, Debug, Eq, PartialEq)]
320pub struct FieldShape {
321 pub member: FieldMember,
323 pub deserialize_name: &'static str,
325 pub deserialize_aliases: Vec<&'static str>,
327 pub shape: Option<ShapeRef>,
329 pub default: DefaultShape,
331 pub flatten: bool,
333 pub skip_deserializing: bool,
335 pub custom_deserializer: bool,
337 pub transparent: bool,
339}
340
341#[derive(Clone, Debug, Eq, PartialEq)]
343pub enum FieldMember {
344 Named(&'static str),
346 Unnamed(usize),
348}
349
350#[derive(Clone, Debug, Eq, PartialEq)]
352pub struct VariantShape {
353 pub rust_name: &'static str,
355 pub deserialize_name: &'static str,
357 pub deserialize_aliases: Vec<&'static str>,
359 pub style: FieldsStyle,
361 pub fields: Vec<FieldShape>,
363 pub skip_deserializing: bool,
365 pub custom_deserializer: bool,
367 pub other: bool,
369 pub untagged: bool,
371}
372
373#[derive(Clone, Debug, Eq, PartialEq)]
375pub enum DefaultShape {
376 None,
378 Default,
380 Path(&'static str),
382}
383
384impl DefaultShape {
385 pub fn is_none(&self) -> bool {
387 matches!(self, Self::None)
388 }
389}
390
391#[derive(Clone, Debug, Eq, PartialEq)]
393pub struct OpaqueShape {
394 pub type_name: &'static str,
396 pub reason: OpaqueReason,
398 pub detail: Option<&'static str>,
400}
401
402#[derive(Clone, Copy, Debug, Eq, PartialEq)]
404pub enum OpaqueReason {
405 FromType,
407 TryFromType,
409 Remote,
411 CustomDeserializer,
413 Unsupported,
415}
416
417macro_rules! primitive_shape {
418 ($($ty:ty => $shape:expr;)+) => {
419 $(
420 impl SerdeShape for $ty {
421 fn shape_in(_context: &mut ShapeContext) -> ShapeRef {
422 $shape
423 }
424 }
425 )+
426 };
427}
428
429primitive_shape! {
430 () => ShapeRef::Unit;
431 bool => ShapeRef::Bool;
432 char => ShapeRef::Char;
433 i8 => ShapeRef::I8;
434 i16 => ShapeRef::I16;
435 i32 => ShapeRef::I32;
436 i64 => ShapeRef::I64;
437 i128 => ShapeRef::I128;
438 isize => ShapeRef::Isize;
439 u8 => ShapeRef::U8;
440 u16 => ShapeRef::U16;
441 u32 => ShapeRef::U32;
442 u64 => ShapeRef::U64;
443 u128 => ShapeRef::U128;
444 usize => ShapeRef::Usize;
445 f32 => ShapeRef::F32;
446 f64 => ShapeRef::F64;
447 str => ShapeRef::String;
448 String => ShapeRef::String;
449 std::path::Path => ShapeRef::String;
450 std::path::PathBuf => ShapeRef::String;
451 std::net::IpAddr => ShapeRef::String;
452 std::net::Ipv4Addr => ShapeRef::String;
453 std::net::Ipv6Addr => ShapeRef::String;
454 std::net::SocketAddr => ShapeRef::String;
455 std::net::SocketAddrV4 => ShapeRef::String;
456 std::net::SocketAddrV6 => ShapeRef::String;
457 std::num::NonZeroI8 => ShapeRef::I8;
458 std::num::NonZeroI16 => ShapeRef::I16;
459 std::num::NonZeroI32 => ShapeRef::I32;
460 std::num::NonZeroI64 => ShapeRef::I64;
461 std::num::NonZeroI128 => ShapeRef::I128;
462 std::num::NonZeroIsize => ShapeRef::Isize;
463 std::num::NonZeroU8 => ShapeRef::U8;
464 std::num::NonZeroU16 => ShapeRef::U16;
465 std::num::NonZeroU32 => ShapeRef::U32;
466 std::num::NonZeroU64 => ShapeRef::U64;
467 std::num::NonZeroU128 => ShapeRef::U128;
468 std::num::NonZeroUsize => ShapeRef::Usize;
469}
470
471#[cfg(target_has_atomic = "8")]
472primitive_shape! {
473 std::sync::atomic::AtomicBool => ShapeRef::Bool;
474 std::sync::atomic::AtomicI8 => ShapeRef::I8;
475 std::sync::atomic::AtomicU8 => ShapeRef::U8;
476}
477
478#[cfg(target_has_atomic = "16")]
479primitive_shape! {
480 std::sync::atomic::AtomicI16 => ShapeRef::I16;
481 std::sync::atomic::AtomicU16 => ShapeRef::U16;
482}
483
484#[cfg(target_has_atomic = "32")]
485primitive_shape! {
486 std::sync::atomic::AtomicI32 => ShapeRef::I32;
487 std::sync::atomic::AtomicU32 => ShapeRef::U32;
488}
489
490#[cfg(target_has_atomic = "64")]
491primitive_shape! {
492 std::sync::atomic::AtomicI64 => ShapeRef::I64;
493 std::sync::atomic::AtomicU64 => ShapeRef::U64;
494}
495
496#[cfg(target_has_atomic = "ptr")]
497primitive_shape! {
498 std::sync::atomic::AtomicIsize => ShapeRef::Isize;
499 std::sync::atomic::AtomicUsize => ShapeRef::Usize;
500}
501
502impl SerdeShape for [u8] {
503 fn shape_in(_context: &mut ShapeContext) -> ShapeRef {
504 ShapeRef::Bytes
505 }
506}
507
508impl<T> SerdeShape for &T
509where
510 T: SerdeShape + ?Sized,
511{
512 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
513 T::shape_in(context)
514 }
515}
516
517impl<T> SerdeShape for &mut T
518where
519 T: SerdeShape + ?Sized,
520{
521 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
522 T::shape_in(context)
523 }
524}
525
526impl<T> SerdeShape for Box<T>
527where
528 T: SerdeShape + ?Sized,
529{
530 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
531 T::shape_in(context)
532 }
533}
534
535impl<'a, T> SerdeShape for std::borrow::Cow<'a, T>
536where
537 T: ToOwned + ?Sized,
538 T::Owned: SerdeShape,
539{
540 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
541 T::Owned::shape_in(context)
542 }
543}
544
545impl<T> SerdeShape for std::cell::Cell<T>
546where
547 T: Copy + SerdeShape,
548{
549 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
550 T::shape_in(context)
551 }
552}
553
554impl<T> SerdeShape for std::cell::RefCell<T>
555where
556 T: SerdeShape,
557{
558 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
559 T::shape_in(context)
560 }
561}
562
563impl<T> SerdeShape for std::sync::Mutex<T>
564where
565 T: SerdeShape,
566{
567 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
568 T::shape_in(context)
569 }
570}
571
572impl<T> SerdeShape for std::sync::RwLock<T>
573where
574 T: SerdeShape,
575{
576 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
577 T::shape_in(context)
578 }
579}
580
581impl<T> SerdeShape for std::num::Wrapping<T>
582where
583 T: SerdeShape,
584{
585 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
586 T::shape_in(context)
587 }
588}
589
590impl<T> SerdeShape for std::cmp::Reverse<T>
591where
592 T: SerdeShape,
593{
594 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
595 T::shape_in(context)
596 }
597}
598
599impl<T> SerdeShape for Option<T>
600where
601 T: SerdeShape,
602{
603 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
604 ShapeRef::Option(Box::new(T::shape_in(context)))
605 }
606}
607
608impl<T> SerdeShape for Vec<T>
609where
610 T: SerdeShape,
611{
612 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
613 ShapeRef::Seq(Box::new(T::shape_in(context)))
614 }
615}
616
617impl<T> SerdeShape for std::collections::VecDeque<T>
618where
619 T: SerdeShape,
620{
621 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
622 ShapeRef::Seq(Box::new(T::shape_in(context)))
623 }
624}
625
626impl<T> SerdeShape for std::collections::LinkedList<T>
627where
628 T: SerdeShape,
629{
630 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
631 ShapeRef::Seq(Box::new(T::shape_in(context)))
632 }
633}
634
635impl<T> SerdeShape for std::collections::BinaryHeap<T>
636where
637 T: Ord + SerdeShape,
638{
639 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
640 ShapeRef::Seq(Box::new(T::shape_in(context)))
641 }
642}
643
644impl<T, const N: usize> SerdeShape for [T; N]
645where
646 T: SerdeShape,
647{
648 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
649 ShapeRef::Array {
650 item: Box::new(T::shape_in(context)),
651 len: N,
652 }
653 }
654}
655
656impl<K, V> SerdeShape for BTreeMap<K, V>
657where
658 K: SerdeShape,
659 V: SerdeShape,
660{
661 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
662 ShapeRef::Map {
663 key: Box::new(K::shape_in(context)),
664 value: Box::new(V::shape_in(context)),
665 }
666 }
667}
668
669impl<K, V, S> SerdeShape for std::collections::HashMap<K, V, S>
670where
671 K: SerdeShape,
672 V: SerdeShape,
673{
674 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
675 ShapeRef::Map {
676 key: Box::new(K::shape_in(context)),
677 value: Box::new(V::shape_in(context)),
678 }
679 }
680}
681
682impl<T> SerdeShape for std::collections::BTreeSet<T>
683where
684 T: SerdeShape,
685{
686 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
687 ShapeRef::Seq(Box::new(T::shape_in(context)))
688 }
689}
690
691impl<T, S> SerdeShape for std::collections::HashSet<T, S>
692where
693 T: SerdeShape,
694{
695 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
696 ShapeRef::Seq(Box::new(T::shape_in(context)))
697 }
698}
699
700impl<T> SerdeShape for std::marker::PhantomData<T> {
701 fn shape_in(_context: &mut ShapeContext) -> ShapeRef {
702 ShapeRef::Unit
703 }
704}
705
706macro_rules! tuple_shape {
707 ($($name:ident),+ $(,)?) => {
708 impl<$($name),+> SerdeShape for ($($name,)+)
709 where
710 $($name: SerdeShape,)+
711 {
712 fn shape_in(context: &mut ShapeContext) -> ShapeRef {
713 ShapeRef::Tuple(vec![$($name::shape_in(context),)+])
714 }
715 }
716 };
717}
718
719tuple_shape!(T0);
720tuple_shape!(T0, T1);
721tuple_shape!(T0, T1, T2);
722tuple_shape!(T0, T1, T2, T3);
723tuple_shape!(T0, T1, T2, T3, T4);
724tuple_shape!(T0, T1, T2, T3, T4, T5);
725tuple_shape!(T0, T1, T2, T3, T4, T5, T6);
726tuple_shape!(T0, T1, T2, T3, T4, T5, T6, T7);
727tuple_shape!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
728tuple_shape!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
729tuple_shape!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
730tuple_shape!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
731
732#[cfg(test)]
733mod tests {
734 use super::*;
735
736 #[test]
737 fn builds_map_shape() {
738 let shape = Shape::for_type::<BTreeMap<String, Option<u16>>>();
739
740 assert_eq!(
741 shape.root,
742 ShapeRef::Map {
743 key: Box::new(ShapeRef::String),
744 value: Box::new(ShapeRef::Option(Box::new(ShapeRef::U16))),
745 }
746 );
747 assert!(shape.definitions.is_empty());
748 }
749
750 #[test]
751 fn maps_common_std_shapes() {
752 assert_eq!(Shape::for_type::<std::path::Path>().root, ShapeRef::String);
753 assert_eq!(
754 Shape::for_type::<std::path::PathBuf>().root,
755 ShapeRef::String
756 );
757 assert_eq!(
758 Shape::for_type::<std::borrow::Cow<'static, str>>().root,
759 ShapeRef::String
760 );
761 assert_eq!(Shape::for_type::<std::cell::Cell<u8>>().root, ShapeRef::U8);
762 assert_eq!(
763 Shape::for_type::<std::num::Wrapping<i16>>().root,
764 ShapeRef::I16
765 );
766 assert_eq!(
767 Shape::for_type::<std::cmp::Reverse<u32>>().root,
768 ShapeRef::U32
769 );
770 assert_eq!(
771 Shape::for_type::<std::collections::VecDeque<u8>>().root,
772 ShapeRef::Seq(Box::new(ShapeRef::U8))
773 );
774 assert_eq!(
775 Shape::for_type::<std::collections::LinkedList<i32>>().root,
776 ShapeRef::Seq(Box::new(ShapeRef::I32))
777 );
778 assert_eq!(
779 Shape::for_type::<std::collections::BinaryHeap<u16>>().root,
780 ShapeRef::Seq(Box::new(ShapeRef::U16))
781 );
782 }
783
784 #[test]
785 fn classifies_flat_numeric_shapes() {
786 assert!(ShapeRef::I8.is_signed_integer());
787 assert!(ShapeRef::Usize.is_unsigned_integer());
788 assert!(ShapeRef::I128.is_integer());
789 assert!(ShapeRef::U64.is_integer());
790 assert!(ShapeRef::F32.is_float());
791 assert!(ShapeRef::F64.is_number());
792 assert!(!ShapeRef::String.is_number());
793 }
794
795 #[cfg(target_has_atomic = "ptr")]
796 #[test]
797 fn maps_atomic_shapes() {
798 assert_eq!(
799 Shape::for_type::<std::sync::atomic::AtomicUsize>().root,
800 ShapeRef::Usize
801 );
802 }
803}