facet_reflect/peek/
enum_.rs

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