Path: blob/main/crates/bevy_reflect/src/enums/dynamic_enum.rs
6599 views
use bevy_reflect_derive::impl_type_path;12use crate::{3enum_debug, enum_hash, enum_partial_eq, ApplyError, DynamicStruct, DynamicTuple, Enum,4PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple,5TypeInfo, VariantFieldIter, VariantType,6};78use alloc::{boxed::Box, string::String};9use core::fmt::Formatter;10use derive_more::derive::From;1112/// A dynamic representation of an enum variant.13#[derive(Debug, Default, From)]14pub enum DynamicVariant {15/// A unit variant.16#[default]17Unit,18/// A tuple variant.19Tuple(DynamicTuple),20/// A struct variant.21Struct(DynamicStruct),22}2324impl Clone for DynamicVariant {25fn clone(&self) -> Self {26match self {27DynamicVariant::Unit => DynamicVariant::Unit,28DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.to_dynamic_tuple()),29DynamicVariant::Struct(data) => DynamicVariant::Struct(data.to_dynamic_struct()),30}31}32}3334impl From<()> for DynamicVariant {35fn from(_: ()) -> Self {36Self::Unit37}38}3940/// A dynamic representation of an enum.41///42/// This allows for enums to be configured at runtime.43///44/// # Example45///46/// ```47/// # use bevy_reflect::{DynamicEnum, DynamicVariant, Reflect, PartialReflect};48///49/// // The original enum value50/// let mut value: Option<usize> = Some(123);51///52/// // Create a DynamicEnum to represent the new value53/// let mut dyn_enum = DynamicEnum::new(54/// "None",55/// DynamicVariant::Unit56/// );57///58/// // Apply the DynamicEnum as a patch to the original value59/// value.apply(dyn_enum.as_partial_reflect());60///61/// // Tada!62/// assert_eq!(None, value);63/// ```64#[derive(Default, Debug)]65pub struct DynamicEnum {66represented_type: Option<&'static TypeInfo>,67variant_name: String,68variant_index: usize,69variant: DynamicVariant,70}7172impl DynamicEnum {73/// Create a new [`DynamicEnum`] to represent an enum at runtime.74///75/// # Arguments76///77/// * `variant_name`: The name of the variant to set78/// * `variant`: The variant data79pub fn new<I: Into<String>, V: Into<DynamicVariant>>(variant_name: I, variant: V) -> Self {80Self {81represented_type: None,82variant_index: 0,83variant_name: variant_name.into(),84variant: variant.into(),85}86}8788/// Create a new [`DynamicEnum`] with a variant index to represent an enum at runtime.89///90/// # Arguments91///92/// * `variant_index`: The index of the variant to set93/// * `variant_name`: The name of the variant to set94/// * `variant`: The variant data95pub fn new_with_index<I: Into<String>, V: Into<DynamicVariant>>(96variant_index: usize,97variant_name: I,98variant: V,99) -> Self {100Self {101represented_type: None,102variant_index,103variant_name: variant_name.into(),104variant: variant.into(),105}106}107108/// Sets the [type] to be represented by this `DynamicEnum`.109///110/// # Panics111///112/// Panics if the given [type] is not a [`TypeInfo::Enum`].113///114/// [type]: TypeInfo115pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) {116if let Some(represented_type) = represented_type {117assert!(118matches!(represented_type, TypeInfo::Enum(_)),119"expected TypeInfo::Enum but received: {represented_type:?}",120);121}122123self.represented_type = represented_type;124}125126/// Set the current enum variant represented by this struct.127pub fn set_variant<I: Into<String>, V: Into<DynamicVariant>>(&mut self, name: I, variant: V) {128self.variant_name = name.into();129self.variant = variant.into();130}131132/// Set the current enum variant represented by this struct along with its variant index.133pub fn set_variant_with_index<I: Into<String>, V: Into<DynamicVariant>>(134&mut self,135variant_index: usize,136variant_name: I,137variant: V,138) {139self.variant_index = variant_index;140self.variant_name = variant_name.into();141self.variant = variant.into();142}143144/// Get a reference to the [`DynamicVariant`] contained in `self`.145pub fn variant(&self) -> &DynamicVariant {146&self.variant147}148149/// Get a mutable reference to the [`DynamicVariant`] contained in `self`.150///151/// Using the mut reference to switch to a different variant will ___not___ update the152/// internal tracking of the variant name and index.153///154/// If you want to switch variants, prefer one of the setters:155/// [`DynamicEnum::set_variant`] or [`DynamicEnum::set_variant_with_index`].156pub fn variant_mut(&mut self) -> &mut DynamicVariant {157&mut self.variant158}159160/// Create a [`DynamicEnum`] from an existing one.161///162/// This is functionally the same as [`DynamicEnum::from_ref`] except it takes an owned value.163pub fn from<TEnum: Enum>(value: TEnum) -> Self {164Self::from_ref(&value)165}166167/// Create a [`DynamicEnum`] from an existing one.168///169/// This is functionally the same as [`DynamicEnum::from`] except it takes a reference.170pub fn from_ref<TEnum: Enum + ?Sized>(value: &TEnum) -> Self {171let type_info = value.get_represented_type_info();172let mut dyn_enum = match value.variant_type() {173VariantType::Unit => DynamicEnum::new_with_index(174value.variant_index(),175value.variant_name(),176DynamicVariant::Unit,177),178VariantType::Tuple => {179let mut data = DynamicTuple::default();180for field in value.iter_fields() {181data.insert_boxed(field.value().to_dynamic());182}183DynamicEnum::new_with_index(184value.variant_index(),185value.variant_name(),186DynamicVariant::Tuple(data),187)188}189VariantType::Struct => {190let mut data = DynamicStruct::default();191for field in value.iter_fields() {192let name = field.name().unwrap();193data.insert_boxed(name, field.value().to_dynamic());194}195DynamicEnum::new_with_index(196value.variant_index(),197value.variant_name(),198DynamicVariant::Struct(data),199)200}201};202203dyn_enum.set_represented_type(type_info);204dyn_enum205}206}207208impl Enum for DynamicEnum {209fn field(&self, name: &str) -> Option<&dyn PartialReflect> {210if let DynamicVariant::Struct(data) = &self.variant {211data.field(name)212} else {213None214}215}216217fn field_at(&self, index: usize) -> Option<&dyn PartialReflect> {218match &self.variant {219DynamicVariant::Tuple(data) => data.field(index),220DynamicVariant::Struct(data) => data.field_at(index),221DynamicVariant::Unit => None,222}223}224225fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect> {226if let DynamicVariant::Struct(data) = &mut self.variant {227data.field_mut(name)228} else {229None230}231}232233fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> {234match &mut self.variant {235DynamicVariant::Tuple(data) => data.field_mut(index),236DynamicVariant::Struct(data) => data.field_at_mut(index),237DynamicVariant::Unit => None,238}239}240241fn index_of(&self, name: &str) -> Option<usize> {242if let DynamicVariant::Struct(data) = &self.variant {243data.index_of(name)244} else {245None246}247}248249fn name_at(&self, index: usize) -> Option<&str> {250if let DynamicVariant::Struct(data) = &self.variant {251data.name_at(index)252} else {253None254}255}256257fn iter_fields(&self) -> VariantFieldIter<'_> {258VariantFieldIter::new(self)259}260261fn field_len(&self) -> usize {262match &self.variant {263DynamicVariant::Unit => 0,264DynamicVariant::Tuple(data) => data.field_len(),265DynamicVariant::Struct(data) => data.field_len(),266}267}268269fn variant_name(&self) -> &str {270&self.variant_name271}272273fn variant_index(&self) -> usize {274self.variant_index275}276277fn variant_type(&self) -> VariantType {278match &self.variant {279DynamicVariant::Unit => VariantType::Unit,280DynamicVariant::Tuple(..) => VariantType::Tuple,281DynamicVariant::Struct(..) => VariantType::Struct,282}283}284}285286impl PartialReflect for DynamicEnum {287#[inline]288fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {289self.represented_type290}291292#[inline]293fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {294self295}296297#[inline]298fn as_partial_reflect(&self) -> &dyn PartialReflect {299self300}301302#[inline]303fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {304self305}306307fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {308Err(self)309}310311fn try_as_reflect(&self) -> Option<&dyn Reflect> {312None313}314315fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {316None317}318319#[inline]320fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {321let value = value.reflect_ref().as_enum()?;322323if Enum::variant_name(self) == value.variant_name() {324// Same variant -> just update fields325match value.variant_type() {326VariantType::Struct => {327for field in value.iter_fields() {328let name = field.name().unwrap();329if let Some(v) = Enum::field_mut(self, name) {330v.try_apply(field.value())?;331}332}333}334VariantType::Tuple => {335for (index, field) in value.iter_fields().enumerate() {336if let Some(v) = Enum::field_at_mut(self, index) {337v.try_apply(field.value())?;338}339}340}341_ => {}342}343} else {344// New variant -> perform a switch345let dyn_variant = match value.variant_type() {346VariantType::Unit => DynamicVariant::Unit,347VariantType::Tuple => {348let mut dyn_tuple = DynamicTuple::default();349for field in value.iter_fields() {350dyn_tuple.insert_boxed(field.value().to_dynamic());351}352DynamicVariant::Tuple(dyn_tuple)353}354VariantType::Struct => {355let mut dyn_struct = DynamicStruct::default();356for field in value.iter_fields() {357dyn_struct.insert_boxed(field.name().unwrap(), field.value().to_dynamic());358}359DynamicVariant::Struct(dyn_struct)360}361};362self.set_variant(value.variant_name(), dyn_variant);363}364365Ok(())366}367368#[inline]369fn reflect_kind(&self) -> ReflectKind {370ReflectKind::Enum371}372373#[inline]374fn reflect_ref(&self) -> ReflectRef<'_> {375ReflectRef::Enum(self)376}377378#[inline]379fn reflect_mut(&mut self) -> ReflectMut<'_> {380ReflectMut::Enum(self)381}382383#[inline]384fn reflect_owned(self: Box<Self>) -> ReflectOwned {385ReflectOwned::Enum(self)386}387388#[inline]389fn reflect_hash(&self) -> Option<u64> {390enum_hash(self)391}392393#[inline]394fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {395enum_partial_eq(self, value)396}397398#[inline]399fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result {400write!(f, "DynamicEnum(")?;401enum_debug(self, f)?;402write!(f, ")")403}404405#[inline]406fn is_dynamic(&self) -> bool {407true408}409}410411impl_type_path!((in bevy_reflect) DynamicEnum);412413414