facet_reflect/peek/
enum_.rs

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