Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/src/enums/enum_trait.rs
9374 views
1
use crate::generics::impl_generic_info_methods;
2
use 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
};
8
use alloc::{boxed::Box, format, string::String};
9
use bevy_platform::collections::HashMap;
10
use bevy_platform::sync::Arc;
11
use 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
97
pub 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
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(Clone, Debug)]
151
pub 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
162
impl 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
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
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
impl_custom_attribute_methods!(self.custom_attributes, "enum");
256
257
impl_generic_info_methods!(generics);
258
}
259
260
/// An iterator over the fields in the current enum variant.
261
pub struct VariantFieldIter<'a> {
262
container: &'a dyn Enum,
263
index: usize,
264
}
265
266
impl<'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
276
impl<'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
298
impl<'a> ExactSizeIterator for VariantFieldIter<'a> {}
299
300
/// A field in the current enum variant.
301
pub 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
308
impl<'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)]
328
mod 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
}
379
380