facet_reflect/peek/
enum_.rs

1use facet_core::{EnumDef, EnumRepr, OpaqueConst, Shape, Variant, VariantKind};
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5#[cfg(feature = "alloc")]
6use alloc::boxed::Box;
7
8/// Lets you read from an enum (implements read-only enum operations)
9#[derive(Clone, Copy)]
10pub struct PeekEnum<'mem> {
11    /// The internal data storage for the enum
12    ///
13    /// Note that this stores both the discriminant and the variant data
14    /// (if any), and the layout depends on the enum representation.
15    /// Use [`Self::variant_data`] to get a pointer to the variant data.
16    value: crate::PeekValue<'mem>,
17    def: EnumDef,
18}
19
20/// Returns the enum definition if the shape represents an enum, None otherwise
21pub fn peek_enum(shape: &'static Shape) -> Option<EnumDef> {
22    match shape.def {
23        facet_core::Def::Enum(enum_def) => Some(enum_def),
24        _ => None,
25    }
26}
27
28/// Returns the enum representation if the shape represents an enum, None otherwise
29pub fn peek_enum_repr(shape: &'static Shape) -> Option<EnumRepr> {
30    peek_enum(shape).map(|enum_def| enum_def.repr)
31}
32
33/// Returns the enum variants if the shape represents an enum, None otherwise
34pub fn peek_enum_variants(shape: &'static Shape) -> Option<&'static [Variant]> {
35    peek_enum(shape).map(|enum_def| enum_def.variants)
36}
37
38impl<'mem> core::ops::Deref for PeekEnum<'mem> {
39    type Target = crate::PeekValue<'mem>;
40
41    #[inline(always)]
42    fn deref(&self) -> &Self::Target {
43        &self.value
44    }
45}
46
47impl<'mem> PeekEnum<'mem> {
48    /// Create a new peek enum
49    pub(crate) fn new(value: crate::PeekValue<'mem>, def: EnumDef) -> Self {
50        Self { value, def }
51    }
52
53    /// Returns the enum definition
54    #[inline(always)]
55    pub fn def(self) -> EnumDef {
56        self.def
57    }
58
59    /// Returns the enum representation
60    #[inline(always)]
61    pub fn repr(self) -> EnumRepr {
62        self.def.repr
63    }
64
65    /// Returns the enum variants
66    #[inline(always)]
67    pub fn variants(self) -> &'static [Variant] {
68        self.def.variants
69    }
70
71    /// Returns the number of variants in this enum
72    #[inline(always)]
73    pub fn variant_count(self) -> usize {
74        self.def.variants.len()
75    }
76
77    /// Returns the variant name at the given index
78    #[inline(always)]
79    pub fn variant_name(self, index: usize) -> Option<&'static str> {
80        self.def.variants.get(index).map(|variant| variant.name)
81    }
82
83    /// Returns the discriminant value for the current enum value
84    #[inline]
85    pub fn discriminant(self) -> i64 {
86        // Read the discriminant based on the enum representation
87        unsafe {
88            let data = self.value.data();
89            match self.def.repr {
90                EnumRepr::U8 => data.read::<u8>() as i64,
91                EnumRepr::U16 => data.read::<u16>() as i64,
92                EnumRepr::U32 => data.read::<u32>() as i64,
93                EnumRepr::U64 => data.read::<u64>() as i64,
94                EnumRepr::USize => data.read::<usize>() as i64,
95                EnumRepr::I8 => data.read::<i8>() as i64,
96                EnumRepr::I16 => data.read::<i16>() as i64,
97                EnumRepr::I32 => data.read::<i32>() as i64,
98                EnumRepr::I64 => data.read::<i64>(),
99                EnumRepr::ISize => data.read::<isize>() as i64,
100                _ => {
101                    // Default to a reasonable size for other representations that might be added in the future
102                    data.read::<u32>() as i64
103                }
104            }
105        }
106    }
107
108    /// Returns the variant index for this enum value
109    #[inline]
110    pub fn variant_index(self) -> usize {
111        let discriminant = self.discriminant();
112
113        // Find the variant with matching discriminant
114        for (index, variant) in self.def.variants.iter().enumerate() {
115            let variant_discriminant = match variant.discriminant {
116                Some(value) => value,
117                None => index as i64,
118            };
119
120            if variant_discriminant == discriminant {
121                return index;
122            }
123        }
124
125        // This should never happen for valid enums
126        panic!("Invalid discriminant value for enum")
127    }
128
129    /// Returns the active variant
130    #[inline]
131    pub fn active_variant(self) -> &'static Variant {
132        let index = self.variant_index();
133        &self.def.variants[index]
134    }
135
136    /// Returns the name of the active variant for this enum value
137    #[inline]
138    pub fn variant_name_active(self) -> &'static str {
139        self.active_variant().name
140    }
141
142    /// Returns the kind of the active variant (Unit, Tuple, Struct)
143    #[inline]
144    pub fn variant_kind_active(self) -> &'static VariantKind {
145        &self.active_variant().kind
146    }
147
148    /// Returns a pointer to the data of the active variant
149    pub(crate) fn variant_data(self) -> OpaqueConst<'mem> {
150        let variant_offset = self.active_variant().offset;
151        unsafe { self.value.data().field(variant_offset) }
152    }
153
154    /// Returns a Peek handle to a field of a tuple or struct variant
155    pub fn field(self, field_name: &str) -> Option<crate::Peek<'mem>> {
156        let variant = self.active_variant();
157
158        match &variant.kind {
159            VariantKind::Unit => None, // Unit variants have no fields
160            VariantKind::Tuple { fields } => {
161                // For tuple variants, find by name
162                let field = fields.iter().find(|f| f.name == field_name)?;
163                let field_data = unsafe { self.variant_data().field(field.offset) };
164                Some(unsafe { crate::Peek::unchecked_new(field_data, field.shape) })
165            }
166            VariantKind::Struct { fields } => {
167                // For struct variants, find by name
168                let field = fields.iter().find(|f| f.name == field_name)?;
169                let field_data = unsafe { self.variant_data().field(field.offset) };
170                Some(unsafe { crate::Peek::unchecked_new(field_data, field.shape) })
171            }
172            _ => None, // Handle other variant kinds that might be added in the future
173        }
174    }
175
176    /// Returns a Peek handle to a field of a tuple variant by index
177    pub fn tuple_field(self, index: usize) -> Option<crate::Peek<'mem>> {
178        let variant = self.active_variant();
179
180        match &variant.kind {
181            VariantKind::Tuple { fields } => {
182                if index >= fields.len() {
183                    return None;
184                }
185
186                let field = &fields[index];
187                let field_data = unsafe { self.variant_data().field(field.offset) };
188                Some(unsafe { crate::Peek::unchecked_new(field_data, field.shape) })
189            }
190            _ => None, // Not a tuple variant
191        }
192    }
193
194    /// Returns an iterator over fields of a struct or tuple variant with metadata
195    #[cfg(feature = "alloc")]
196    pub fn fields_with_metadata(
197        self,
198    ) -> Box<
199        dyn Iterator<
200                Item = (
201                    usize,
202                    &'static str,
203                    crate::Peek<'mem>,
204                    &'static facet_core::Field,
205                ),
206            > + 'mem,
207    > {
208        let variant = self.active_variant();
209        let data = self.variant_data();
210
211        match &variant.kind {
212            VariantKind::Struct { fields } => {
213                Box::new(fields.iter().enumerate().map(move |(i, field)| {
214                    let field_data = unsafe { data.field(field.offset) };
215                    let field_peek = unsafe { crate::Peek::unchecked_new(field_data, field.shape) };
216                    (i, field.name, field_peek, field)
217                }))
218            }
219            VariantKind::Tuple { fields } => {
220                Box::new(fields.iter().enumerate().map(move |(i, field)| {
221                    let field_data = unsafe { data.field(field.offset) };
222                    let field_peek = unsafe { crate::Peek::unchecked_new(field_data, field.shape) };
223                    (i, field.name, field_peek, field)
224                }))
225            }
226            _ => Box::new(core::iter::empty()),
227        }
228    }
229
230    /// Returns an iterator over fields of a struct or tuple variant
231    #[cfg(feature = "alloc")]
232    pub fn fields(self) -> Box<dyn Iterator<Item = (&'static str, crate::Peek<'mem>)> + 'mem> {
233        let variant = self.active_variant();
234        let data = self.variant_data();
235
236        match &variant.kind {
237            VariantKind::Struct { fields } => Box::new(fields.iter().map(move |field| {
238                let field_data = unsafe { data.field(field.offset) };
239                let peek = unsafe { crate::Peek::unchecked_new(field_data, field.shape) };
240                (field.name, peek)
241            })),
242            VariantKind::Tuple { fields } => Box::new(fields.iter().map(move |field| {
243                let field_data = unsafe { data.field(field.offset) };
244                let peek = unsafe { crate::Peek::unchecked_new(field_data, field.shape) };
245                (field.name, peek)
246            })),
247            _ => Box::new(core::iter::empty()),
248        }
249    }
250}