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
6599 views
1
use crate::generics::impl_generic_info_methods;
2
use crate::{
3
attributes::{impl_custom_attribute_methods, CustomAttributes},
4
type_info::impl_type_methods,
5
DynamicEnum, Generics, PartialReflect, Type, TypePath, VariantInfo, VariantType,
6
};
7
use alloc::{boxed::Box, format, string::String};
8
use bevy_platform::collections::HashMap;
9
use bevy_platform::sync::Arc;
10
use 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
96
pub 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
fn to_dynamic_enum(&self) -> DynamicEnum {
129
DynamicEnum::from_ref(self)
130
}
131
/// Returns true if the current variant's type matches the given one.
132
fn is_variant(&self, variant_type: VariantType) -> bool {
133
self.variant_type() == variant_type
134
}
135
/// Returns the full path to the current variant.
136
fn variant_path(&self) -> String {
137
format!("{}::{}", self.reflect_type_path(), self.variant_name())
138
}
139
140
/// Will return `None` if [`TypeInfo`] is not available.
141
///
142
/// [`TypeInfo`]: crate::TypeInfo
143
fn get_represented_enum_info(&self) -> Option<&'static EnumInfo> {
144
self.get_represented_type_info()?.as_enum().ok()
145
}
146
}
147
148
/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo).
149
#[derive(Clone, Debug)]
150
pub struct EnumInfo {
151
ty: Type,
152
generics: Generics,
153
variants: Box<[VariantInfo]>,
154
variant_names: Box<[&'static str]>,
155
variant_indices: HashMap<&'static str, usize>,
156
custom_attributes: Arc<CustomAttributes>,
157
#[cfg(feature = "documentation")]
158
docs: Option<&'static str>,
159
}
160
161
impl EnumInfo {
162
/// Create a new [`EnumInfo`].
163
///
164
/// # Arguments
165
///
166
/// * `variants`: The variants of this enum in the order they are defined
167
pub fn new<TEnum: Enum + TypePath>(variants: &[VariantInfo]) -> Self {
168
let variant_indices = variants
169
.iter()
170
.enumerate()
171
.map(|(index, variant)| (variant.name(), index))
172
.collect::<HashMap<_, _>>();
173
174
let variant_names = variants.iter().map(VariantInfo::name).collect();
175
176
Self {
177
ty: Type::of::<TEnum>(),
178
generics: Generics::new(),
179
variants: variants.to_vec().into_boxed_slice(),
180
variant_names,
181
variant_indices,
182
custom_attributes: Arc::new(CustomAttributes::default()),
183
#[cfg(feature = "documentation")]
184
docs: None,
185
}
186
}
187
188
/// Sets the docstring for this enum.
189
#[cfg(feature = "documentation")]
190
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
191
Self { docs, ..self }
192
}
193
194
/// Sets the custom attributes for this enum.
195
pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {
196
Self {
197
custom_attributes: Arc::new(custom_attributes),
198
..self
199
}
200
}
201
202
/// A slice containing the names of all variants in order.
203
pub fn variant_names(&self) -> &[&'static str] {
204
&self.variant_names
205
}
206
207
/// Get a variant with the given name.
208
pub fn variant(&self, name: &str) -> Option<&VariantInfo> {
209
self.variant_indices
210
.get(name)
211
.map(|index| &self.variants[*index])
212
}
213
214
/// Get a variant at the given index.
215
pub fn variant_at(&self, index: usize) -> Option<&VariantInfo> {
216
self.variants.get(index)
217
}
218
219
/// Get the index of the variant with the given name.
220
pub fn index_of(&self, name: &str) -> Option<usize> {
221
self.variant_indices.get(name).copied()
222
}
223
224
/// Returns the full path to the given variant.
225
///
226
/// This does _not_ check if the given variant exists.
227
pub fn variant_path(&self, name: &str) -> String {
228
format!("{}::{name}", self.type_path())
229
}
230
231
/// Checks if a variant with the given name exists within this enum.
232
pub fn contains_variant(&self, name: &str) -> bool {
233
self.variant_indices.contains_key(name)
234
}
235
236
/// Iterate over the variants of this enum.
237
pub fn iter(&self) -> Iter<'_, VariantInfo> {
238
self.variants.iter()
239
}
240
241
/// The number of variants in this enum.
242
pub fn variant_len(&self) -> usize {
243
self.variants.len()
244
}
245
246
impl_type_methods!(ty);
247
248
/// The docstring of this enum, if any.
249
#[cfg(feature = "documentation")]
250
pub fn docs(&self) -> Option<&'static str> {
251
self.docs
252
}
253
254
impl_custom_attribute_methods!(self.custom_attributes, "enum");
255
256
impl_generic_info_methods!(generics);
257
}
258
259
/// An iterator over the fields in the current enum variant.
260
pub struct VariantFieldIter<'a> {
261
container: &'a dyn Enum,
262
index: usize,
263
}
264
265
impl<'a> VariantFieldIter<'a> {
266
/// Creates a new [`VariantFieldIter`].
267
pub fn new(container: &'a dyn Enum) -> Self {
268
Self {
269
container,
270
index: 0,
271
}
272
}
273
}
274
275
impl<'a> Iterator for VariantFieldIter<'a> {
276
type Item = VariantField<'a>;
277
278
fn next(&mut self) -> Option<Self::Item> {
279
let value = match self.container.variant_type() {
280
VariantType::Unit => None,
281
VariantType::Tuple => Some(VariantField::Tuple(self.container.field_at(self.index)?)),
282
VariantType::Struct => {
283
let name = self.container.name_at(self.index)?;
284
Some(VariantField::Struct(name, self.container.field(name)?))
285
}
286
};
287
self.index += value.is_some() as usize;
288
value
289
}
290
291
fn size_hint(&self) -> (usize, Option<usize>) {
292
let size = self.container.field_len();
293
(size, Some(size))
294
}
295
}
296
297
impl<'a> ExactSizeIterator for VariantFieldIter<'a> {}
298
299
/// A field in the current enum variant.
300
pub enum VariantField<'a> {
301
/// The name and value of a field in a struct variant.
302
Struct(&'a str, &'a dyn PartialReflect),
303
/// The value of a field in a tuple variant.
304
Tuple(&'a dyn PartialReflect),
305
}
306
307
impl<'a> VariantField<'a> {
308
/// Returns the name of a struct variant field, or [`None`] for a tuple variant field.
309
pub fn name(&self) -> Option<&'a str> {
310
if let Self::Struct(name, ..) = self {
311
Some(*name)
312
} else {
313
None
314
}
315
}
316
317
/// Gets a reference to the value of this field.
318
pub fn value(&self) -> &'a dyn PartialReflect {
319
match *self {
320
Self::Struct(_, value) | Self::Tuple(value) => value,
321
}
322
}
323
}
324
325
// Tests that need access to internal fields have to go here rather than in mod.rs
326
#[cfg(test)]
327
mod tests {
328
use crate::*;
329
330
#[derive(Reflect, Debug, PartialEq)]
331
enum MyEnum {
332
A,
333
B(usize, i32),
334
C { foo: f32, bar: bool },
335
}
336
#[test]
337
fn next_index_increment() {
338
// unit enums always return none, so index should stay at 0
339
let unit_enum = MyEnum::A;
340
let mut iter = unit_enum.iter_fields();
341
let size = iter.len();
342
for _ in 0..2 {
343
assert!(iter.next().is_none());
344
assert_eq!(size, iter.index);
345
}
346
// tuple enums we iter over each value (unnamed fields), stop after that
347
let tuple_enum = MyEnum::B(0, 1);
348
let mut iter = tuple_enum.iter_fields();
349
let size = iter.len();
350
for _ in 0..2 {
351
let prev_index = iter.index;
352
assert!(iter.next().is_some());
353
assert_eq!(prev_index, iter.index - 1);
354
}
355
for _ in 0..2 {
356
assert!(iter.next().is_none());
357
assert_eq!(size, iter.index);
358
}
359
360
// struct enums, we iterate over each field in the struct
361
let struct_enum = MyEnum::C {
362
foo: 0.,
363
bar: false,
364
};
365
let mut iter = struct_enum.iter_fields();
366
let size = iter.len();
367
for _ in 0..2 {
368
let prev_index = iter.index;
369
assert!(iter.next().is_some());
370
assert_eq!(prev_index, iter.index - 1);
371
}
372
for _ in 0..2 {
373
assert!(iter.next().is_none());
374
assert_eq!(size, iter.index);
375
}
376
}
377
}
378
379