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;
#[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
}
}
#[derive(Default, Debug)]
pub struct DynamicEnum {
represented_type: Option<&'static TypeInfo>,
variant_name: String,
variant_index: usize,
variant: DynamicVariant,
}
impl DynamicEnum {
pub fn new<I: Into<String>, V: Into<DynamicVariant>>(variant_name: I, variant: V) -> Self {
Self {
represented_type: None,
variant_index: 0,
variant_name: variant_name.into(),
variant: variant.into(),
}
}
pub fn new_with_index<I: Into<String>, V: Into<DynamicVariant>>(
variant_index: usize,
variant_name: I,
variant: V,
) -> Self {
Self {
represented_type: None,
variant_index,
variant_name: variant_name.into(),
variant: variant.into(),
}
}
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;
}
pub fn set_variant<I: Into<String>, V: Into<DynamicVariant>>(&mut self, name: I, variant: V) {
self.variant_name = name.into();
self.variant = variant.into();
}
pub fn set_variant_with_index<I: Into<String>, V: Into<DynamicVariant>>(
&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();
}
pub fn from<TEnum: Enum>(value: TEnum) -> Self {
Self::from_ref(&value)
}
pub fn from_ref<TEnum: Enum>(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<usize> {
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<Self>) -> Box<dyn PartialReflect> {
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<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {
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() {
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 {
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<Self>) -> ReflectOwned {
ReflectOwned::Enum(self)
}
#[inline]
fn clone_value(&self) -> Box<dyn PartialReflect> {
Box::new(self.clone_dynamic())
}
#[inline]
fn reflect_hash(&self) -> Option<u64> {
enum_hash(self)
}
#[inline]
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
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);