facet_reflect/wip/
enum_.rs

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