bevy_reflect/enums/
dynamic_enum.rs

1use bevy_reflect_derive::impl_type_path;
2
3use 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
9use alloc::{boxed::Box, string::String};
10use core::fmt::Formatter;
11use derive_more::derive::From;
12
13/// A dynamic representation of an enum variant.
14#[derive(Debug, Default, From)]
15pub 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
25impl 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
35impl 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)]
66pub struct DynamicEnum {
67    represented_type: Option<&'static TypeInfo>,
68    variant_name: String,
69    variant_index: usize,
70    variant: DynamicVariant,
71}
72
73impl 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
209impl 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
287impl 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
412impl_type_path!((in bevy_reflect) DynamicEnum);