Skip to main content

facet_reflect/partial/partial_api/
eenum.rs

1use super::*;
2
3////////////////////////////////////////////////////////////////////////////////////////////////////
4// Enum variant selection
5////////////////////////////////////////////////////////////////////////////////////////////////////
6impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
7    /// Get the currently selected variant for an enum
8    pub fn selected_variant(&self) -> Option<Variant> {
9        let frame = self.frames().last()?;
10
11        match &frame.tracker {
12            Tracker::Enum { variant, .. } => Some(**variant),
13            _ => None,
14        }
15    }
16
17    /// Get the currently selected variant's plan metadata.
18    ///
19    /// Returns the VariantPlanMeta for the selected variant, which contains precomputed
20    /// information like `has_flatten` and `field_lookup` for fast field lookups.
21    pub fn selected_variant_plan(&self) -> Option<&crate::typeplan::VariantPlanMeta> {
22        let frame = self.frames().last()?;
23        let enum_plan = self.root_plan.enum_plan_by_id(frame.type_plan)?;
24
25        match &frame.tracker {
26            Tracker::Enum { variant_idx, .. } => self
27                .root_plan
28                .variants(enum_plan.variants)
29                .get(*variant_idx),
30            _ => None,
31        }
32    }
33
34    /// Find a variant by name in the current enum.
35    ///
36    /// This searches by effective name (respecting `#[facet(rename = "...")]` attributes).
37    pub fn find_variant(&self, variant_name: &str) -> Option<(usize, &'static Variant)> {
38        let frame = self.frames().last()?;
39        let enum_plan = self.root_plan.enum_plan_by_id(frame.type_plan)?;
40        let idx = enum_plan.variant_lookup.find(variant_name)?;
41        let variants = self.root_plan.variants(enum_plan.variants);
42        Some((idx, variants[idx].variant))
43    }
44
45    /// Assuming the current frame is an enum, this selects a variant by index
46    /// (0-based, in declaration order).
47    ///
48    /// For example:
49    ///
50    /// ```rust,no_run
51    /// enum E { A, B, C }
52    /// ```
53    ///
54    /// Calling `select_nth_variant(2)` would select variant `C`.
55    ///
56    /// This will return an error if the current frame is anything other than fully-uninitialized.
57    /// In other words, it's not possible to "switch to a different variant" once you've selected one.
58    ///
59    /// This does _not_ push a frame on the stack.
60    pub fn select_nth_variant(mut self, index: usize) -> Result<Self, ReflectError> {
61        let frame = self.frames().last().unwrap();
62        let enum_type = frame.get_enum_type().map_err(|e| self.err(e))?;
63
64        if index >= enum_type.variants.len() {
65            return Err(self.err(ReflectErrorKind::OperationFailed {
66                shape: frame.allocated.shape(),
67                operation: "variant index out of bounds",
68            }));
69        }
70        let variant = &enum_type.variants[index];
71
72        self.select_variant_internal(&enum_type, variant, index)?;
73        Ok(self)
74    }
75
76    /// Pushes a variant for enum initialization by name.
77    ///
78    /// This searches by effective name (respecting `#[facet(rename = "...")]` attributes).
79    ///
80    /// See [Self::select_nth_variant] for more notes.
81    pub fn select_variant_named(mut self, variant_name: &str) -> Result<Self, ReflectError> {
82        let frame = self.frames().last().unwrap();
83        let enum_type = frame.get_enum_type().map_err(|e| self.err(e))?;
84        let shape = frame.allocated.shape();
85
86        // Use precomputed VariantLookup for fast lookup
87        let enum_plan = self.root_plan.enum_plan_by_id(frame.type_plan).unwrap();
88        let variant_idx = enum_plan.variant_lookup.find(variant_name).ok_or_else(|| {
89            self.err(ReflectErrorKind::OperationFailed {
90                shape,
91                operation: "No variant found with the given name",
92            })
93        })?;
94        let variant = &enum_type.variants[variant_idx];
95
96        self.select_variant_internal(&enum_type, variant, variant_idx)?;
97        Ok(self)
98    }
99
100    /// Selects a given enum variant by discriminant. If none of the variants
101    /// of the frame's enum have that discriminant, this returns an error.
102    ///
103    /// See [Self::select_nth_variant] for more notes.
104    pub fn select_variant(mut self, discriminant: i64) -> Result<Self, ReflectError> {
105        // Check all invariants early before making any changes
106        let frame = self.frames().last().unwrap();
107
108        // Check that we're dealing with an enum
109        let enum_type = match frame.allocated.shape().ty {
110            Type::User(UserType::Enum(e)) => e,
111            _ => {
112                return Err(self.err(ReflectErrorKind::WasNotA {
113                    expected: "enum",
114                    actual: frame.allocated.shape(),
115                }));
116            }
117        };
118
119        // Find the variant with the matching discriminant
120        let Some((variant_idx, variant)) = enum_type
121            .variants
122            .iter()
123            .enumerate()
124            .find(|(_, v)| v.discriminant == Some(discriminant))
125        else {
126            return Err(self.err(ReflectErrorKind::OperationFailed {
127                shape: frame.allocated.shape(),
128                operation: "No variant found with the given discriminant",
129            }));
130        };
131
132        // Update the frame tracker to select the variant
133        self.select_variant_internal(&enum_type, variant, variant_idx)?;
134
135        Ok(self)
136    }
137}