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    /// the kind of struct (e.g. struct, tuple struct, tuple)
202    pub kind: StructKind,
203
204    /// all fields, in declaration order (not necessarily in memory order)
205    pub fields: &'static [Field],
206}
207
208/// Describes the kind of struct (useful for deserializing)
209#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
210pub enum StructKind {
211    /// struct S { t0: T0, t1: T1 }
212    Struct,
213
214    /// struct TupleStruct(T0, T1);
215    TupleStruct,
216
217    /// (T0, T1)
218    Tuple,
219}
220
221/// Describes a field in a struct or tuple
222#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
223pub struct Field {
224    /// key for the struct field (for tuples and tuple-structs, this is the 0-based index)
225    pub name: &'static str,
226
227    /// schema of the inner type
228    pub shape: &'static Shape,
229
230    /// offset of the field in the struct (obtained through `std::mem::offset_of`)
231    pub offset: usize,
232
233    /// flags for the field (e.g. sensitive, etc.)
234    pub flags: FieldFlags,
235}
236
237bitflags::bitflags! {
238    /// Flags that can be applied to fields to modify their behavior
239    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
240    pub struct FieldFlags: u64 {
241        /// An empty set of flags
242        const EMPTY = 0;
243
244        /// Flag indicating this field contains sensitive data that should not be displayed
245        const SENSITIVE = 1 << 0;
246    }
247}
248
249impl Default for FieldFlags {
250    #[inline(always)]
251    fn default() -> Self {
252        Self::EMPTY
253    }
254}
255
256impl std::fmt::Display for FieldFlags {
257    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258        if self.is_empty() {
259            return write!(f, "none");
260        }
261
262        // Define a vector of flag entries: (flag, name)
263        let flags = [
264            (FieldFlags::SENSITIVE, "sensitive"),
265            // Future flags can be easily added here:
266            // (FieldFlags::SOME_FLAG, "some_flag"),
267            // (FieldFlags::ANOTHER_FLAG, "another_flag"),
268        ];
269
270        // Write all active flags with proper separators
271        let mut is_first = true;
272        for (flag, name) in flags {
273            if self.contains(flag) {
274                if !is_first {
275                    write!(f, ", ")?;
276                }
277                is_first = false;
278                write!(f, "{}", name)?;
279            }
280        }
281
282        Ok(())
283    }
284}
285
286/// Fields for map types
287#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
288pub struct MapDef {
289    /// vtable for interacting with the map
290    pub vtable: &'static MapVTable,
291    /// shape of the keys in the map
292    pub k: &'static Shape,
293    /// shape of the values in the map
294    pub v: &'static Shape,
295}
296
297/// Fields for list types
298#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
299pub struct ListDef {
300    /// vtable for interacting with the list
301    pub vtable: &'static ListVTable,
302    /// shape of the items in the list
303    pub t: &'static Shape,
304}
305
306/// Fields for enum types
307#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
308pub struct EnumDef {
309    /// representation of the enum (u8, u16, etc.)
310    pub repr: EnumRepr,
311    /// all variants for this enum
312    pub variants: &'static [Variant],
313}
314
315/// Describes a variant of an enum
316#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
317pub struct Variant {
318    /// Name of the variant
319    pub name: &'static str,
320
321    /// Discriminant value (if available)
322    pub discriminant: Option<i64>,
323
324    /// Kind of variant (unit, tuple, or struct)
325    pub kind: VariantKind,
326}
327
328/// Represents the different kinds of variants that can exist in a Rust enum
329#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
330pub enum VariantKind {
331    /// Unit variant (e.g., `None` in Option)
332    Unit,
333
334    /// Tuple variant with unnamed fields (e.g., `Some(T)` in Option)
335    Tuple {
336        /// List of fields contained in the tuple variant
337        fields: &'static [Field],
338    },
339
340    /// Struct variant with named fields (e.g., `Struct { field: T }`)
341    Struct {
342        /// List of fields contained in the struct variant
343        fields: &'static [Field],
344    },
345}
346
347/// All possible representations for Rust enums
348#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
349pub enum EnumRepr {
350    /// Default representation (compiler-dependent)
351    Default,
352    /// u8 representation (#[repr(u8)])
353    U8,
354    /// u16 representation (#[repr(u16)])
355    U16,
356    /// u32 representation (#[repr(u32)])
357    U32,
358    /// u64 representation (#[repr(u64)])
359    U64,
360    /// usize representation (#[repr(usize)])
361    USize,
362    /// i8 representation (#[repr(i8)])
363    I8,
364    /// i16 representation (#[repr(i16)])
365    I16,
366    /// i32 representation (#[repr(i32)])
367    I32,
368    /// i64 representation (#[repr(i64)])
369    I64,
370    /// isize representation (#[repr(isize)])
371    ISize,
372}
373
374impl Default for EnumRepr {
375    fn default() -> Self {
376        Self::Default
377    }
378}
379
380/// Definition for scalar types
381#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
382pub struct ScalarDef {
383    /// The TypeId of the scalar type
384    pub type_id: ConstTypeId,
385}
386
387impl ScalarDef {
388    /// Create a new ScalarDef with the given TypeId
389    pub const fn of<T>() -> Self {
390        Self {
391            type_id: ConstTypeId::of::<T>(),
392        }
393    }
394}
395
396/// The definition of a shape: is it more like a struct, a map, a list?
397#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
398pub enum Def {
399    /// Scalar — those don't have a def, they're not composed of other things.
400    /// You can interact with them through [`ValueVTable`].
401    ///
402    /// e.g. `u32`, `String`, `bool`, `SocketAddr`, etc.
403    Scalar(ScalarDef),
404
405    /// Various kinds of structs, see [`StructKind`]
406    ///
407    /// e.g. `struct Struct { field: u32 }`, `struct TupleStruct(u32, u32);`, `(u32, u32)`
408    Struct(StructDef),
409
410    /// Map — keys are dynamic (and strings, sorry), values are homogeneous
411    ///
412    /// e.g. `Map<String, T>`
413    Map(MapDef),
414
415    /// Ordered list of heterogenous values, variable size
416    ///
417    /// e.g. `Vec<T>`
418    List(ListDef),
419
420    /// Enum with variants
421    ///
422    /// e.g. `enum Enum { Variant1, Variant2 }`
423    Enum(EnumDef),
424}
425
426/// A characteristic a shape can have
427#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
428pub enum Characteristic {
429    // Marker traits
430    /// Implements Send
431    Send,
432
433    /// Implements Sync
434    Sync,
435
436    /// Implements Copy
437    Copy,
438
439    /// Implements Eq
440    Eq,
441
442    // Functionality traits
443    /// Implements Clone
444    Clone,
445
446    /// Implements Debug
447    Debug,
448
449    /// Implements PartialEq
450    PartialEq,
451
452    /// Implements PartialOrd
453    PartialOrd,
454
455    /// Implements Ord
456    Ord,
457
458    /// Implements Hash
459    Hash,
460
461    /// Implements Default
462    Default,
463}
464
465impl Characteristic {
466    /// Checks if all shapes have the given characteristic.
467    pub const fn all(self, shapes: &'static [&'static Shape]) -> bool {
468        let mut i = 0;
469        while i < shapes.len() {
470            if !shapes[i].is(self) {
471                return false;
472            }
473            i += 1;
474        }
475        true
476    }
477
478    /// Checks if any shape has the given characteristic.
479    pub const fn any(self, shapes: &'static [&'static Shape]) -> bool {
480        let mut i = 0;
481        while i < shapes.len() {
482            if shapes[i].is(self) {
483                return true;
484            }
485            i += 1;
486        }
487        false
488    }
489
490    /// Checks if none of the shapes have the given characteristic.
491    pub const fn none(self, shapes: &'static [&'static Shape]) -> bool {
492        let mut i = 0;
493        while i < shapes.len() {
494            if shapes[i].is(self) {
495                return false;
496            }
497            i += 1;
498        }
499        true
500    }
501}