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