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, ReflectKind, ReflectMut, ReflectOwned, ReflectRef,
11    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
30impl 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
40impl 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)]
71pub struct DynamicEnum {
72    represented_type: Option<&'static TypeInfo>,
73    variant_name: String,
74    variant_index: usize,
75    variant: DynamicVariant,
76}
77
78impl 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
214impl 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
292impl 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
422impl_type_path!((in bevy_reflect) DynamicEnum);