facet_peek/
enum_.rs

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