Skip to main content

bevy_reflect/enums/
dynamic_enum.rs

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