use bevy_reflect_derive::impl_type_path; use crate::{ self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, ApplyError, DynamicStruct, DynamicTuple, Enum, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo, VariantFieldIter, VariantType, }; use core::fmt::Formatter; use derive_more::derive::From; /// A dynamic representation of an enum variant. #[derive(Debug, Default, From)] pub enum DynamicVariant { #[default] Unit, Tuple(DynamicTuple), Struct(DynamicStruct), } impl Clone for DynamicVariant { fn clone(&self) -> Self { match self { DynamicVariant::Unit => DynamicVariant::Unit, DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.clone_dynamic()), DynamicVariant::Struct(data) => DynamicVariant::Struct(data.clone_dynamic()), } } } impl From<()> for DynamicVariant { fn from(_: ()) -> Self { Self::Unit } } /// A dynamic representation of an enum. /// /// This allows for enums to be configured at runtime. /// /// # Example /// /// ``` /// # use bevy_reflect::{DynamicEnum, DynamicVariant, Reflect, PartialReflect}; /// /// // The original enum value /// let mut value: Option = Some(123); /// /// // Create a DynamicEnum to represent the new value /// let mut dyn_enum = DynamicEnum::new( /// "None", /// DynamicVariant::Unit /// ); /// /// // Apply the DynamicEnum as a patch to the original value /// value.apply(dyn_enum.as_partial_reflect()); /// /// // Tada! /// assert_eq!(None, value); /// ``` #[derive(Default, Debug)] pub struct DynamicEnum { represented_type: Option<&'static TypeInfo>, variant_name: String, variant_index: usize, variant: DynamicVariant, } impl DynamicEnum { /// Create a new [`DynamicEnum`] to represent an enum at runtime. /// /// # Arguments /// /// * `variant_name`: The name of the variant to set /// * `variant`: The variant data pub fn new, V: Into>(variant_name: I, variant: V) -> Self { Self { represented_type: None, variant_index: 0, variant_name: variant_name.into(), variant: variant.into(), } } /// Create a new [`DynamicEnum`] with a variant index to represent an enum at runtime. /// /// # Arguments /// /// * `variant_index`: The index of the variant to set /// * `variant_name`: The name of the variant to set /// * `variant`: The variant data pub fn new_with_index, V: Into>( variant_index: usize, variant_name: I, variant: V, ) -> Self { Self { represented_type: None, variant_index, variant_name: variant_name.into(), variant: variant.into(), } } /// Sets the [type] to be represented by this `DynamicEnum`. /// /// # Panics /// /// Panics if the given [type] is not a [`TypeInfo::Enum`]. /// /// [type]: TypeInfo pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) { if let Some(represented_type) = represented_type { assert!( matches!(represented_type, TypeInfo::Enum(_)), "expected TypeInfo::Enum but received: {:?}", represented_type ); } self.represented_type = represented_type; } /// Set the current enum variant represented by this struct. pub fn set_variant, V: Into>(&mut self, name: I, variant: V) { self.variant_name = name.into(); self.variant = variant.into(); } /// Set the current enum variant represented by this struct along with its variant index. pub fn set_variant_with_index, V: Into>( &mut self, variant_index: usize, variant_name: I, variant: V, ) { self.variant_index = variant_index; self.variant_name = variant_name.into(); self.variant = variant.into(); } /// Create a [`DynamicEnum`] from an existing one. /// /// This is functionally the same as [`DynamicEnum::from_ref`] except it takes an owned value. pub fn from(value: TEnum) -> Self { Self::from_ref(&value) } /// Create a [`DynamicEnum`] from an existing one. /// /// This is functionally the same as [`DynamicEnum::from`] except it takes a reference. pub fn from_ref(value: &TEnum) -> Self { let type_info = value.get_represented_type_info(); let mut dyn_enum = match value.variant_type() { VariantType::Unit => DynamicEnum::new_with_index( value.variant_index(), value.variant_name(), DynamicVariant::Unit, ), VariantType::Tuple => { let mut data = DynamicTuple::default(); for field in value.iter_fields() { data.insert_boxed(field.value().clone_value()); } DynamicEnum::new_with_index( value.variant_index(), value.variant_name(), DynamicVariant::Tuple(data), ) } VariantType::Struct => { let mut data = DynamicStruct::default(); for field in value.iter_fields() { let name = field.name().unwrap(); data.insert_boxed(name, field.value().clone_value()); } DynamicEnum::new_with_index( value.variant_index(), value.variant_name(), DynamicVariant::Struct(data), ) } }; dyn_enum.set_represented_type(type_info); dyn_enum } } impl Enum for DynamicEnum { fn field(&self, name: &str) -> Option<&dyn PartialReflect> { if let DynamicVariant::Struct(data) = &self.variant { data.field(name) } else { None } } fn field_at(&self, index: usize) -> Option<&dyn PartialReflect> { if let DynamicVariant::Tuple(data) = &self.variant { data.field(index) } else { None } } fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect> { if let DynamicVariant::Struct(data) = &mut self.variant { data.field_mut(name) } else { None } } fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> { if let DynamicVariant::Tuple(data) = &mut self.variant { data.field_mut(index) } else { None } } fn index_of(&self, name: &str) -> Option { if let DynamicVariant::Struct(data) = &self.variant { data.index_of(name) } else { None } } fn name_at(&self, index: usize) -> Option<&str> { if let DynamicVariant::Struct(data) = &self.variant { data.name_at(index) } else { None } } fn iter_fields(&self) -> VariantFieldIter { VariantFieldIter::new(self) } fn field_len(&self) -> usize { match &self.variant { DynamicVariant::Unit => 0, DynamicVariant::Tuple(data) => data.field_len(), DynamicVariant::Struct(data) => data.field_len(), } } fn variant_name(&self) -> &str { &self.variant_name } fn variant_index(&self) -> usize { self.variant_index } fn variant_type(&self) -> VariantType { match &self.variant { DynamicVariant::Unit => VariantType::Unit, DynamicVariant::Tuple(..) => VariantType::Tuple, DynamicVariant::Struct(..) => VariantType::Struct, } } fn clone_dynamic(&self) -> DynamicEnum { Self { represented_type: self.represented_type, variant_index: self.variant_index, variant_name: self.variant_name.clone(), variant: self.variant.clone(), } } } impl PartialReflect for DynamicEnum { #[inline] fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { self.represented_type } #[inline] fn into_partial_reflect(self: Box) -> Box { self } #[inline] fn as_partial_reflect(&self) -> &dyn PartialReflect { self } #[inline] fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect { self } fn try_into_reflect(self: Box) -> Result, Box> { Err(self) } fn try_as_reflect(&self) -> Option<&dyn Reflect> { None } fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> { None } #[inline] fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> { let value = value.reflect_ref().as_enum()?; if Enum::variant_name(self) == value.variant_name() { // Same variant -> just update fields match value.variant_type() { VariantType::Struct => { for field in value.iter_fields() { let name = field.name().unwrap(); if let Some(v) = Enum::field_mut(self, name) { v.try_apply(field.value())?; } } } VariantType::Tuple => { for (index, field) in value.iter_fields().enumerate() { if let Some(v) = Enum::field_at_mut(self, index) { v.try_apply(field.value())?; } } } _ => {} } } else { // New variant -> perform a switch let dyn_variant = match value.variant_type() { VariantType::Unit => DynamicVariant::Unit, VariantType::Tuple => { let mut dyn_tuple = DynamicTuple::default(); for field in value.iter_fields() { dyn_tuple.insert_boxed(field.value().clone_value()); } DynamicVariant::Tuple(dyn_tuple) } VariantType::Struct => { let mut dyn_struct = DynamicStruct::default(); for field in value.iter_fields() { dyn_struct.insert_boxed(field.name().unwrap(), field.value().clone_value()); } DynamicVariant::Struct(dyn_struct) } }; self.set_variant(value.variant_name(), dyn_variant); } Ok(()) } #[inline] fn reflect_kind(&self) -> ReflectKind { ReflectKind::Enum } #[inline] fn reflect_ref(&self) -> ReflectRef { ReflectRef::Enum(self) } #[inline] fn reflect_mut(&mut self) -> ReflectMut { ReflectMut::Enum(self) } #[inline] fn reflect_owned(self: Box) -> ReflectOwned { ReflectOwned::Enum(self) } #[inline] fn clone_value(&self) -> Box { Box::new(self.clone_dynamic()) } #[inline] fn reflect_hash(&self) -> Option { enum_hash(self) } #[inline] fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option { enum_partial_eq(self, value) } #[inline] fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "DynamicEnum(")?; enum_debug(self, f)?; write!(f, ")") } #[inline] fn is_dynamic(&self) -> bool { true } } impl_type_path!((in bevy_reflect) DynamicEnum);