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::fmt;
9use std::alloc::Layout;
10
11use facet_opaque::OpaqueUninit;
12use typeid::ConstTypeId;
13
14mod list;
15pub use list::*;
16
17mod map;
18pub use map::*;
19
20mod value;
21pub use value::*;
22
23/// Schema for reflection of a type
24#[derive(Clone, Copy, Debug)]
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
120impl PartialEq for Shape {
121    fn eq(&self, other: &Self) -> bool {
122        self.def == other.def && self.layout == other.layout
123    }
124}
125
126impl Eq for Shape {}
127
128impl std::hash::Hash for Shape {
129    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
130        self.def.hash(state);
131        self.layout.hash(state);
132    }
133}
134
135impl Shape {
136    /// Check if this shape is of the given type
137    pub fn is_shape(&'static self, other: &'static Shape) -> bool {
138        self == other
139    }
140
141    /// Assert that this shape is equal to the given shape, panicking if it's not
142    pub fn assert_shape(&'static self, other: &'static Shape) {
143        assert!(
144            self.is_shape(other),
145            "Shape mismatch: expected {other}, found {self}",
146        );
147    }
148}
149
150// Helper struct to format the name for display
151impl std::fmt::Display for Shape {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        (self.vtable.type_name)(f, TypeNameOpts::default())
154    }
155}
156
157impl Shape {
158    /// Heap-allocate a value of this shape
159    #[inline]
160    pub fn allocate(&self) -> OpaqueUninit<'static> {
161        OpaqueUninit::new(unsafe { std::alloc::alloc(self.layout) })
162    }
163}
164
165/// Errors encountered when calling `field_by_index` or `field_by_name`
166#[derive(Debug, Copy, Clone, PartialEq, Eq)]
167pub enum FieldError {
168    /// `field_by_index` was called on a dynamic collection, that has no
169    /// static fields. a map doesn't have a "first field", it can only
170    /// associate by keys.
171    NoStaticFields,
172
173    /// `field_by_name` was called on a struct, and there is no static field
174    /// with the given key.
175    NoSuchStaticField,
176
177    /// `field_by_index` was called on a fixed-size collection (like a tuple,
178    /// a struct, or a fixed-size array) and the index was out of bounds.
179    IndexOutOfBounds,
180
181    /// `field_by_index` or `field_by_name` was called on a non-struct type.
182    NotAStruct,
183}
184
185impl std::error::Error for FieldError {}
186
187impl std::fmt::Display for FieldError {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        match self {
190            FieldError::NoStaticFields => write!(f, "No static fields available"),
191            FieldError::NoSuchStaticField => write!(f, "No such static field"),
192            FieldError::IndexOutOfBounds => write!(f, "Index out of bounds"),
193            FieldError::NotAStruct => write!(f, "Not a struct"),
194        }
195    }
196}
197
198/// Common fields for struct-like types
199#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
200pub struct StructDef {
201    /// all fields, in declaration order (not necessarily in memory order)
202    pub fields: &'static [Field],
203}
204
205/// Describes a field in a struct or tuple
206#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
207pub struct Field {
208    /// key for the struct field (for tuples and tuple-structs, this is the 0-based index)
209    pub name: &'static str,
210
211    /// schema of the inner type
212    pub shape: &'static Shape,
213
214    /// offset of the field in the struct (obtained through `std::mem::offset_of`)
215    pub offset: usize,
216
217    /// flags for the field (e.g. sensitive, etc.)
218    pub flags: FieldFlags,
219}
220
221bitflags::bitflags! {
222    /// Flags that can be applied to fields to modify their behavior
223    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
224    pub struct FieldFlags: u64 {
225        /// An empty set of flags
226        const EMPTY = 0;
227
228        /// Flag indicating this field contains sensitive data that should not be displayed
229        const SENSITIVE = 1 << 0;
230    }
231}
232
233impl Default for FieldFlags {
234    #[inline(always)]
235    fn default() -> Self {
236        Self::EMPTY
237    }
238}
239
240impl std::fmt::Display for FieldFlags {
241    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242        if self.is_empty() {
243            return write!(f, "none");
244        }
245
246        // Define a vector of flag entries: (flag, name)
247        let flags = [
248            (FieldFlags::SENSITIVE, "sensitive"),
249            // Future flags can be easily added here:
250            // (FieldFlags::SOME_FLAG, "some_flag"),
251            // (FieldFlags::ANOTHER_FLAG, "another_flag"),
252        ];
253
254        // Write all active flags with proper separators
255        let mut is_first = true;
256        for (flag, name) in flags {
257            if self.contains(flag) {
258                if !is_first {
259                    write!(f, ", ")?;
260                }
261                is_first = false;
262                write!(f, "{}", name)?;
263            }
264        }
265
266        Ok(())
267    }
268}
269
270/// Fields for map types
271#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
272pub struct MapDef {
273    /// vtable for interacting with the map
274    pub vtable: &'static MapVTable,
275    /// shape of the keys in the map
276    pub k: &'static Shape,
277    /// shape of the values in the map
278    pub v: &'static Shape,
279}
280
281/// Fields for list types
282#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
283pub struct ListDef {
284    /// vtable for interacting with the list
285    pub vtable: &'static ListVTable,
286    /// shape of the items in the list
287    pub t: &'static Shape,
288}
289
290/// Fields for enum types
291#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
292pub struct EnumDef {
293    /// representation of the enum (u8, u16, etc.)
294    pub repr: EnumRepr,
295    /// all variants for this enum
296    pub variants: &'static [Variant],
297}
298
299/// Describes a variant of an enum
300#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
301pub struct Variant {
302    /// Name of the variant
303    pub name: &'static str,
304
305    /// Discriminant value (if available)
306    pub discriminant: Option<i64>,
307
308    /// Kind of variant (unit, tuple, or struct)
309    pub kind: VariantKind,
310}
311
312/// Represents the different kinds of variants that can exist in a Rust enum
313#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
314pub enum VariantKind {
315    /// Unit variant (e.g., `None` in Option)
316    Unit,
317
318    /// Tuple variant with unnamed fields (e.g., `Some(T)` in Option)
319    Tuple {
320        /// List of fields contained in the tuple variant
321        fields: &'static [Field],
322    },
323
324    /// Struct variant with named fields (e.g., `Struct { field: T }`)
325    Struct {
326        /// List of fields contained in the struct variant
327        fields: &'static [Field],
328    },
329}
330
331/// All possible representations for Rust enums
332#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
333pub enum EnumRepr {
334    /// Default representation (compiler-dependent)
335    Default,
336    /// u8 representation (#[repr(u8)])
337    U8,
338    /// u16 representation (#[repr(u16)])
339    U16,
340    /// u32 representation (#[repr(u32)])
341    U32,
342    /// u64 representation (#[repr(u64)])
343    U64,
344    /// usize representation (#[repr(usize)])
345    USize,
346    /// i8 representation (#[repr(i8)])
347    I8,
348    /// i16 representation (#[repr(i16)])
349    I16,
350    /// i32 representation (#[repr(i32)])
351    I32,
352    /// i64 representation (#[repr(i64)])
353    I64,
354    /// isize representation (#[repr(isize)])
355    ISize,
356}
357
358impl Default for EnumRepr {
359    fn default() -> Self {
360        Self::Default
361    }
362}
363
364/// Definition for scalar types
365#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
366pub struct ScalarDef {
367    /// The TypeId of the scalar type
368    pub type_id: ConstTypeId,
369}
370
371impl ScalarDef {
372    /// Create a new ScalarDef with the given TypeId
373    pub const fn of<T>() -> Self {
374        Self {
375            type_id: ConstTypeId::of::<T>(),
376        }
377    }
378}
379
380/// The definition of a shape: is it more like a struct, a map, a list?
381#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
382pub enum Def {
383    /// Scalar — those don't have a def, they're not composed of other things.
384    /// You can interact with them through [`ValueVTable`].
385    ///
386    /// e.g. `u32`, `String`, `bool`, `SocketAddr`, etc.
387    Scalar(ScalarDef),
388
389    /// Struct with statically-known, named fields
390    ///
391    /// e.g. `struct Struct { field: u32 }`
392    Struct(StructDef),
393
394    /// Tuple-struct, with numbered fields
395    ///
396    /// e.g. `struct TupleStruct(u32, u32);`
397    TupleStruct(StructDef),
398
399    /// Tuple, with numbered fields
400    ///
401    /// e.g. `(u32, u32);`
402    Tuple(StructDef),
403
404    /// Map — keys are dynamic (and strings, sorry), values are homogeneous
405    ///
406    /// e.g. `Map<String, T>`
407    Map(MapDef),
408
409    /// Ordered list of heterogenous values, variable size
410    ///
411    /// e.g. `Vec<T>`
412    List(ListDef),
413
414    /// Enum with variants
415    ///
416    /// e.g. `enum Enum { Variant1, Variant2 }`
417    Enum(EnumDef),
418}
419
420/// A characteristic a shape can have
421#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
422pub enum Characteristic {
423    // Marker traits
424    /// Implements Send
425    Send,
426
427    /// Implements Sync
428    Sync,
429
430    /// Implements Copy
431    Copy,
432
433    /// Implements Eq
434    Eq,
435
436    // Functionality traits
437    /// Implements Clone
438    Clone,
439
440    /// Implements Debug
441    Debug,
442
443    /// Implements PartialEq
444    PartialEq,
445
446    /// Implements PartialOrd
447    PartialOrd,
448
449    /// Implements Ord
450    Ord,
451
452    /// Implements Hash
453    Hash,
454
455    /// Implements Default
456    Default,
457}
458
459impl Characteristic {
460    /// Checks if all shapes have the given characteristic.
461    pub const fn all(self, shapes: &'static [&'static Shape]) -> bool {
462        let mut i = 0;
463        while i < shapes.len() {
464            if !shapes[i].is(self) {
465                return false;
466            }
467            i += 1;
468        }
469        true
470    }
471
472    /// Checks if any shape has the given characteristic.
473    pub const fn any(self, shapes: &'static [&'static Shape]) -> bool {
474        let mut i = 0;
475        while i < shapes.len() {
476            if shapes[i].is(self) {
477                return true;
478            }
479            i += 1;
480        }
481        false
482    }
483
484    /// Checks if none of the shapes have the given characteristic.
485    pub const fn none(self, shapes: &'static [&'static Shape]) -> bool {
486        let mut i = 0;
487        while i < shapes.len() {
488            if shapes[i].is(self) {
489                return false;
490            }
491            i += 1;
492        }
493        true
494    }
495}