Path: blob/main/crates/bevy_reflect/src/enums/dynamic_enum.rs
9401 views
use bevy_reflect_derive::impl_type_path;12use crate::{3enums::{4enum_debug, enum_hash, enum_partial_cmp, enum_partial_eq, Enum, VariantFieldIter,5VariantType,6},7structs::{DynamicStruct, Struct},8tuple::{DynamicTuple, Tuple},9ApplyError, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef,10TypeInfo,11};1213use alloc::{boxed::Box, string::String};14use core::fmt::Formatter;15use derive_more::derive::From;1617/// A dynamic representation of an enum variant.18#[derive(Debug, Default, From)]19pub enum DynamicVariant {20/// A unit variant.21#[default]22Unit,23/// A tuple variant.24Tuple(DynamicTuple),25/// A struct variant.26Struct(DynamicStruct),27}2829impl Clone for DynamicVariant {30fn clone(&self) -> Self {31match self {32DynamicVariant::Unit => DynamicVariant::Unit,33DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.to_dynamic_tuple()),34DynamicVariant::Struct(data) => DynamicVariant::Struct(data.to_dynamic_struct()),35}36}37}3839impl From<()> for DynamicVariant {40fn from(_: ()) -> Self {41Self::Unit42}43}4445/// A dynamic representation of an enum.46///47/// This allows for enums to be configured at runtime.48///49/// # Example50///51/// ```52/// # use bevy_reflect::{enums::{DynamicEnum, DynamicVariant}, Reflect, PartialReflect};53///54/// // The original enum value55/// let mut value: Option<usize> = Some(123);56///57/// // Create a DynamicEnum to represent the new value58/// let mut dyn_enum = DynamicEnum::new(59/// "None",60/// DynamicVariant::Unit61/// );62///63/// // Apply the DynamicEnum as a patch to the original value64/// value.apply(dyn_enum.as_partial_reflect());65///66/// // Tada!67/// assert_eq!(None, value);68/// ```69#[derive(Default, Debug)]70pub struct DynamicEnum {71represented_type: Option<&'static TypeInfo>,72variant_name: String,73variant_index: usize,74variant: DynamicVariant,75}7677impl DynamicEnum {78/// Create a new [`DynamicEnum`] to represent an enum at runtime.79///80/// # Arguments81///82/// * `variant_name`: The name of the variant to set83/// * `variant`: The variant data84pub fn new<I: Into<String>, V: Into<DynamicVariant>>(variant_name: I, variant: V) -> Self {85Self {86represented_type: None,87variant_index: 0,88variant_name: variant_name.into(),89variant: variant.into(),90}91}9293/// Create a new [`DynamicEnum`] with a variant index to represent an enum at runtime.94///95/// # Arguments96///97/// * `variant_index`: The index of the variant to set98/// * `variant_name`: The name of the variant to set99/// * `variant`: The variant data100pub fn new_with_index<I: Into<String>, V: Into<DynamicVariant>>(101variant_index: usize,102variant_name: I,103variant: V,104) -> Self {105Self {106represented_type: None,107variant_index,108variant_name: variant_name.into(),109variant: variant.into(),110}111}112113/// Sets the [type] to be represented by this `DynamicEnum`.114///115/// # Panics116///117/// Panics if the given [type] is not a [`TypeInfo::Enum`].118///119/// [type]: TypeInfo120pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) {121if let Some(represented_type) = represented_type {122assert!(123matches!(represented_type, TypeInfo::Enum(_)),124"expected TypeInfo::Enum but received: {represented_type:?}",125);126}127128self.represented_type = represented_type;129}130131/// Set the current enum variant represented by this struct.132pub fn set_variant<I: Into<String>, V: Into<DynamicVariant>>(&mut self, name: I, variant: V) {133self.variant_name = name.into();134self.variant = variant.into();135}136137/// Set the current enum variant represented by this struct along with its variant index.138pub fn set_variant_with_index<I: Into<String>, V: Into<DynamicVariant>>(139&mut self,140variant_index: usize,141variant_name: I,142variant: V,143) {144self.variant_index = variant_index;145self.variant_name = variant_name.into();146self.variant = variant.into();147}148149/// Get a reference to the [`DynamicVariant`] contained in `self`.150pub fn variant(&self) -> &DynamicVariant {151&self.variant152}153154/// Get a mutable reference to the [`DynamicVariant`] contained in `self`.155///156/// Using the mut reference to switch to a different variant will ___not___ update the157/// internal tracking of the variant name and index.158///159/// If you want to switch variants, prefer one of the setters:160/// [`DynamicEnum::set_variant`] or [`DynamicEnum::set_variant_with_index`].161pub fn variant_mut(&mut self) -> &mut DynamicVariant {162&mut self.variant163}164165/// Create a [`DynamicEnum`] from an existing one.166///167/// This is functionally the same as [`DynamicEnum::from_ref`] except it takes an owned value.168pub fn from<TEnum: Enum>(value: TEnum) -> Self {169Self::from_ref(&value)170}171172/// Create a [`DynamicEnum`] from an existing one.173///174/// This is functionally the same as [`DynamicEnum::from`] except it takes a reference.175pub fn from_ref<TEnum: Enum + ?Sized>(value: &TEnum) -> Self {176let type_info = value.get_represented_type_info();177let mut dyn_enum = match value.variant_type() {178VariantType::Unit => DynamicEnum::new_with_index(179value.variant_index(),180value.variant_name(),181DynamicVariant::Unit,182),183VariantType::Tuple => {184let mut data = DynamicTuple::default();185for field in value.iter_fields() {186data.insert_boxed(field.value().to_dynamic());187}188DynamicEnum::new_with_index(189value.variant_index(),190value.variant_name(),191DynamicVariant::Tuple(data),192)193}194VariantType::Struct => {195let mut data = DynamicStruct::default();196for field in value.iter_fields() {197let name = field.name().unwrap();198data.insert_boxed(name, field.value().to_dynamic());199}200DynamicEnum::new_with_index(201value.variant_index(),202value.variant_name(),203DynamicVariant::Struct(data),204)205}206};207208dyn_enum.set_represented_type(type_info);209dyn_enum210}211}212213impl Enum for DynamicEnum {214fn field(&self, name: &str) -> Option<&dyn PartialReflect> {215if let DynamicVariant::Struct(data) = &self.variant {216data.field(name)217} else {218None219}220}221222fn field_at(&self, index: usize) -> Option<&dyn PartialReflect> {223match &self.variant {224DynamicVariant::Tuple(data) => data.field(index),225DynamicVariant::Struct(data) => data.field_at(index),226DynamicVariant::Unit => None,227}228}229230fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect> {231if let DynamicVariant::Struct(data) = &mut self.variant {232data.field_mut(name)233} else {234None235}236}237238fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> {239match &mut self.variant {240DynamicVariant::Tuple(data) => data.field_mut(index),241DynamicVariant::Struct(data) => data.field_at_mut(index),242DynamicVariant::Unit => None,243}244}245246fn index_of(&self, name: &str) -> Option<usize> {247if let DynamicVariant::Struct(data) = &self.variant {248data.index_of_name(name)249} else {250None251}252}253254fn name_at(&self, index: usize) -> Option<&str> {255if let DynamicVariant::Struct(data) = &self.variant {256data.name_at(index)257} else {258None259}260}261262fn iter_fields(&self) -> VariantFieldIter<'_> {263VariantFieldIter::new(self)264}265266fn field_len(&self) -> usize {267match &self.variant {268DynamicVariant::Unit => 0,269DynamicVariant::Tuple(data) => data.field_len(),270DynamicVariant::Struct(data) => data.field_len(),271}272}273274fn variant_name(&self) -> &str {275&self.variant_name276}277278fn variant_index(&self) -> usize {279self.variant_index280}281282fn variant_type(&self) -> VariantType {283match &self.variant {284DynamicVariant::Unit => VariantType::Unit,285DynamicVariant::Tuple(..) => VariantType::Tuple,286DynamicVariant::Struct(..) => VariantType::Struct,287}288}289}290291impl PartialReflect for DynamicEnum {292#[inline]293fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {294self.represented_type295}296297#[inline]298fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {299self300}301302#[inline]303fn as_partial_reflect(&self) -> &dyn PartialReflect {304self305}306307#[inline]308fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {309self310}311312fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {313Err(self)314}315316fn try_as_reflect(&self) -> Option<&dyn Reflect> {317None318}319320fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {321None322}323324#[inline]325fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {326let value = value.reflect_ref().as_enum()?;327328if Enum::variant_name(self) == value.variant_name() {329// Same variant -> just update fields330match value.variant_type() {331VariantType::Struct => {332for field in value.iter_fields() {333let name = field.name().unwrap();334if let Some(v) = Enum::field_mut(self, name) {335v.try_apply(field.value())?;336}337}338}339VariantType::Tuple => {340for (index, field) in value.iter_fields().enumerate() {341if let Some(v) = Enum::field_at_mut(self, index) {342v.try_apply(field.value())?;343}344}345}346_ => {}347}348} else {349// New variant -> perform a switch350let dyn_variant = match value.variant_type() {351VariantType::Unit => DynamicVariant::Unit,352VariantType::Tuple => {353let mut dyn_tuple = DynamicTuple::default();354for field in value.iter_fields() {355dyn_tuple.insert_boxed(field.value().to_dynamic());356}357DynamicVariant::Tuple(dyn_tuple)358}359VariantType::Struct => {360let mut dyn_struct = DynamicStruct::default();361for field in value.iter_fields() {362dyn_struct.insert_boxed(field.name().unwrap(), field.value().to_dynamic());363}364DynamicVariant::Struct(dyn_struct)365}366};367self.set_variant(value.variant_name(), dyn_variant);368}369370Ok(())371}372373#[inline]374fn reflect_kind(&self) -> ReflectKind {375ReflectKind::Enum376}377378#[inline]379fn reflect_ref(&self) -> ReflectRef<'_> {380ReflectRef::Enum(self)381}382383#[inline]384fn reflect_mut(&mut self) -> ReflectMut<'_> {385ReflectMut::Enum(self)386}387388#[inline]389fn reflect_owned(self: Box<Self>) -> ReflectOwned {390ReflectOwned::Enum(self)391}392393#[inline]394fn reflect_hash(&self) -> Option<u64> {395enum_hash(self)396}397398#[inline]399fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {400enum_partial_eq(self, value)401}402403#[inline]404fn reflect_partial_cmp(&self, value: &dyn PartialReflect) -> Option<::core::cmp::Ordering> {405enum_partial_cmp(self, value)406}407408#[inline]409fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result {410write!(f, "DynamicEnum(")?;411enum_debug(self, f)?;412write!(f, ")")413}414415#[inline]416fn is_dynamic(&self) -> bool {417true418}419}420421impl_type_path!((in bevy_reflect) DynamicEnum);422423424