facet_reflect/wip/
enum_.rs

1use facet_core::{Def, FieldError, Variant};
2use owo_colors::OwoColorize;
3
4use crate::{ISet, ReflectError, Wip};
5
6impl Wip<'_> {
7    /// Selects a variant of an enum by index.
8    ///
9    /// # Arguments
10    ///
11    /// * `index` - The index of the variant to select.
12    ///
13    /// # Returns
14    ///
15    /// * `Ok(Self)` if the variant was successfully selected.
16    /// * `Err(ReflectError)` if the current frame is not an enum or the variant index is out of bounds.
17    pub fn variant(mut self, index: usize) -> Result<Self, ReflectError> {
18        let frame = self.frames.last_mut().unwrap();
19        let shape = frame.shape;
20        let Def::Enum(def) = shape.def else {
21            return Err(ReflectError::WasNotA {
22                expected: "enum",
23                actual: shape,
24            });
25        };
26
27        if index >= def.variants.len() {
28            return Err(ReflectError::FieldError {
29                shape,
30                field_error: FieldError::IndexOutOfBounds,
31            });
32        }
33
34        let variant = def.variants[index];
35
36        // Reset the field initialization state since we're selecting a new variant
37        ISet::clear(&mut frame.istate.fields);
38
39        // Write the discriminant value based on the enum's representation
40        let discriminant = variant.discriminant;
41        unsafe {
42            let data_ptr = frame.data.as_mut_byte_ptr();
43            match def.repr {
44                facet_core::EnumRepr::U8 => *data_ptr = discriminant as u8,
45                facet_core::EnumRepr::U16 => *(data_ptr as *mut u16) = discriminant as u16,
46                facet_core::EnumRepr::U32 => *(data_ptr as *mut u32) = discriminant as u32,
47                facet_core::EnumRepr::U64 => *(data_ptr as *mut u64) = discriminant as u64,
48                facet_core::EnumRepr::USize => *(data_ptr as *mut usize) = discriminant as usize,
49                facet_core::EnumRepr::I8 => *(data_ptr as *mut i8) = discriminant as i8,
50                facet_core::EnumRepr::I16 => *(data_ptr as *mut i16) = discriminant as i16,
51                facet_core::EnumRepr::I32 => *(data_ptr as *mut i32) = discriminant as i32,
52                facet_core::EnumRepr::I64 => *(data_ptr as *mut i64) = discriminant,
53                facet_core::EnumRepr::ISize => *(data_ptr as *mut isize) = discriminant as isize,
54                _ => {
55                    // Default to a reasonable size for other representations
56                    *(data_ptr as *mut u32) = discriminant as u32;
57                }
58            }
59        }
60
61        // Now that we've set the discriminant, we can store the variant
62        frame.istate.variant = Some(variant);
63
64        log::trace!(
65            "[{}] Selecting variant {} of {} with discriminant {}",
66            self.frames.len(),
67            variant.name.blue(),
68            shape.blue(),
69            discriminant
70        );
71
72        Ok(self)
73    }
74
75    /// Selects a variant of an enum by name.
76    ///
77    /// # Arguments
78    ///
79    /// * `name` - The name of the variant to select.
80    ///
81    /// # Returns
82    ///
83    /// * `Ok(Self)` if the variant was successfully selected.
84    /// * `Err(ReflectError)` if the current frame is not an enum or no variant with the given name exists.
85    pub fn variant_named(self, name: &str) -> Result<Self, ReflectError> {
86        let frame = self.frames.last().unwrap();
87        let shape = frame.shape;
88        let Def::Enum(def) = shape.def else {
89            return Err(ReflectError::WasNotA {
90                expected: "enum",
91                actual: shape,
92            });
93        };
94
95        let index =
96            def.variants
97                .iter()
98                .position(|v| v.name == name)
99                .ok_or(ReflectError::FieldError {
100                    shape,
101                    field_error: FieldError::NoSuchField,
102                })?;
103
104        self.variant(index)
105    }
106
107    /// Finds a variant in an enum by name.
108    ///
109    /// # Arguments
110    ///
111    /// * `name` - The name of the variant to find.
112    ///
113    /// # Returns
114    ///
115    /// * `Some(index, variant)` if a variant with the given name exists.
116    /// * `None` if the current frame is not an enum or no variant with the given name exists.
117    pub fn find_variant(&self, name: &str) -> Option<(usize, Variant)> {
118        let frame = self.frames.last()?;
119        if let Def::Enum(def) = frame.shape.def {
120            def.variants
121                .iter()
122                .enumerate()
123                .find(|(_, v)| v.name == name)
124                .map(|(i, &v)| (i, v))
125        } else {
126            None
127        }
128    }
129
130    /// Returns the currently selected variant for the enum in the current frame.
131    ///
132    /// # Returns
133    ///
134    /// * `Some(variant)` if the current frame is an enum and a variant has been selected.
135    /// * `None` if the current frame is not an enum or no variant has been selected yet.
136    pub fn selected_variant(&self) -> Option<Variant> {
137        let frame = self.frames.last()?;
138        frame.istate.variant
139    }
140}