Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/src/enums/dynamic_enum.rs
9401 views
1
use bevy_reflect_derive::impl_type_path;
2
3
use crate::{
4
enums::{
5
enum_debug, enum_hash, enum_partial_cmp, enum_partial_eq, Enum, VariantFieldIter,
6
VariantType,
7
},
8
structs::{DynamicStruct, Struct},
9
tuple::{DynamicTuple, Tuple},
10
ApplyError, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef,
11
TypeInfo,
12
};
13
14
use alloc::{boxed::Box, string::String};
15
use core::fmt::Formatter;
16
use derive_more::derive::From;
17
18
/// A dynamic representation of an enum variant.
19
#[derive(Debug, Default, From)]
20
pub enum DynamicVariant {
21
/// A unit variant.
22
#[default]
23
Unit,
24
/// A tuple variant.
25
Tuple(DynamicTuple),
26
/// A struct variant.
27
Struct(DynamicStruct),
28
}
29
30
impl Clone for DynamicVariant {
31
fn clone(&self) -> Self {
32
match self {
33
DynamicVariant::Unit => DynamicVariant::Unit,
34
DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.to_dynamic_tuple()),
35
DynamicVariant::Struct(data) => DynamicVariant::Struct(data.to_dynamic_struct()),
36
}
37
}
38
}
39
40
impl From<()> for DynamicVariant {
41
fn from(_: ()) -> Self {
42
Self::Unit
43
}
44
}
45
46
/// A dynamic representation of an enum.
47
///
48
/// This allows for enums to be configured at runtime.
49
///
50
/// # Example
51
///
52
/// ```
53
/// # use bevy_reflect::{enums::{DynamicEnum, DynamicVariant}, Reflect, PartialReflect};
54
///
55
/// // The original enum value
56
/// let mut value: Option<usize> = Some(123);
57
///
58
/// // Create a DynamicEnum to represent the new value
59
/// let mut dyn_enum = DynamicEnum::new(
60
/// "None",
61
/// DynamicVariant::Unit
62
/// );
63
///
64
/// // Apply the DynamicEnum as a patch to the original value
65
/// value.apply(dyn_enum.as_partial_reflect());
66
///
67
/// // Tada!
68
/// assert_eq!(None, value);
69
/// ```
70
#[derive(Default, Debug)]
71
pub struct DynamicEnum {
72
represented_type: Option<&'static TypeInfo>,
73
variant_name: String,
74
variant_index: usize,
75
variant: DynamicVariant,
76
}
77
78
impl DynamicEnum {
79
/// Create a new [`DynamicEnum`] to represent an enum at runtime.
80
///
81
/// # Arguments
82
///
83
/// * `variant_name`: The name of the variant to set
84
/// * `variant`: The variant data
85
pub fn new<I: Into<String>, V: Into<DynamicVariant>>(variant_name: I, variant: V) -> Self {
86
Self {
87
represented_type: None,
88
variant_index: 0,
89
variant_name: variant_name.into(),
90
variant: variant.into(),
91
}
92
}
93
94
/// Create a new [`DynamicEnum`] with a variant index to represent an enum at runtime.
95
///
96
/// # Arguments
97
///
98
/// * `variant_index`: The index of the variant to set
99
/// * `variant_name`: The name of the variant to set
100
/// * `variant`: The variant data
101
pub fn new_with_index<I: Into<String>, V: Into<DynamicVariant>>(
102
variant_index: usize,
103
variant_name: I,
104
variant: V,
105
) -> Self {
106
Self {
107
represented_type: None,
108
variant_index,
109
variant_name: variant_name.into(),
110
variant: variant.into(),
111
}
112
}
113
114
/// Sets the [type] to be represented by this `DynamicEnum`.
115
///
116
/// # Panics
117
///
118
/// Panics if the given [type] is not a [`TypeInfo::Enum`].
119
///
120
/// [type]: TypeInfo
121
pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) {
122
if let Some(represented_type) = represented_type {
123
assert!(
124
matches!(represented_type, TypeInfo::Enum(_)),
125
"expected TypeInfo::Enum but received: {represented_type:?}",
126
);
127
}
128
129
self.represented_type = represented_type;
130
}
131
132
/// Set the current enum variant represented by this struct.
133
pub fn set_variant<I: Into<String>, V: Into<DynamicVariant>>(&mut self, name: I, variant: V) {
134
self.variant_name = name.into();
135
self.variant = variant.into();
136
}
137
138
/// Set the current enum variant represented by this struct along with its variant index.
139
pub fn set_variant_with_index<I: Into<String>, V: Into<DynamicVariant>>(
140
&mut self,
141
variant_index: usize,
142
variant_name: I,
143
variant: V,
144
) {
145
self.variant_index = variant_index;
146
self.variant_name = variant_name.into();
147
self.variant = variant.into();
148
}
149
150
/// Get a reference to the [`DynamicVariant`] contained in `self`.
151
pub fn variant(&self) -> &DynamicVariant {
152
&self.variant
153
}
154
155
/// Get a mutable reference to the [`DynamicVariant`] contained in `self`.
156
///
157
/// Using the mut reference to switch to a different variant will ___not___ update the
158
/// internal tracking of the variant name and index.
159
///
160
/// If you want to switch variants, prefer one of the setters:
161
/// [`DynamicEnum::set_variant`] or [`DynamicEnum::set_variant_with_index`].
162
pub fn variant_mut(&mut self) -> &mut DynamicVariant {
163
&mut self.variant
164
}
165
166
/// Create a [`DynamicEnum`] from an existing one.
167
///
168
/// This is functionally the same as [`DynamicEnum::from_ref`] except it takes an owned value.
169
pub fn from<TEnum: Enum>(value: TEnum) -> Self {
170
Self::from_ref(&value)
171
}
172
173
/// Create a [`DynamicEnum`] from an existing one.
174
///
175
/// This is functionally the same as [`DynamicEnum::from`] except it takes a reference.
176
pub fn from_ref<TEnum: Enum + ?Sized>(value: &TEnum) -> Self {
177
let type_info = value.get_represented_type_info();
178
let mut dyn_enum = match value.variant_type() {
179
VariantType::Unit => DynamicEnum::new_with_index(
180
value.variant_index(),
181
value.variant_name(),
182
DynamicVariant::Unit,
183
),
184
VariantType::Tuple => {
185
let mut data = DynamicTuple::default();
186
for field in value.iter_fields() {
187
data.insert_boxed(field.value().to_dynamic());
188
}
189
DynamicEnum::new_with_index(
190
value.variant_index(),
191
value.variant_name(),
192
DynamicVariant::Tuple(data),
193
)
194
}
195
VariantType::Struct => {
196
let mut data = DynamicStruct::default();
197
for field in value.iter_fields() {
198
let name = field.name().unwrap();
199
data.insert_boxed(name, field.value().to_dynamic());
200
}
201
DynamicEnum::new_with_index(
202
value.variant_index(),
203
value.variant_name(),
204
DynamicVariant::Struct(data),
205
)
206
}
207
};
208
209
dyn_enum.set_represented_type(type_info);
210
dyn_enum
211
}
212
}
213
214
impl Enum for DynamicEnum {
215
fn field(&self, name: &str) -> Option<&dyn PartialReflect> {
216
if let DynamicVariant::Struct(data) = &self.variant {
217
data.field(name)
218
} else {
219
None
220
}
221
}
222
223
fn field_at(&self, index: usize) -> Option<&dyn PartialReflect> {
224
match &self.variant {
225
DynamicVariant::Tuple(data) => data.field(index),
226
DynamicVariant::Struct(data) => data.field_at(index),
227
DynamicVariant::Unit => None,
228
}
229
}
230
231
fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect> {
232
if let DynamicVariant::Struct(data) = &mut self.variant {
233
data.field_mut(name)
234
} else {
235
None
236
}
237
}
238
239
fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> {
240
match &mut self.variant {
241
DynamicVariant::Tuple(data) => data.field_mut(index),
242
DynamicVariant::Struct(data) => data.field_at_mut(index),
243
DynamicVariant::Unit => None,
244
}
245
}
246
247
fn index_of(&self, name: &str) -> Option<usize> {
248
if let DynamicVariant::Struct(data) = &self.variant {
249
data.index_of_name(name)
250
} else {
251
None
252
}
253
}
254
255
fn name_at(&self, index: usize) -> Option<&str> {
256
if let DynamicVariant::Struct(data) = &self.variant {
257
data.name_at(index)
258
} else {
259
None
260
}
261
}
262
263
fn iter_fields(&self) -> VariantFieldIter<'_> {
264
VariantFieldIter::new(self)
265
}
266
267
fn field_len(&self) -> usize {
268
match &self.variant {
269
DynamicVariant::Unit => 0,
270
DynamicVariant::Tuple(data) => data.field_len(),
271
DynamicVariant::Struct(data) => data.field_len(),
272
}
273
}
274
275
fn variant_name(&self) -> &str {
276
&self.variant_name
277
}
278
279
fn variant_index(&self) -> usize {
280
self.variant_index
281
}
282
283
fn variant_type(&self) -> VariantType {
284
match &self.variant {
285
DynamicVariant::Unit => VariantType::Unit,
286
DynamicVariant::Tuple(..) => VariantType::Tuple,
287
DynamicVariant::Struct(..) => VariantType::Struct,
288
}
289
}
290
}
291
292
impl PartialReflect for DynamicEnum {
293
#[inline]
294
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
295
self.represented_type
296
}
297
298
#[inline]
299
fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {
300
self
301
}
302
303
#[inline]
304
fn as_partial_reflect(&self) -> &dyn PartialReflect {
305
self
306
}
307
308
#[inline]
309
fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {
310
self
311
}
312
313
fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {
314
Err(self)
315
}
316
317
fn try_as_reflect(&self) -> Option<&dyn Reflect> {
318
None
319
}
320
321
fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {
322
None
323
}
324
325
#[inline]
326
fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {
327
let value = value.reflect_ref().as_enum()?;
328
329
if Enum::variant_name(self) == value.variant_name() {
330
// Same variant -> just update fields
331
match value.variant_type() {
332
VariantType::Struct => {
333
for field in value.iter_fields() {
334
let name = field.name().unwrap();
335
if let Some(v) = Enum::field_mut(self, name) {
336
v.try_apply(field.value())?;
337
}
338
}
339
}
340
VariantType::Tuple => {
341
for (index, field) in value.iter_fields().enumerate() {
342
if let Some(v) = Enum::field_at_mut(self, index) {
343
v.try_apply(field.value())?;
344
}
345
}
346
}
347
_ => {}
348
}
349
} else {
350
// New variant -> perform a switch
351
let dyn_variant = match value.variant_type() {
352
VariantType::Unit => DynamicVariant::Unit,
353
VariantType::Tuple => {
354
let mut dyn_tuple = DynamicTuple::default();
355
for field in value.iter_fields() {
356
dyn_tuple.insert_boxed(field.value().to_dynamic());
357
}
358
DynamicVariant::Tuple(dyn_tuple)
359
}
360
VariantType::Struct => {
361
let mut dyn_struct = DynamicStruct::default();
362
for field in value.iter_fields() {
363
dyn_struct.insert_boxed(field.name().unwrap(), field.value().to_dynamic());
364
}
365
DynamicVariant::Struct(dyn_struct)
366
}
367
};
368
self.set_variant(value.variant_name(), dyn_variant);
369
}
370
371
Ok(())
372
}
373
374
#[inline]
375
fn reflect_kind(&self) -> ReflectKind {
376
ReflectKind::Enum
377
}
378
379
#[inline]
380
fn reflect_ref(&self) -> ReflectRef<'_> {
381
ReflectRef::Enum(self)
382
}
383
384
#[inline]
385
fn reflect_mut(&mut self) -> ReflectMut<'_> {
386
ReflectMut::Enum(self)
387
}
388
389
#[inline]
390
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
391
ReflectOwned::Enum(self)
392
}
393
394
#[inline]
395
fn reflect_hash(&self) -> Option<u64> {
396
enum_hash(self)
397
}
398
399
#[inline]
400
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
401
enum_partial_eq(self, value)
402
}
403
404
#[inline]
405
fn reflect_partial_cmp(&self, value: &dyn PartialReflect) -> Option<::core::cmp::Ordering> {
406
enum_partial_cmp(self, value)
407
}
408
409
#[inline]
410
fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
411
write!(f, "DynamicEnum(")?;
412
enum_debug(self, f)?;
413
write!(f, ")")
414
}
415
416
#[inline]
417
fn is_dynamic(&self) -> bool {
418
true
419
}
420
}
421
422
impl_type_path!((in bevy_reflect) DynamicEnum);
423
424