facet_reflect/peek/
enum_.rs

1use facet_core::{EnumDef, EnumRepr, Field, FieldAttribute, Shape, Variant};
2
3use crate::Peek;
4
5/// Lets you read from an enum (implements read-only enum operations)
6#[derive(Clone, Copy)]
7pub struct PeekEnum<'mem> {
8    /// The internal data storage for the enum
9    ///
10    /// Note that this stores both the discriminant and the variant data
11    /// (if any), and the layout depends on the enum representation.
12    pub(crate) value: Peek<'mem>,
13
14    /// The definition of the enum.
15    pub(crate) def: EnumDef,
16}
17
18/// Returns the enum definition if the shape represents an enum, None otherwise
19pub fn peek_enum(shape: &'static Shape) -> Option<EnumDef> {
20    match shape.def {
21        facet_core::Def::Enum(enum_def) => Some(enum_def),
22        _ => None,
23    }
24}
25
26/// Returns the enum representation if the shape represents an enum, None otherwise
27pub fn peek_enum_repr(shape: &'static Shape) -> Option<EnumRepr> {
28    peek_enum(shape).map(|enum_def| enum_def.repr)
29}
30
31/// Returns the enum variants if the shape represents an enum, None otherwise
32pub fn peek_enum_variants(shape: &'static Shape) -> Option<&'static [Variant]> {
33    peek_enum(shape).map(|enum_def| enum_def.variants)
34}
35
36impl<'mem> core::ops::Deref for PeekEnum<'mem> {
37    type Target = Peek<'mem>;
38
39    #[inline(always)]
40    fn deref(&self) -> &Self::Target {
41        &self.value
42    }
43}
44
45impl<'mem> PeekEnum<'mem> {
46    /// Returns the enum definition
47    #[inline(always)]
48    pub fn def(self) -> EnumDef {
49        self.def
50    }
51
52    /// Returns the enum representation
53    #[inline(always)]
54    pub fn repr(self) -> EnumRepr {
55        self.def.repr
56    }
57
58    /// Returns the enum variants
59    #[inline(always)]
60    pub fn variants(self) -> &'static [Variant] {
61        self.def.variants
62    }
63
64    /// Returns the number of variants in this enum
65    #[inline(always)]
66    pub fn variant_count(self) -> usize {
67        self.def.variants.len()
68    }
69
70    /// Returns the variant name at the given index
71    #[inline(always)]
72    pub fn variant_name(self, index: usize) -> Option<&'static str> {
73        self.def.variants.get(index).map(|variant| variant.name)
74    }
75
76    /// Returns the discriminant value for the current enum value
77    #[inline]
78    pub fn discriminant(self) -> i64 {
79        // Read the discriminant based on the enum representation
80        unsafe {
81            let data = self.value.data();
82            match self.def.repr {
83                EnumRepr::U8 => data.read::<u8>() as i64,
84                EnumRepr::U16 => data.read::<u16>() as i64,
85                EnumRepr::U32 => data.read::<u32>() as i64,
86                EnumRepr::U64 => data.read::<u64>() as i64,
87                EnumRepr::USize => data.read::<usize>() as i64,
88                EnumRepr::I8 => data.read::<i8>() as i64,
89                EnumRepr::I16 => data.read::<i16>() as i64,
90                EnumRepr::I32 => data.read::<i32>() as i64,
91                EnumRepr::I64 => data.read::<i64>(),
92                EnumRepr::ISize => data.read::<isize>() as i64,
93                _ => {
94                    // Default to a reasonable size for other representations that might be added in the future
95                    data.read::<u32>() as i64
96                }
97            }
98        }
99    }
100
101    /// Returns the variant index for this enum value
102    #[inline]
103    pub fn variant_index(self) -> usize {
104        let discriminant = self.discriminant();
105
106        // Find the variant with matching discriminant using position method
107        self.def
108            .variants
109            .iter()
110            .position(|variant| variant.discriminant == discriminant)
111            .expect("No variant found with matching discriminant")
112    }
113
114    /// Returns the active variant
115    #[inline]
116    pub fn active_variant(self) -> &'static Variant {
117        let index = self.variant_index();
118        &self.def.variants[index]
119    }
120
121    /// Returns the name of the active variant for this enum value
122    #[inline]
123    pub fn variant_name_active(self) -> &'static str {
124        self.active_variant().name
125    }
126
127    // variant_data has been removed to reduce unsafe code exposure
128
129    /// Returns a PeekValue handle to a field of a tuple or struct variant by index
130    pub fn field(self, index: usize) -> Option<Peek<'mem>> {
131        let variant = self.active_variant();
132        let fields = &variant.data.fields;
133
134        if index >= fields.len() {
135            return None;
136        }
137
138        let field = &fields[index];
139        let field_data = unsafe { self.value.data().field(field.offset) };
140        Some(Peek {
141            data: field_data,
142            shape: field.shape(),
143        })
144    }
145
146    /// Returns the index of a field in the active variant by name
147    pub fn field_index(self, field_name: &str) -> Option<usize> {
148        let variant = self.active_variant();
149        variant
150            .data
151            .fields
152            .iter()
153            .position(|f| f.name == field_name)
154    }
155
156    /// Returns a PeekValue handle to a field of a tuple or struct variant by name
157    pub fn field_by_name(self, field_name: &str) -> Option<Peek<'mem>> {
158        let index = self.field_index(field_name)?;
159        self.field(index)
160    }
161
162    /// Iterates over all fields in this enum variant, providing both field metadata and value
163    #[inline]
164    pub fn fields(self) -> impl Iterator<Item = (&'static Field, Peek<'mem>)> {
165        let variant = self.active_variant();
166        let fields = &variant.data.fields;
167
168        // Create an iterator that maps each field to a (Field, PeekValue) pair
169        fields.iter().map(move |field| {
170            let field_data = unsafe { self.value.data().field(field.offset) };
171            let peek = Peek {
172                data: field_data,
173                shape: field.shape(),
174            };
175            (field, peek)
176        })
177    }
178
179    /// Iterates over thos fields in this struct that should be included when it is serialized.
180    #[inline]
181    pub fn fields_for_serialize(&self) -> impl Iterator<Item = (&'static Field, Peek<'mem>)> + '_ {
182        self.fields().filter(|(field, peek)| {
183            for attr in field.attributes {
184                match attr {
185                    FieldAttribute::SkipSerializingIf(fn_ptr) => {
186                        if unsafe { fn_ptr(peek.data()) } {
187                            return false;
188                        }
189                    }
190                    FieldAttribute::SkipSerializing => {
191                        return false;
192                    }
193                    _ => {}
194                }
195            }
196            true
197        })
198    }
199}