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