facet_types/
lib.rs

1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4//! structs and vtable definitions used by Facet
5
6// TODO: mark `non_exhaustive`, add `const fn` builder patterns
7
8use core::alloc::Layout;
9use core::fmt;
10
11use typeid::ConstTypeId;
12
13mod list;
14pub use list::*;
15
16mod map;
17pub use map::*;
18
19mod value;
20pub use value::*;
21
22/// Schema for reflection of a type
23#[derive(Clone, Copy, Debug)]
24#[non_exhaustive]
25pub struct Shape {
26    /// Size, alignment
27    pub layout: Layout,
28
29    /// VTable for common operations. This is indirected because the vtable might
30    /// have different functions implemented based on generic type parameters:
31    /// HashMap<K, V> is not even constructible if `K` is not `Hash` + `Eq`.
32    pub vtable: &'static ValueVTable,
33
34    /// Details/contents of the value
35    pub def: Def,
36}
37
38impl Shape {
39    /// Checks if a shape has the given characteristic.
40    pub const fn is(&'static self, characteristic: Characteristic) -> bool {
41        match characteristic {
42            // Marker traits
43            Characteristic::Send => self.vtable.marker_traits.contains(MarkerTraits::SEND),
44            Characteristic::Sync => self.vtable.marker_traits.contains(MarkerTraits::SYNC),
45            Characteristic::Copy => self.vtable.marker_traits.contains(MarkerTraits::COPY),
46            Characteristic::Eq => self.vtable.marker_traits.contains(MarkerTraits::EQ),
47
48            // Functionality traits
49            Characteristic::Clone => self.vtable.clone_into.is_some(),
50            Characteristic::Debug => self.vtable.debug.is_some(),
51            Characteristic::PartialEq => self.vtable.eq.is_some(),
52            Characteristic::PartialOrd => self.vtable.partial_ord.is_some(),
53            Characteristic::Ord => self.vtable.ord.is_some(),
54            Characteristic::Hash => self.vtable.hash.is_some(),
55            Characteristic::Default => self.vtable.default_in_place.is_some(),
56        }
57    }
58
59    /// Check if this shape implements the Send trait
60    pub const fn is_send(&'static self) -> bool {
61        self.is(Characteristic::Send)
62    }
63
64    /// Check if this shape implements the Sync trait
65    pub const fn is_sync(&'static self) -> bool {
66        self.is(Characteristic::Sync)
67    }
68
69    /// Check if this shape implements the Copy trait
70    pub const fn is_copy(&'static self) -> bool {
71        self.is(Characteristic::Copy)
72    }
73
74    /// Check if this shape implements the Eq trait
75    pub const fn is_eq(&'static self) -> bool {
76        self.is(Characteristic::Eq)
77    }
78
79    /// Check if this shape implements the Clone trait
80    pub const fn is_clone(&'static self) -> bool {
81        self.is(Characteristic::Clone)
82    }
83
84    /// Check if this shape implements the Debug trait
85    pub const fn is_debug(&'static self) -> bool {
86        self.is(Characteristic::Debug)
87    }
88
89    /// Check if this shape implements the PartialEq trait
90    pub const fn is_partial_eq(&'static self) -> bool {
91        self.is(Characteristic::PartialEq)
92    }
93
94    /// Check if this shape implements the PartialOrd trait
95    pub const fn is_partial_ord(&'static self) -> bool {
96        self.is(Characteristic::PartialOrd)
97    }
98
99    /// Check if this shape implements the Ord trait
100    pub const fn is_ord(&'static self) -> bool {
101        self.is(Characteristic::Ord)
102    }
103
104    /// Check if this shape implements the Hash trait
105    pub const fn is_hash(&'static self) -> bool {
106        self.is(Characteristic::Hash)
107    }
108
109    /// Check if this shape implements the Default trait
110    pub const fn is_default(&'static self) -> bool {
111        self.is(Characteristic::Default)
112    }
113
114    /// Writes the name of this type to the given formatter
115    pub fn write_type_name(&self, f: &mut fmt::Formatter<'_>, opts: TypeNameOpts) -> fmt::Result {
116        (self.vtable.type_name)(f, opts)
117    }
118
119    /// Returns a builder for shape
120    pub const fn builder() -> ShapeBuilder {
121        ShapeBuilder::new()
122    }
123}
124
125/// Builder for [`Shape`]
126pub struct ShapeBuilder {
127    layout: Option<Layout>,
128    vtable: Option<&'static ValueVTable>,
129    def: Option<Def>,
130}
131
132impl ShapeBuilder {
133    /// Creates a new `ShapeBuilder` with all fields set to `None`.
134    #[allow(clippy::new_without_default)]
135    pub const fn new() -> Self {
136        Self {
137            layout: None,
138            vtable: None,
139            def: None,
140        }
141    }
142
143    /// Sets the `layout` field of the `ShapeBuilder`.
144    #[inline]
145    pub const fn layout(mut self, layout: Layout) -> Self {
146        self.layout = Some(layout);
147        self
148    }
149
150    /// Sets the `vtable` field of the `ShapeBuilder`.
151    #[inline]
152    pub const fn vtable(mut self, vtable: &'static ValueVTable) -> Self {
153        self.vtable = Some(vtable);
154        self
155    }
156
157    /// Sets the `def` field of the `ShapeBuilder`.
158    #[inline]
159    pub const fn def(mut self, def: Def) -> Self {
160        self.def = Some(def);
161        self
162    }
163
164    /// Builds a `Shape` from the `ShapeBuilder`.
165    ///
166    /// # Panics
167    ///
168    /// This method will panic if any of the required fields (`layout`, `vtable`, or `def`) are `None`.
169    #[inline]
170    pub const fn build(self) -> Shape {
171        Shape {
172            layout: self.layout.unwrap(),
173            vtable: self.vtable.unwrap(),
174            def: self.def.unwrap(),
175        }
176    }
177}
178
179impl PartialEq for Shape {
180    fn eq(&self, other: &Self) -> bool {
181        self.def == other.def && self.layout == other.layout
182    }
183}
184
185impl Eq for Shape {}
186
187impl core::hash::Hash for Shape {
188    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
189        self.def.hash(state);
190        self.layout.hash(state);
191    }
192}
193
194impl Shape {
195    /// Check if this shape is of the given type
196    pub fn is_shape(&'static self, other: &'static Shape) -> bool {
197        self == other
198    }
199
200    /// Assert that this shape is equal to the given shape, panicking if it's not
201    pub fn assert_shape(&'static self, other: &'static Shape) {
202        assert!(
203            self.is_shape(other),
204            "Shape mismatch: expected {other}, found {self}",
205        );
206    }
207}
208
209// Helper struct to format the name for display
210impl core::fmt::Display for Shape {
211    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
212        (self.vtable.type_name)(f, TypeNameOpts::default())
213    }
214}
215
216impl Shape {
217    /// Heap-allocate a value of this shape
218    #[cfg(feature = "std")]
219    #[inline]
220    pub fn allocate(&self) -> facet_opaque::OpaqueUninit<'static> {
221        facet_opaque::OpaqueUninit::new(unsafe { std::alloc::alloc(self.layout) })
222    }
223}
224
225/// Errors encountered when calling `field_by_index` or `field_by_name`
226#[derive(Debug, Copy, Clone, PartialEq, Eq)]
227#[non_exhaustive]
228pub enum FieldError {
229    /// `field_by_index` was called on a dynamic collection, that has no
230    /// static fields. a map doesn't have a "first field", it can only
231    /// associate by keys.
232    NoStaticFields,
233
234    /// `field_by_name` was called on a struct, and there is no static field
235    /// with the given key.
236    NoSuchStaticField,
237
238    /// `field_by_index` was called on a fixed-size collection (like a tuple,
239    /// a struct, or a fixed-size array) and the index was out of bounds.
240    IndexOutOfBounds,
241
242    /// `field_by_index` or `field_by_name` was called on a non-struct type.
243    NotAStruct,
244}
245
246impl core::error::Error for FieldError {}
247
248impl core::fmt::Display for FieldError {
249    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
250        match self {
251            FieldError::NoStaticFields => write!(f, "No static fields available"),
252            FieldError::NoSuchStaticField => write!(f, "No such static field"),
253            FieldError::IndexOutOfBounds => write!(f, "Index out of bounds"),
254            FieldError::NotAStruct => write!(f, "Not a struct"),
255        }
256    }
257}
258
259/// Common fields for struct-like types
260#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
261#[non_exhaustive]
262pub struct StructDef {
263    /// the kind of struct (e.g. struct, tuple struct, tuple)
264    pub kind: StructKind,
265
266    /// all fields, in declaration order (not necessarily in memory order)
267    pub fields: &'static [Field],
268}
269
270impl StructDef {
271    /// Returns a builder for StructDef
272    pub const fn builder() -> StructDefBuilder {
273        StructDefBuilder::new()
274    }
275}
276
277/// Builder for StructDef
278pub struct StructDefBuilder {
279    kind: Option<StructKind>,
280    fields: Option<&'static [Field]>,
281}
282
283impl StructDefBuilder {
284    /// Creates a new StructDefBuilder
285    #[allow(clippy::new_without_default)]
286    pub const fn new() -> Self {
287        Self {
288            kind: None,
289            fields: None,
290        }
291    }
292
293    /// Sets the kind for the StructDef
294    pub const fn kind(mut self, kind: StructKind) -> Self {
295        self.kind = Some(kind);
296        self
297    }
298
299    /// Sets the fields for the StructDef
300    pub const fn fields(mut self, fields: &'static [Field]) -> Self {
301        self.fields = Some(fields);
302        self
303    }
304
305    /// Builds the StructDef
306    pub const fn build(self) -> StructDef {
307        StructDef {
308            kind: self.kind.unwrap(),
309            fields: self.fields.unwrap(),
310        }
311    }
312}
313
314/// Describes the kind of struct (useful for deserializing)
315#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
316#[non_exhaustive]
317pub enum StructKind {
318    /// struct S { t0: T0, t1: T1 }
319    Struct,
320
321    /// struct TupleStruct(T0, T1);
322    TupleStruct,
323
324    /// (T0, T1)
325    Tuple,
326}
327
328/// Describes a field in a struct or tuple
329#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
330#[non_exhaustive]
331pub struct Field {
332    /// key for the struct field (for tuples and tuple-structs, this is the 0-based index)
333    pub name: &'static str,
334
335    /// schema of the inner type
336    pub shape: &'static Shape,
337
338    /// offset of the field in the struct (obtained through `core::mem::offset_of`)
339    pub offset: usize,
340
341    /// flags for the field (e.g. sensitive, etc.)
342    pub flags: FieldFlags,
343
344    /// arbitrary attributes set via the derive macro
345    pub attributes: &'static [FieldAttribute],
346}
347
348impl Field {
349    /// Returns a builder for Field
350    pub const fn builder() -> FieldBuilder {
351        FieldBuilder::new()
352    }
353}
354
355/// Builder for Field
356pub struct FieldBuilder {
357    name: Option<&'static str>,
358    shape: Option<&'static Shape>,
359    offset: Option<usize>,
360    flags: Option<FieldFlags>,
361    attributes: &'static [FieldAttribute],
362}
363
364/// An attribute that can be set on a field
365#[non_exhaustive]
366#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
367pub enum FieldAttribute {
368    /// Marks field as containing sensitive information
369    Sensitive,
370    /// Custom field attribute containing arbitrary text
371    Arbitrary(&'static str),
372}
373
374impl FieldBuilder {
375    /// Creates a new FieldBuilder
376    #[allow(clippy::new_without_default)]
377    pub const fn new() -> Self {
378        Self {
379            name: None,
380            shape: None,
381            offset: None,
382            flags: None,
383            attributes: &[],
384        }
385    }
386
387    /// Sets the name for the Field
388    pub const fn name(mut self, name: &'static str) -> Self {
389        self.name = Some(name);
390        self
391    }
392
393    /// Sets the shape for the Field
394    pub const fn shape(mut self, shape: &'static Shape) -> Self {
395        self.shape = Some(shape);
396        self
397    }
398
399    /// Sets the offset for the Field
400    pub const fn offset(mut self, offset: usize) -> Self {
401        self.offset = Some(offset);
402        self
403    }
404
405    /// Sets the flags for the Field
406    pub const fn flags(mut self, flags: FieldFlags) -> Self {
407        self.flags = Some(flags);
408        self
409    }
410
411    /// Sets the attributes for the Field
412    pub const fn attributes(mut self, attributes: &'static [FieldAttribute]) -> Self {
413        self.attributes = attributes;
414        self
415    }
416
417    /// Builds the Field
418    pub const fn build(self) -> Field {
419        Field {
420            name: self.name.unwrap(),
421            shape: self.shape.unwrap(),
422            offset: self.offset.unwrap(),
423            flags: match self.flags {
424                Some(flags) => flags,
425                None => FieldFlags::EMPTY,
426            },
427            attributes: self.attributes,
428        }
429    }
430}
431
432bitflags::bitflags! {
433    /// Flags that can be applied to fields to modify their behavior
434    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
435    pub struct FieldFlags: u64 {
436        /// An empty set of flags
437        const EMPTY = 0;
438
439        /// Flag indicating this field contains sensitive data that should not be displayed
440        const SENSITIVE = 1 << 0;
441    }
442}
443
444impl Default for FieldFlags {
445    #[inline(always)]
446    fn default() -> Self {
447        Self::EMPTY
448    }
449}
450
451impl core::fmt::Display for FieldFlags {
452    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
453        if self.is_empty() {
454            return write!(f, "none");
455        }
456
457        // Define a vector of flag entries: (flag, name)
458        let flags = [
459            (FieldFlags::SENSITIVE, "sensitive"),
460            // Future flags can be easily added here:
461            // (FieldFlags::SOME_FLAG, "some_flag"),
462            // (FieldFlags::ANOTHER_FLAG, "another_flag"),
463        ];
464
465        // Write all active flags with proper separators
466        let mut is_first = true;
467        for (flag, name) in flags {
468            if self.contains(flag) {
469                if !is_first {
470                    write!(f, ", ")?;
471                }
472                is_first = false;
473                write!(f, "{}", name)?;
474            }
475        }
476
477        Ok(())
478    }
479}
480
481/// Fields for map types
482#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
483#[non_exhaustive]
484pub struct MapDef {
485    /// vtable for interacting with the map
486    pub vtable: &'static MapVTable,
487    /// shape of the keys in the map
488    pub k: &'static Shape,
489    /// shape of the values in the map
490    pub v: &'static Shape,
491}
492
493impl MapDef {
494    /// Returns a builder for MapDef
495    pub const fn builder() -> MapDefBuilder {
496        MapDefBuilder::new()
497    }
498}
499
500/// Builder for MapDef
501pub struct MapDefBuilder {
502    vtable: Option<&'static MapVTable>,
503    k: Option<&'static Shape>,
504    v: Option<&'static Shape>,
505}
506
507impl MapDefBuilder {
508    /// Creates a new MapDefBuilder
509    #[allow(clippy::new_without_default)]
510    pub const fn new() -> Self {
511        Self {
512            vtable: None,
513            k: None,
514            v: None,
515        }
516    }
517
518    /// Sets the vtable for the MapDef
519    pub const fn vtable(mut self, vtable: &'static MapVTable) -> Self {
520        self.vtable = Some(vtable);
521        self
522    }
523
524    /// Sets the key shape for the MapDef
525    pub const fn k(mut self, k: &'static Shape) -> Self {
526        self.k = Some(k);
527        self
528    }
529
530    /// Sets the value shape for the MapDef
531    pub const fn v(mut self, v: &'static Shape) -> Self {
532        self.v = Some(v);
533        self
534    }
535
536    /// Builds the MapDef
537    pub const fn build(self) -> MapDef {
538        MapDef {
539            vtable: self.vtable.unwrap(),
540            k: self.k.unwrap(),
541            v: self.v.unwrap(),
542        }
543    }
544}
545
546/// Fields for list types
547#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
548#[non_exhaustive]
549pub struct ListDef {
550    /// vtable for interacting with the list
551    pub vtable: &'static ListVTable,
552    /// shape of the items in the list
553    pub t: &'static Shape,
554}
555
556impl ListDef {
557    /// Returns a builder for ListDef
558    pub const fn builder() -> ListDefBuilder {
559        ListDefBuilder::new()
560    }
561}
562
563/// Builder for ListDef
564pub struct ListDefBuilder {
565    vtable: Option<&'static ListVTable>,
566    t: Option<&'static Shape>,
567}
568
569impl ListDefBuilder {
570    /// Creates a new ListDefBuilder
571    #[allow(clippy::new_without_default)]
572    pub const fn new() -> Self {
573        Self {
574            vtable: None,
575            t: None,
576        }
577    }
578
579    /// Sets the vtable for the ListDef
580    pub const fn vtable(mut self, vtable: &'static ListVTable) -> Self {
581        self.vtable = Some(vtable);
582        self
583    }
584
585    /// Sets the item shape for the ListDef
586    pub const fn t(mut self, t: &'static Shape) -> Self {
587        self.t = Some(t);
588        self
589    }
590
591    /// Builds the ListDef
592    pub const fn build(self) -> ListDef {
593        ListDef {
594            vtable: self.vtable.unwrap(),
595            t: self.t.unwrap(),
596        }
597    }
598}
599
600/// Fields for enum types
601#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
602#[non_exhaustive]
603pub struct EnumDef {
604    /// representation of the enum (u8, u16, etc.)
605    pub repr: EnumRepr,
606    /// all variants for this enum
607    pub variants: &'static [Variant],
608}
609
610impl EnumDef {
611    /// Returns a builder for EnumDef
612    pub const fn builder() -> EnumDefBuilder {
613        EnumDefBuilder::new()
614    }
615}
616
617/// Builder for EnumDef
618pub struct EnumDefBuilder {
619    repr: Option<EnumRepr>,
620    variants: Option<&'static [Variant]>,
621}
622
623impl EnumDefBuilder {
624    /// Creates a new EnumDefBuilder
625    #[allow(clippy::new_without_default)]
626    pub const fn new() -> Self {
627        Self {
628            repr: None,
629            variants: None,
630        }
631    }
632
633    /// Sets the representation for the EnumDef
634    pub const fn repr(mut self, repr: EnumRepr) -> Self {
635        self.repr = Some(repr);
636        self
637    }
638
639    /// Sets the variants for the EnumDef
640    pub const fn variants(mut self, variants: &'static [Variant]) -> Self {
641        self.variants = Some(variants);
642        self
643    }
644
645    /// Builds the EnumDef
646    pub const fn build(self) -> EnumDef {
647        EnumDef {
648            repr: self.repr.unwrap(),
649            variants: self.variants.unwrap(),
650        }
651    }
652}
653
654/// Describes a variant of an enum
655#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
656#[non_exhaustive]
657pub struct Variant {
658    /// Name of the variant
659    pub name: &'static str,
660
661    /// Discriminant value (if available)
662    pub discriminant: Option<i64>,
663
664    /// Kind of variant (unit, tuple, or struct)
665    pub kind: VariantKind,
666}
667
668impl Variant {
669    /// Returns a builder for Variant
670    pub const fn builder() -> VariantBuilder {
671        VariantBuilder::new()
672    }
673}
674
675/// Builder for Variant
676pub struct VariantBuilder {
677    name: Option<&'static str>,
678    discriminant: Option<Option<i64>>,
679    kind: Option<VariantKind>,
680}
681
682impl VariantBuilder {
683    /// Creates a new VariantBuilder
684    #[allow(clippy::new_without_default)]
685    pub const fn new() -> Self {
686        Self {
687            name: None,
688            discriminant: None,
689            kind: None,
690        }
691    }
692
693    /// Sets the name for the Variant
694    pub const fn name(mut self, name: &'static str) -> Self {
695        self.name = Some(name);
696        self
697    }
698
699    /// Sets the discriminant for the Variant
700    pub const fn discriminant(mut self, discriminant: Option<i64>) -> Self {
701        self.discriminant = Some(discriminant);
702        self
703    }
704
705    /// Sets the kind for the Variant
706    pub const fn kind(mut self, kind: VariantKind) -> Self {
707        self.kind = Some(kind);
708        self
709    }
710
711    /// Builds the Variant
712    pub const fn build(self) -> Variant {
713        Variant {
714            name: self.name.unwrap(),
715            discriminant: self.discriminant.unwrap(),
716            kind: self.kind.unwrap(),
717        }
718    }
719}
720
721/// Represents the different kinds of variants that can exist in a Rust enum
722#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
723#[non_exhaustive]
724pub enum VariantKind {
725    /// Unit variant (e.g., `None` in Option)
726    Unit,
727
728    /// Tuple variant with unnamed fields (e.g., `Some(T)` in Option)
729    Tuple {
730        /// List of fields contained in the tuple variant
731        fields: &'static [Field],
732    },
733
734    /// Struct variant with named fields (e.g., `Struct { field: T }`)
735    Struct {
736        /// List of fields contained in the struct variant
737        fields: &'static [Field],
738    },
739}
740
741/// All possible representations for Rust enums
742#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
743#[non_exhaustive]
744pub enum EnumRepr {
745    /// Default representation (compiler-dependent)
746    Default,
747    /// u8 representation (#[repr(u8)])
748    U8,
749    /// u16 representation (#[repr(u16)])
750    U16,
751    /// u32 representation (#[repr(u32)])
752    U32,
753    /// u64 representation (#[repr(u64)])
754    U64,
755    /// usize representation (#[repr(usize)])
756    USize,
757    /// i8 representation (#[repr(i8)])
758    I8,
759    /// i16 representation (#[repr(i16)])
760    I16,
761    /// i32 representation (#[repr(i32)])
762    I32,
763    /// i64 representation (#[repr(i64)])
764    I64,
765    /// isize representation (#[repr(isize)])
766    ISize,
767}
768
769impl Default for EnumRepr {
770    fn default() -> Self {
771        Self::Default
772    }
773}
774
775/// Definition for scalar types
776#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
777#[non_exhaustive]
778pub struct ScalarDef {
779    /// The TypeId of the scalar type
780    pub type_id: ConstTypeId,
781}
782
783impl ScalarDef {
784    /// Create a new ScalarDef with the given TypeId
785    pub const fn of<T>() -> Self {
786        ScalarDefBuilder::new()
787            .type_id(ConstTypeId::of::<T>())
788            .build()
789    }
790}
791
792/// Builder for ScalarDef
793#[derive(Default)]
794pub struct ScalarDefBuilder {
795    type_id: Option<ConstTypeId>,
796}
797
798impl ScalarDefBuilder {
799    /// Creates a new ScalarDefBuilder
800    #[allow(clippy::new_without_default)]
801    pub const fn new() -> Self {
802        Self { type_id: None }
803    }
804
805    /// Sets the type_id for the ScalarDef
806    pub const fn type_id(mut self, type_id: ConstTypeId) -> Self {
807        self.type_id = Some(type_id);
808        self
809    }
810
811    /// Builds the ScalarDef
812    pub const fn build(self) -> ScalarDef {
813        ScalarDef {
814            type_id: self.type_id.unwrap(),
815        }
816    }
817}
818
819/// The definition of a shape: is it more like a struct, a map, a list?
820#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
821#[non_exhaustive]
822pub enum Def {
823    /// Scalar — those don't have a def, they're not composed of other things.
824    /// You can interact with them through [`ValueVTable`].
825    ///
826    /// e.g. `u32`, `String`, `bool`, `SocketAddr`, etc.
827    Scalar(ScalarDef),
828
829    /// Various kinds of structs, see [`StructKind`]
830    ///
831    /// e.g. `struct Struct { field: u32 }`, `struct TupleStruct(u32, u32);`, `(u32, u32)`
832    Struct(StructDef),
833
834    /// Map — keys are dynamic (and strings, sorry), values are homogeneous
835    ///
836    /// e.g. `Map<String, T>`
837    Map(MapDef),
838
839    /// Ordered list of heterogenous values, variable size
840    ///
841    /// e.g. `Vec<T>`
842    List(ListDef),
843
844    /// Enum with variants
845    ///
846    /// e.g. `enum Enum { Variant1, Variant2 }`
847    Enum(EnumDef),
848}
849
850/// A characteristic a shape can have
851#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
852#[non_exhaustive]
853pub enum Characteristic {
854    // Marker traits
855    /// Implements Send
856    Send,
857
858    /// Implements Sync
859    Sync,
860
861    /// Implements Copy
862    Copy,
863
864    /// Implements Eq
865    Eq,
866
867    // Functionality traits
868    /// Implements Clone
869    Clone,
870
871    /// Implements Debug
872    Debug,
873
874    /// Implements PartialEq
875    PartialEq,
876
877    /// Implements PartialOrd
878    PartialOrd,
879
880    /// Implements Ord
881    Ord,
882
883    /// Implements Hash
884    Hash,
885
886    /// Implements Default
887    Default,
888}
889
890impl Characteristic {
891    /// Checks if all shapes have the given characteristic.
892    pub const fn all(self, shapes: &'static [&'static Shape]) -> bool {
893        let mut i = 0;
894        while i < shapes.len() {
895            if !shapes[i].is(self) {
896                return false;
897            }
898            i += 1;
899        }
900        true
901    }
902
903    /// Checks if any shape has the given characteristic.
904    pub const fn any(self, shapes: &'static [&'static Shape]) -> bool {
905        let mut i = 0;
906        while i < shapes.len() {
907            if shapes[i].is(self) {
908                return true;
909            }
910            i += 1;
911        }
912        false
913    }
914
915    /// Checks if none of the shapes have the given characteristic.
916    pub const fn none(self, shapes: &'static [&'static Shape]) -> bool {
917        let mut i = 0;
918        while i < shapes.len() {
919            if shapes[i].is(self) {
920                return false;
921            }
922            i += 1;
923        }
924        true
925    }
926}