facet_core/types/
mod.rs

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