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
345impl Field {
346    /// Returns a builder for Field
347    pub const fn builder() -> FieldBuilder {
348        FieldBuilder::new()
349    }
350}
351
352/// Builder for Field
353pub struct FieldBuilder {
354    name: Option<&'static str>,
355    shape: Option<&'static Shape>,
356    offset: Option<usize>,
357    flags: Option<FieldFlags>,
358}
359
360impl FieldBuilder {
361    /// Creates a new FieldBuilder
362    #[allow(clippy::new_without_default)]
363    pub const fn new() -> Self {
364        Self {
365            name: None,
366            shape: None,
367            offset: None,
368            flags: None,
369        }
370    }
371
372    /// Sets the name for the Field
373    pub const fn name(mut self, name: &'static str) -> Self {
374        self.name = Some(name);
375        self
376    }
377
378    /// Sets the shape for the Field
379    pub const fn shape(mut self, shape: &'static Shape) -> Self {
380        self.shape = Some(shape);
381        self
382    }
383
384    /// Sets the offset for the Field
385    pub const fn offset(mut self, offset: usize) -> Self {
386        self.offset = Some(offset);
387        self
388    }
389
390    /// Sets the flags for the Field
391    pub const fn flags(mut self, flags: FieldFlags) -> Self {
392        self.flags = Some(flags);
393        self
394    }
395
396    /// Builds the Field
397    pub const fn build(self) -> Field {
398        Field {
399            name: self.name.unwrap(),
400            shape: self.shape.unwrap(),
401            offset: self.offset.unwrap(),
402            flags: match self.flags {
403                Some(flags) => flags,
404                None => FieldFlags::EMPTY,
405            },
406        }
407    }
408}
409
410bitflags::bitflags! {
411    /// Flags that can be applied to fields to modify their behavior
412    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
413    pub struct FieldFlags: u64 {
414        /// An empty set of flags
415        const EMPTY = 0;
416
417        /// Flag indicating this field contains sensitive data that should not be displayed
418        const SENSITIVE = 1 << 0;
419    }
420}
421
422impl Default for FieldFlags {
423    #[inline(always)]
424    fn default() -> Self {
425        Self::EMPTY
426    }
427}
428
429impl core::fmt::Display for FieldFlags {
430    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
431        if self.is_empty() {
432            return write!(f, "none");
433        }
434
435        // Define a vector of flag entries: (flag, name)
436        let flags = [
437            (FieldFlags::SENSITIVE, "sensitive"),
438            // Future flags can be easily added here:
439            // (FieldFlags::SOME_FLAG, "some_flag"),
440            // (FieldFlags::ANOTHER_FLAG, "another_flag"),
441        ];
442
443        // Write all active flags with proper separators
444        let mut is_first = true;
445        for (flag, name) in flags {
446            if self.contains(flag) {
447                if !is_first {
448                    write!(f, ", ")?;
449                }
450                is_first = false;
451                write!(f, "{}", name)?;
452            }
453        }
454
455        Ok(())
456    }
457}
458
459/// Fields for map types
460#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
461#[non_exhaustive]
462pub struct MapDef {
463    /// vtable for interacting with the map
464    pub vtable: &'static MapVTable,
465    /// shape of the keys in the map
466    pub k: &'static Shape,
467    /// shape of the values in the map
468    pub v: &'static Shape,
469}
470
471impl MapDef {
472    /// Returns a builder for MapDef
473    pub const fn builder() -> MapDefBuilder {
474        MapDefBuilder::new()
475    }
476}
477
478/// Builder for MapDef
479pub struct MapDefBuilder {
480    vtable: Option<&'static MapVTable>,
481    k: Option<&'static Shape>,
482    v: Option<&'static Shape>,
483}
484
485impl MapDefBuilder {
486    /// Creates a new MapDefBuilder
487    #[allow(clippy::new_without_default)]
488    pub const fn new() -> Self {
489        Self {
490            vtable: None,
491            k: None,
492            v: None,
493        }
494    }
495
496    /// Sets the vtable for the MapDef
497    pub const fn vtable(mut self, vtable: &'static MapVTable) -> Self {
498        self.vtable = Some(vtable);
499        self
500    }
501
502    /// Sets the key shape for the MapDef
503    pub const fn k(mut self, k: &'static Shape) -> Self {
504        self.k = Some(k);
505        self
506    }
507
508    /// Sets the value shape for the MapDef
509    pub const fn v(mut self, v: &'static Shape) -> Self {
510        self.v = Some(v);
511        self
512    }
513
514    /// Builds the MapDef
515    pub const fn build(self) -> MapDef {
516        MapDef {
517            vtable: self.vtable.unwrap(),
518            k: self.k.unwrap(),
519            v: self.v.unwrap(),
520        }
521    }
522}
523
524/// Fields for list types
525#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
526#[non_exhaustive]
527pub struct ListDef {
528    /// vtable for interacting with the list
529    pub vtable: &'static ListVTable,
530    /// shape of the items in the list
531    pub t: &'static Shape,
532}
533
534impl ListDef {
535    /// Returns a builder for ListDef
536    pub const fn builder() -> ListDefBuilder {
537        ListDefBuilder::new()
538    }
539}
540
541/// Builder for ListDef
542pub struct ListDefBuilder {
543    vtable: Option<&'static ListVTable>,
544    t: Option<&'static Shape>,
545}
546
547impl ListDefBuilder {
548    /// Creates a new ListDefBuilder
549    #[allow(clippy::new_without_default)]
550    pub const fn new() -> Self {
551        Self {
552            vtable: None,
553            t: None,
554        }
555    }
556
557    /// Sets the vtable for the ListDef
558    pub const fn vtable(mut self, vtable: &'static ListVTable) -> Self {
559        self.vtable = Some(vtable);
560        self
561    }
562
563    /// Sets the item shape for the ListDef
564    pub const fn t(mut self, t: &'static Shape) -> Self {
565        self.t = Some(t);
566        self
567    }
568
569    /// Builds the ListDef
570    pub const fn build(self) -> ListDef {
571        ListDef {
572            vtable: self.vtable.unwrap(),
573            t: self.t.unwrap(),
574        }
575    }
576}
577
578/// Fields for enum types
579#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
580#[non_exhaustive]
581pub struct EnumDef {
582    /// representation of the enum (u8, u16, etc.)
583    pub repr: EnumRepr,
584    /// all variants for this enum
585    pub variants: &'static [Variant],
586}
587
588impl EnumDef {
589    /// Returns a builder for EnumDef
590    pub const fn builder() -> EnumDefBuilder {
591        EnumDefBuilder::new()
592    }
593}
594
595/// Builder for EnumDef
596pub struct EnumDefBuilder {
597    repr: Option<EnumRepr>,
598    variants: Option<&'static [Variant]>,
599}
600
601impl EnumDefBuilder {
602    /// Creates a new EnumDefBuilder
603    #[allow(clippy::new_without_default)]
604    pub const fn new() -> Self {
605        Self {
606            repr: None,
607            variants: None,
608        }
609    }
610
611    /// Sets the representation for the EnumDef
612    pub const fn repr(mut self, repr: EnumRepr) -> Self {
613        self.repr = Some(repr);
614        self
615    }
616
617    /// Sets the variants for the EnumDef
618    pub const fn variants(mut self, variants: &'static [Variant]) -> Self {
619        self.variants = Some(variants);
620        self
621    }
622
623    /// Builds the EnumDef
624    pub const fn build(self) -> EnumDef {
625        EnumDef {
626            repr: self.repr.unwrap(),
627            variants: self.variants.unwrap(),
628        }
629    }
630}
631
632/// Describes a variant of an enum
633#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
634#[non_exhaustive]
635pub struct Variant {
636    /// Name of the variant
637    pub name: &'static str,
638
639    /// Discriminant value (if available)
640    pub discriminant: Option<i64>,
641
642    /// Kind of variant (unit, tuple, or struct)
643    pub kind: VariantKind,
644}
645
646impl Variant {
647    /// Returns a builder for Variant
648    pub const fn builder() -> VariantBuilder {
649        VariantBuilder::new()
650    }
651}
652
653/// Builder for Variant
654pub struct VariantBuilder {
655    name: Option<&'static str>,
656    discriminant: Option<Option<i64>>,
657    kind: Option<VariantKind>,
658}
659
660impl VariantBuilder {
661    /// Creates a new VariantBuilder
662    #[allow(clippy::new_without_default)]
663    pub const fn new() -> Self {
664        Self {
665            name: None,
666            discriminant: None,
667            kind: None,
668        }
669    }
670
671    /// Sets the name for the Variant
672    pub const fn name(mut self, name: &'static str) -> Self {
673        self.name = Some(name);
674        self
675    }
676
677    /// Sets the discriminant for the Variant
678    pub const fn discriminant(mut self, discriminant: Option<i64>) -> Self {
679        self.discriminant = Some(discriminant);
680        self
681    }
682
683    /// Sets the kind for the Variant
684    pub const fn kind(mut self, kind: VariantKind) -> Self {
685        self.kind = Some(kind);
686        self
687    }
688
689    /// Builds the Variant
690    pub const fn build(self) -> Variant {
691        Variant {
692            name: self.name.unwrap(),
693            discriminant: self.discriminant.unwrap(),
694            kind: self.kind.unwrap(),
695        }
696    }
697}
698
699/// Represents the different kinds of variants that can exist in a Rust enum
700#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
701#[non_exhaustive]
702pub enum VariantKind {
703    /// Unit variant (e.g., `None` in Option)
704    Unit,
705
706    /// Tuple variant with unnamed fields (e.g., `Some(T)` in Option)
707    Tuple {
708        /// List of fields contained in the tuple variant
709        fields: &'static [Field],
710    },
711
712    /// Struct variant with named fields (e.g., `Struct { field: T }`)
713    Struct {
714        /// List of fields contained in the struct variant
715        fields: &'static [Field],
716    },
717}
718
719/// All possible representations for Rust enums
720#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
721#[non_exhaustive]
722pub enum EnumRepr {
723    /// Default representation (compiler-dependent)
724    Default,
725    /// u8 representation (#[repr(u8)])
726    U8,
727    /// u16 representation (#[repr(u16)])
728    U16,
729    /// u32 representation (#[repr(u32)])
730    U32,
731    /// u64 representation (#[repr(u64)])
732    U64,
733    /// usize representation (#[repr(usize)])
734    USize,
735    /// i8 representation (#[repr(i8)])
736    I8,
737    /// i16 representation (#[repr(i16)])
738    I16,
739    /// i32 representation (#[repr(i32)])
740    I32,
741    /// i64 representation (#[repr(i64)])
742    I64,
743    /// isize representation (#[repr(isize)])
744    ISize,
745}
746
747impl Default for EnumRepr {
748    fn default() -> Self {
749        Self::Default
750    }
751}
752
753/// Definition for scalar types
754#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
755#[non_exhaustive]
756pub struct ScalarDef {
757    /// The TypeId of the scalar type
758    pub type_id: ConstTypeId,
759}
760
761impl ScalarDef {
762    /// Create a new ScalarDef with the given TypeId
763    pub const fn of<T>() -> Self {
764        ScalarDefBuilder::new()
765            .type_id(ConstTypeId::of::<T>())
766            .build()
767    }
768}
769
770/// Builder for ScalarDef
771#[derive(Default)]
772pub struct ScalarDefBuilder {
773    type_id: Option<ConstTypeId>,
774}
775
776impl ScalarDefBuilder {
777    /// Creates a new ScalarDefBuilder
778    #[allow(clippy::new_without_default)]
779    pub const fn new() -> Self {
780        Self { type_id: None }
781    }
782
783    /// Sets the type_id for the ScalarDef
784    pub const fn type_id(mut self, type_id: ConstTypeId) -> Self {
785        self.type_id = Some(type_id);
786        self
787    }
788
789    /// Builds the ScalarDef
790    pub const fn build(self) -> ScalarDef {
791        ScalarDef {
792            type_id: self.type_id.unwrap(),
793        }
794    }
795}
796
797/// The definition of a shape: is it more like a struct, a map, a list?
798#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
799#[non_exhaustive]
800pub enum Def {
801    /// Scalar — those don't have a def, they're not composed of other things.
802    /// You can interact with them through [`ValueVTable`].
803    ///
804    /// e.g. `u32`, `String`, `bool`, `SocketAddr`, etc.
805    Scalar(ScalarDef),
806
807    /// Various kinds of structs, see [`StructKind`]
808    ///
809    /// e.g. `struct Struct { field: u32 }`, `struct TupleStruct(u32, u32);`, `(u32, u32)`
810    Struct(StructDef),
811
812    /// Map — keys are dynamic (and strings, sorry), values are homogeneous
813    ///
814    /// e.g. `Map<String, T>`
815    Map(MapDef),
816
817    /// Ordered list of heterogenous values, variable size
818    ///
819    /// e.g. `Vec<T>`
820    List(ListDef),
821
822    /// Enum with variants
823    ///
824    /// e.g. `enum Enum { Variant1, Variant2 }`
825    Enum(EnumDef),
826}
827
828/// A characteristic a shape can have
829#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
830#[non_exhaustive]
831pub enum Characteristic {
832    // Marker traits
833    /// Implements Send
834    Send,
835
836    /// Implements Sync
837    Sync,
838
839    /// Implements Copy
840    Copy,
841
842    /// Implements Eq
843    Eq,
844
845    // Functionality traits
846    /// Implements Clone
847    Clone,
848
849    /// Implements Debug
850    Debug,
851
852    /// Implements PartialEq
853    PartialEq,
854
855    /// Implements PartialOrd
856    PartialOrd,
857
858    /// Implements Ord
859    Ord,
860
861    /// Implements Hash
862    Hash,
863
864    /// Implements Default
865    Default,
866}
867
868impl Characteristic {
869    /// Checks if all shapes have the given characteristic.
870    pub const fn all(self, shapes: &'static [&'static Shape]) -> bool {
871        let mut i = 0;
872        while i < shapes.len() {
873            if !shapes[i].is(self) {
874                return false;
875            }
876            i += 1;
877        }
878        true
879    }
880
881    /// Checks if any shape has the given characteristic.
882    pub const fn any(self, shapes: &'static [&'static Shape]) -> bool {
883        let mut i = 0;
884        while i < shapes.len() {
885            if shapes[i].is(self) {
886                return true;
887            }
888            i += 1;
889        }
890        false
891    }
892
893    /// Checks if none of the shapes have the given characteristic.
894    pub const fn none(self, shapes: &'static [&'static Shape]) -> bool {
895        let mut i = 0;
896        while i < shapes.len() {
897            if shapes[i].is(self) {
898                return false;
899            }
900            i += 1;
901        }
902        true
903    }
904}