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