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