Skip to main content

bevy_reflect/enums/
enum_trait.rs

1use crate::generics::impl_generic_info_methods;
2use crate::{
3    attributes::{impl_custom_attribute_methods, CustomAttributes},
4    enums::{DynamicEnum, VariantInfo, VariantType},
5    ty::impl_type_methods,
6    Generics, PartialReflect, ReflectCloneError, Type, TypePath,
7};
8use alloc::{boxed::Box, format, string::String};
9use bevy_platform::collections::HashMap;
10use core::slice::Iter;
11
12/// A trait used to power [enum-like] operations via [reflection].
13///
14/// This allows enums to be processed and modified dynamically at runtime without
15/// necessarily knowing the actual type.
16/// Enums are much more complex than their struct counterparts.
17/// As a result, users will need to be mindful of conventions, considerations,
18/// and complications when working with this trait.
19///
20/// # Variants
21///
22/// An enum is a set of choices called _variants_.
23/// An instance of an enum can only exist as one of these choices at any given time.
24/// Consider Rust's [`Option<T>`]. It's an enum with two variants: [`None`] and [`Some`].
25/// If you're `None`, you can't be `Some` and vice versa.
26///
27/// > ⚠️ __This is very important:__
28/// > The [`Enum`] trait represents an enum _as one of its variants_.
29/// > It does not represent the entire enum since that's not true to how enums work.
30///
31/// Variants come in a few [flavors](VariantType):
32///
33/// | Variant Type | Syntax                         |
34/// | ------------ | ------------------------------ |
35/// | Unit         | `MyEnum::Foo`                  |
36/// | Tuple        | `MyEnum::Foo( i32, i32 )`      |
37/// | Struct       | `MyEnum::Foo{ value: String }` |
38///
39/// As you can see, a unit variant contains no fields, while tuple and struct variants
40/// can contain one or more fields.
41/// The fields in a tuple variant is defined by their _order_ within the variant.
42/// Index `0` represents the first field in the variant and so on.
43/// Fields in struct variants (excluding tuple structs), on the other hand, are
44/// represented by a _name_.
45///
46/// # Implementation
47///
48/// > 💡 This trait can be automatically implemented using [`#[derive(Reflect)]`](derive@crate::Reflect)
49/// > on an enum definition.
50///
51/// Despite the fact that enums can represent multiple states, traits only exist in one state
52/// and must be applied to the entire enum rather than a particular variant.
53/// Because of this limitation, the [`Enum`] trait must not only _represent_ any of the
54/// three variant types, but also define the _methods_ for all three as well.
55///
56/// What does this mean? It means that even though a unit variant contains no fields, a
57/// representation of that variant using the [`Enum`] trait will still contain methods for
58/// accessing fields!
59/// Again, this is to account for _all three_ variant types.
60///
61/// We recommend using the built-in [`#[derive(Reflect)]`](derive@crate::Reflect) macro to automatically handle all the
62/// implementation details for you.
63/// However, if you _must_ implement this trait manually, there are a few things to keep in mind...
64///
65/// ## Field Order
66///
67/// While tuple variants identify their fields by the order in which they are defined, struct
68/// variants identify fields by their name.
69/// However, both should allow access to fields by their defined order.
70///
71/// The reason all fields, regardless of variant type, need to be accessible by their order is
72/// due to field iteration.
73/// We need a way to iterate through each field in a variant, and the easiest way of achieving
74/// that is through the use of field order.
75///
76/// The derive macro adds proper struct variant handling for [`Enum::index_of`], [`Enum::name_at`]
77/// and [`Enum::field_at[_mut]`](Enum::field_at) methods.
78/// The first two methods are __required__ for all struct variant types.
79/// By convention, implementors should also handle the last method as well, but this is not
80/// a strict requirement.
81///
82/// ## Field Names
83///
84/// Implementors may choose to handle [`Enum::index_of`], [`Enum::name_at`], and
85/// [`Enum::field[_mut]`](Enum::field) for tuple variants by considering stringified `usize`s to be
86/// valid names (such as `"3"`).
87/// This isn't wrong to do, but the convention set by the derive macro is that it isn't supported.
88/// It's preferred that these strings be converted to their proper `usize` representations and
89/// the [`Enum::field_at[_mut]`](Enum::field_at) methods be used instead.
90///
91/// [enum-like]: https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html
92/// [reflection]: crate
93/// [`None`]: Option<T>::None
94/// [`Some`]: Option<T>::Some
95/// [`Reflect`]: bevy_reflect_derive::Reflect
96pub trait Enum: PartialReflect {
97    /// Returns a reference to the value of the field (in the current variant) with the given name.
98    ///
99    /// For non-[`VariantType::Struct`] variants, this should return `None`.
100    fn field(&self, name: &str) -> Option<&dyn PartialReflect>;
101    /// Returns a reference to the value of the field (in the current variant) at the given index.
102    fn field_at(&self, index: usize) -> Option<&dyn PartialReflect>;
103    /// Returns a mutable reference to the value of the field (in the current variant) with the given name.
104    ///
105    /// For non-[`VariantType::Struct`] variants, this should return `None`.
106    fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect>;
107    /// Returns a mutable reference to the value of the field (in the current variant) at the given index.
108    fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect>;
109    /// Returns the index of the field (in the current variant) with the given name.
110    ///
111    /// For non-[`VariantType::Struct`] variants, this should return `None`.
112    fn index_of(&self, name: &str) -> Option<usize>;
113    /// Returns the name of the field (in the current variant) with the given index.
114    ///
115    /// For non-[`VariantType::Struct`] variants, this should return `None`.
116    fn name_at(&self, index: usize) -> Option<&str>;
117    /// Returns an iterator over the values of the current variant's fields.
118    fn iter_fields(&self) -> VariantFieldIter<'_>;
119    /// Returns the number of fields in the current variant.
120    fn field_len(&self) -> usize;
121    /// The name of the current variant.
122    fn variant_name(&self) -> &str;
123    /// The index of the current variant.
124    fn variant_index(&self) -> usize;
125    /// The type of the current variant.
126    fn variant_type(&self) -> VariantType;
127    /// Creates a new [`DynamicEnum`] from this enum.
128    ///
129    /// Returns an error if any field of the active variant cannot be converted via
130    /// [`PartialReflect::to_dynamic`].
131    fn to_dynamic_enum(&self) -> Result<DynamicEnum, ReflectCloneError> {
132        DynamicEnum::try_from_ref(self)
133    }
134    /// Returns true if the current variant's type matches the given one.
135    fn is_variant(&self, variant_type: VariantType) -> bool {
136        self.variant_type() == variant_type
137    }
138    /// Returns the full path to the current variant.
139    fn variant_path(&self) -> String {
140        format!("{}::{}", self.reflect_type_path(), self.variant_name())
141    }
142
143    /// Will return `None` if [`TypeInfo`] is not available.
144    ///
145    /// [`TypeInfo`]: crate::TypeInfo
146    fn get_represented_enum_info(&self) -> Option<&'static EnumInfo> {
147        self.get_represented_type_info()?.as_enum().ok()
148    }
149}
150
151/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo).
152#[derive(Clone, Debug)]
153pub struct EnumInfo {
154    ty: Type,
155    generics: Generics,
156    variants: Box<[VariantInfo]>,
157    variant_names: Box<[&'static str]>,
158    variant_indices: HashMap<&'static str, usize>,
159    custom_attributes: CustomAttributes,
160    #[cfg(feature = "reflect_documentation")]
161    docs: Option<&'static str>,
162}
163
164impl EnumInfo {
165    /// Create a new [`EnumInfo`].
166    ///
167    /// # Arguments
168    ///
169    /// * `variants`: The variants of this enum in the order they are defined
170    pub fn new<TEnum: Enum + TypePath>(variants: &[VariantInfo]) -> Self {
171        Self::from_erased(variants, Type::of::<TEnum>())
172    }
173
174    // Inlining is disabled because this function is called many times by cold
175    // functions inside generated code.
176    #[inline(never)]
177    fn from_erased(variants: &[VariantInfo], ty: Type) -> Self {
178        let variant_indices = variants
179            .iter()
180            .enumerate()
181            .map(|(index, variant)| (variant.name(), index))
182            .collect::<HashMap<_, _>>();
183
184        let variant_names = variants.iter().map(VariantInfo::name).collect();
185
186        Self {
187            ty,
188            generics: Generics::new(),
189            variants: variants.to_vec().into_boxed_slice(),
190            variant_names,
191            variant_indices,
192            custom_attributes: CustomAttributes::default(),
193            #[cfg(feature = "reflect_documentation")]
194            docs: None,
195        }
196    }
197
198    /// Sets the docstring for this enum.
199    #[cfg(feature = "reflect_documentation")]
200    pub fn with_docs(self, docs: Option<&'static str>) -> Self {
201        Self { docs, ..self }
202    }
203
204    /// Sets the custom attributes for this enum.
205    pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {
206        Self {
207            custom_attributes,
208            ..self
209        }
210    }
211
212    /// A slice containing the names of all variants in order.
213    pub fn variant_names(&self) -> &[&'static str] {
214        &self.variant_names
215    }
216
217    /// Get a variant with the given name.
218    pub fn variant(&self, name: &str) -> Option<&VariantInfo> {
219        self.variant_indices
220            .get(name)
221            .map(|index| &self.variants[*index])
222    }
223
224    /// Get a variant at the given index.
225    pub fn variant_at(&self, index: usize) -> Option<&VariantInfo> {
226        self.variants.get(index)
227    }
228
229    /// Get the index of the variant with the given name.
230    pub fn index_of(&self, name: &str) -> Option<usize> {
231        self.variant_indices.get(name).copied()
232    }
233
234    /// Returns the full path to the given variant.
235    ///
236    /// This does _not_ check if the given variant exists.
237    pub fn variant_path(&self, name: &str) -> String {
238        format!("{}::{name}", self.type_path())
239    }
240
241    /// Checks if a variant with the given name exists within this enum.
242    pub fn contains_variant(&self, name: &str) -> bool {
243        self.variant_indices.contains_key(name)
244    }
245
246    /// Iterate over the variants of this enum.
247    pub fn iter(&self) -> Iter<'_, VariantInfo> {
248        self.variants.iter()
249    }
250
251    /// The number of variants in this enum.
252    pub fn variant_len(&self) -> usize {
253        self.variants.len()
254    }
255
256    impl_type_methods!(ty);
257
258    /// The docstring of this enum, if any.
259    #[cfg(feature = "reflect_documentation")]
260    pub fn docs(&self) -> Option<&'static str> {
261        self.docs
262    }
263
264    impl_custom_attribute_methods!(self.custom_attributes, "enum");
265
266    impl_generic_info_methods!(generics);
267}
268
269/// An iterator over the fields in the current enum variant.
270pub struct VariantFieldIter<'a> {
271    container: &'a dyn Enum,
272    index: usize,
273}
274
275impl<'a> VariantFieldIter<'a> {
276    /// Creates a new [`VariantFieldIter`].
277    pub fn new(container: &'a dyn Enum) -> Self {
278        Self {
279            container,
280            index: 0,
281        }
282    }
283}
284
285impl<'a> Iterator for VariantFieldIter<'a> {
286    type Item = VariantField<'a>;
287
288    fn next(&mut self) -> Option<Self::Item> {
289        let value = match self.container.variant_type() {
290            VariantType::Unit => None,
291            VariantType::Tuple => Some(VariantField::Tuple(self.container.field_at(self.index)?)),
292            VariantType::Struct => {
293                let name = self.container.name_at(self.index)?;
294                Some(VariantField::Struct(name, self.container.field(name)?))
295            }
296        };
297        self.index += value.is_some() as usize;
298        value
299    }
300
301    fn size_hint(&self) -> (usize, Option<usize>) {
302        let remaining = self.container.field_len().saturating_sub(self.index);
303        (remaining, Some(remaining))
304    }
305}
306
307impl<'a> ExactSizeIterator for VariantFieldIter<'a> {}
308
309/// A field in the current enum variant.
310pub enum VariantField<'a> {
311    /// The name and value of a field in a struct variant.
312    Struct(&'a str, &'a dyn PartialReflect),
313    /// The value of a field in a tuple variant.
314    Tuple(&'a dyn PartialReflect),
315}
316
317impl<'a> VariantField<'a> {
318    /// Returns the name of a struct variant field, or [`None`] for a tuple variant field.
319    pub fn name(&self) -> Option<&'a str> {
320        if let Self::Struct(name, ..) = self {
321            Some(*name)
322        } else {
323            None
324        }
325    }
326
327    /// Gets a reference to the value of this field.
328    pub fn value(&self) -> &'a dyn PartialReflect {
329        match *self {
330            Self::Struct(_, value) | Self::Tuple(value) => value,
331        }
332    }
333}
334
335// Tests that need access to internal fields have to go here rather than in mod.rs
336#[cfg(test)]
337mod tests {
338    use crate::{enums::*, Reflect};
339
340    #[derive(Reflect, Debug, PartialEq)]
341    enum MyEnum {
342        A,
343        B(usize, i32),
344        C { foo: f32, bar: bool },
345    }
346    #[test]
347    fn next_index_increment() {
348        // unit enums always return none, so index should stay at 0
349        let unit_enum = MyEnum::A;
350        let mut iter = unit_enum.iter_fields();
351        let size = iter.len();
352        for _ in 0..2 {
353            assert!(iter.next().is_none());
354            assert_eq!(size, iter.index);
355        }
356        // tuple enums we iter over each value (unnamed fields), stop after that
357        let tuple_enum = MyEnum::B(0, 1);
358        let mut iter = tuple_enum.iter_fields();
359        let size = iter.len();
360        for _ in 0..2 {
361            let prev_index = iter.index;
362            assert!(iter.next().is_some());
363            assert_eq!(prev_index, iter.index - 1);
364        }
365        for _ in 0..2 {
366            assert!(iter.next().is_none());
367            assert_eq!(size, iter.index);
368        }
369
370        // struct enums, we iterate over each field in the struct
371        let struct_enum = MyEnum::C {
372            foo: 0.,
373            bar: false,
374        };
375        let mut iter = struct_enum.iter_fields();
376        let size = iter.len();
377        for _ in 0..2 {
378            let prev_index = iter.index;
379            assert!(iter.next().is_some());
380            assert_eq!(prev_index, iter.index - 1);
381        }
382        for _ in 0..2 {
383            assert!(iter.next().is_none());
384            assert_eq!(size, iter.index);
385        }
386    }
387}