diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 73371602f1b6a..fbd0692f8b3c4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -15,8 +15,8 @@ use syn::{ parse_macro_input, punctuated::Punctuated, token::{Comma, Paren, Where}, - Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, Member, Meta, NestedMeta, - Path, + Data, DataEnum, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, Member, Meta, + NestedMeta, Path, Token, Variant, }; #[derive(Default)] @@ -41,70 +41,111 @@ enum DeriveType { Struct, TupleStruct, UnitStruct, + Enum, Value, } +enum Item<'a> { + Field(&'a Field), + Variant(&'a Variant), +} + static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; #[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let unit_struct_punctuated = Punctuated::new(); - let (fields, mut derive_type) = match &ast.data { + let unit_struct_punctuated = Punctuated::::new(); + let (items, mut derive_type): (Vec, DeriveType) = match &ast.data { Data::Struct(DataStruct { fields: Fields::Named(fields), .. - }) => (&fields.named, DeriveType::Struct), + }) => ( + fields + .named + .iter() + .map(|field| Item::Field(field)) + .collect(), + DeriveType::Struct, + ), Data::Struct(DataStruct { fields: Fields::Unnamed(fields), .. - }) => (&fields.unnamed, DeriveType::TupleStruct), + }) => ( + fields + .unnamed + .iter() + .map(|field| Item::Field(field)) + .collect(), + DeriveType::TupleStruct, + ), Data::Struct(DataStruct { fields: Fields::Unit, .. - }) => (&unit_struct_punctuated, DeriveType::UnitStruct), - _ => (&unit_struct_punctuated, DeriveType::Value), + }) => ( + unit_struct_punctuated + .iter() + .map(|field| Item::Field(field)) + .collect(), + DeriveType::UnitStruct, + ), + Data::Enum(DataEnum { variants, .. }) => ( + variants + .iter() + .map(|variant| Item::Variant(variant)) + .collect(), + DeriveType::Enum, + ), + _ => ( + unit_struct_punctuated + .iter() + .map(|field| Item::Field(field)) + .collect(), + DeriveType::Value, + ), }; - - let fields_and_args = fields + let items_and_args = items .iter() .enumerate() - .map(|(i, f)| { + .map(|(i, item)| { ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); + item, + match *item { + Item::Field(field) => &field.attrs, + Item::Variant(variant) => &variant.attrs, + } + .iter() + .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) + .map(|a| { + syn::custom_keyword!(ignore); + let mut attribute_args = PropAttributeArgs { ignore: None }; + a.parse_args_with(|input: ParseStream| { + if input.parse::>()?.is_some() { + attribute_args.ignore = Some(true); + return Ok(()); + } + Ok(()) + }) + .expect("Invalid 'property' attribute format."); - attribute_args - }), + attribute_args + }), i, ) }) - .collect::, usize)>>(); - let active_fields = fields_and_args + .collect::, usize)>>(); + let active_items = items_and_args .iter() - .filter(|(_field, attrs, _i)| { + .filter(|(_item, attrs, _i)| { attrs.is_none() || match attrs.as_ref().unwrap().ignore { Some(ignore) => !ignore, None => true, } }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); + .map(|(item, _attr, i)| (*item, *i)) + .collect::>(); let modules = get_modules(); let bevy_reflect_path = get_path(&modules.bevy_reflect); @@ -137,22 +178,54 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { ); match derive_type { - DeriveType::Struct | DeriveType::UnitStruct => impl_struct( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), - DeriveType::TupleStruct => impl_tuple_struct( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), + DeriveType::Struct | DeriveType::UnitStruct => { + let active_fields = active_items + .iter() + .map(|(item, i)| { + ( + match *item { + Item::Field(field) => *field, + Item::Variant(_) => { + unreachable!() + } + }, + *i, + ) + }) + .collect::>(); + impl_struct( + type_name, + &ast.generics, + get_type_registration_impl, + &bevy_reflect_path, + &reflect_attrs, + &active_fields, + ) + } + DeriveType::TupleStruct => { + let active_fields = active_items + .iter() + .map(|(item, i)| { + ( + match *item { + Item::Field(field) => *field, + Item::Variant(_) => { + unreachable!() + } + }, + *i, + ) + }) + .collect::>(); + impl_tuple_struct( + type_name, + &ast.generics, + get_type_registration_impl, + &bevy_reflect_path, + &reflect_attrs, + &active_fields, + ) + } DeriveType::Value => impl_value( type_name, &ast.generics, @@ -160,6 +233,28 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { &bevy_reflect_path, &reflect_attrs, ), + DeriveType::Enum => { + let active_variants = active_items + .iter() + .map(|(item, i)| { + ( + match *item { + Item::Field(_) => unreachable!(), + Item::Variant(variant) => *variant, + }, + *i, + ) + }) + .collect::>(); + impl_enum( + type_name, + &ast.generics, + get_type_registration_impl, + &bevy_reflect_path, + &reflect_attrs, + &active_variants, + ) + } } } @@ -318,10 +413,26 @@ fn impl_struct( fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { #partial_eq_fn } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } } }) } +fn tuple_field_name(i: usize) -> String { + format!("t{}", i) +} + +fn tuple_field_ident(i: usize) -> Ident { + Ident::new(tuple_field_name(i).as_str(), Span::call_site()) +} + fn impl_tuple_struct( struct_name: &Ident, generics: &Generics, @@ -437,6 +548,14 @@ fn impl_tuple_struct( fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { #partial_eq_fn } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } } }) } @@ -512,9 +631,550 @@ fn impl_value( fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { #serialize_fn } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } } }) } + +fn impl_enum( + enum_name: &Ident, + generics: &Generics, + get_type_registration_impl: proc_macro2::TokenStream, + bevy_reflect_path: &Path, + reflect_attrs: &ReflectAttrs, + active_variants: &[(&Variant, usize)], +) -> TokenStream { + let mut variant_indices = Vec::new(); + let mut struct_wrappers = Vec::new(); + let mut tuple_wrappers = Vec::new(); + let mut variant_names = Vec::new(); + let mut variant_idents = Vec::new(); + let mut reflect_variants = Vec::new(); + let mut reflect_variants_mut = Vec::new(); + let mut variant_with_fields_idents = Vec::new(); + let mut variant_without_fields_idents = Vec::new(); + for (variant, variant_index) in active_variants.iter() { + let variant_ident = { + let ident = &variant.ident; + quote!(#enum_name::#ident) + }; + let variant_name = variant_ident + .to_string() + .chars() + .filter(|c| !c.is_whitespace()) + .collect::(); + let variant_without_fields_ident = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!(#variant_ident {..}) + } + Fields::Unnamed(tuple) => { + let tuple_fields = &tuple.unnamed; + if tuple_fields.len() == 1 { + quote!(#variant_ident (_)) + } else { + quote!(#variant_ident (..)) + } + } + Fields::Unit => { + quote!(#variant_ident) + } + } + }; + let variant_with_fields_ident = { + match &variant.fields { + Fields::Named(struct_fields) => { + let field_idents = struct_fields + .named + .iter() + .map(|field| field.ident.as_ref().unwrap()) + .collect::>(); + quote!(#variant_ident {#(#field_idents,)*}) + } + Fields::Unnamed(tuple_fields) => { + let field_idents = (0..tuple_fields.unnamed.len()) + .map(tuple_field_ident) + .collect::>(); + if tuple_fields.unnamed.len() == 1 { + quote!(#variant_ident (new_type)) + } else { + quote!(#variant_ident (#(#field_idents,)*)) + } + } + Fields::Unit => { + quote!(#variant_ident) + } + } + }; + let wrapper_ident = if let Fields::Named(_) | Fields::Unnamed(_) = &variant.fields { + Ident::new( + format!("{}{}Wrapper", enum_name, variant.ident).as_str(), + Span::call_site(), + ) + } else { + Ident::new("unused", Span::call_site()) + }; + let wrapper_name = match &variant.fields { + Fields::Named(struct_fields) => quote!(#struct_fields).to_string(), + Fields::Unnamed(tuple_fields) => quote!(#tuple_fields).to_string(), + Fields::Unit => "unused".to_string(), + }; + let reflect_variant = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariant::Struct(wrapper_ref as &dyn #bevy_reflect_path::Struct) + }) + } + Fields::Unnamed(tuple_fields) => { + if tuple_fields.unnamed.len() == 1 { + quote!(#bevy_reflect_path::EnumVariant::NewType(new_type as &dyn #bevy_reflect_path::Reflect)) + } else { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariant::Tuple(wrapper_ref as &dyn #bevy_reflect_path::Tuple) + }) + } + } + Fields::Unit => { + quote!(#bevy_reflect_path::EnumVariant::Unit) + } + } + }; + let reflect_variant_mut = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariantMut::Struct(wrapper_ref as &mut dyn #bevy_reflect_path::Struct) + }) + } + Fields::Unnamed(tuple) => { + let tuple_fields = &tuple.unnamed; + if tuple_fields.len() == 1 { + quote!(#bevy_reflect_path::EnumVariantMut::NewType(new_type as &mut dyn #bevy_reflect_path::Reflect)) + } else { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariantMut::Tuple(wrapper_ref as &mut dyn #bevy_reflect_path::Tuple) + }) + } + } + Fields::Unit => { + quote!(#bevy_reflect_path::EnumVariantMut::Unit) + } + } + }; + match &variant.fields { + Fields::Named(struct_fields) => { + struct_wrappers.push(( + wrapper_ident, + wrapper_name, + variant_index, + variant_with_fields_ident.clone(), + struct_fields.clone(), + )); + } + Fields::Unnamed(tuple_fields) => { + if tuple_fields.unnamed.len() > 1 { + tuple_wrappers.push(( + wrapper_ident, + wrapper_name, + variant_index, + variant_with_fields_ident.clone(), + tuple_fields.clone(), + )); + } + } + Fields::Unit => {} + } + variant_indices.push(variant_index); + variant_names.push(variant_name); + variant_idents.push(variant_ident); + reflect_variants.push(reflect_variant); + reflect_variants_mut.push(reflect_variant_mut); + variant_with_fields_idents.push(variant_with_fields_ident); + variant_without_fields_idents.push(variant_without_fields_ident); + } + let hash_fn = reflect_attrs.get_hash_impl(&bevy_reflect_path); + let serialize_fn = reflect_attrs.get_serialize_impl(&bevy_reflect_path); + let partial_eq_fn = match reflect_attrs.reflect_partial_eq { + TraitImpl::NotImplemented => quote! { + use #bevy_reflect_path::Enum; + #bevy_reflect_path::enum_partial_eq(self, value) + }, + TraitImpl::Implemented | TraitImpl::Custom(_) => reflect_attrs.get_partial_eq_impl(), + }; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let mut token_stream = TokenStream::from(quote! { + #get_type_registration_impl + + impl #impl_generics #bevy_reflect_path::Enum for #enum_name#ty_generics #where_clause { + fn variant(&self) -> #bevy_reflect_path::EnumVariant<'_> { + match self { + #(#variant_with_fields_idents => #reflect_variants,)* + } + } + + fn variant_mut(&mut self) -> #bevy_reflect_path::EnumVariantMut<'_> { + match self { + #(#variant_with_fields_idents => #reflect_variants_mut,)* + } + } + + fn variant_info(&self) -> #bevy_reflect_path::VariantInfo<'_> { + let index = match self { + #(#variant_without_fields_idents => #variant_indices,)* + }; + #bevy_reflect_path::VariantInfo { + index, + name: self.get_index_name(index).unwrap(), + } + } + + fn get_index_name(&self, index: usize) -> Option<&'_ str> { + match index { + #(#variant_indices => Some(#variant_names),)* + _ => None, + } + } + + fn get_index_from_name(&self, name: &str) -> Option { + match name { + #(#variant_names => Some(#variant_indices),)* + _ => None, + } + } + + fn iter_variants_info(&self) -> #bevy_reflect_path::VariantInfoIter<'_> { + #bevy_reflect_path::VariantInfoIter::new(self) + } + } + + impl #impl_generics #bevy_reflect_path::Reflect for #enum_name#ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + #[inline] + fn clone_value(&self) -> Box { + use #bevy_reflect_path::Enum; + Box::new(self.clone()) // FIXME: should be clone_dynamic, so that Clone is not a required bound + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + use #bevy_reflect_path::Enum; + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); //FIXME: should apply the variant instead + } else { + panic!("Attempted to apply non-enum type to enum type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Enum(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Enum(self) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + }); + for (wrapper_ident, wrapper_name, variant_index, variant_with_fields_ident, fields) in + struct_wrappers + { + let mut field_names = Vec::new(); + let mut field_idents = Vec::new(); + let mut field_indices = Vec::new(); + for (i, field) in fields.named.iter().enumerate() { + field_names.push(field.ident.as_ref().unwrap().to_string()); + field_idents.push(field.ident.clone()); + field_indices.push(i); + } + let fields_len = field_indices.len(); + let match_fields = quote!( + #variant_with_fields_ident => (#(#field_idents,)*), + _ => unreachable!(), + ); + let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { + #match_fields + };); + let match_fields = quote!(let (#(#field_idents,)*) = match &self.0 { + #match_fields + };); + token_stream.extend(TokenStream::from(quote! { + #[repr(transparent)] + pub struct #wrapper_ident(#enum_name); + impl #bevy_reflect_path::Reflect for #wrapper_ident { + fn type_name(&self) -> &str { + #wrapper_name + } + + fn any(&self) -> &dyn std::any::Any { + self.0.any() + } + + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self.0.any_mut() + } + + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + self.0.apply(value); + } + + fn set(&mut self, value: Box) -> Result<(), Box> { + self.0.set(value) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Struct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Struct(self) + } + + fn clone_value(&self) -> Box { + self.0.clone_value() + } + + fn reflect_hash(&self) -> Option { + self.0.reflect_hash() + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + self.0.reflect_partial_eq(value) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + self.0.serializable() + } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + impl #bevy_reflect_path::Struct for #wrapper_ident { + fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match name { + #(#field_names => Some(#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match name { + #(#field_names => Some(#field_idents),)* + _ => None, + } + } + + fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + fn name_at(&self, index: usize) -> Option<&str> { + match index { + #(#field_indices => Some(#field_names),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #fields_len + } + + fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { + #bevy_reflect_path::FieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { + #match_fields + let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(#field_names, #field_idents.clone_value());)* + dynamic + } + } + })); + } + for (wrapper_ident, wrapper_name, variant_index, variant_with_fields_ident, fields) in + tuple_wrappers + { + let mut field_names = Vec::new(); + let mut field_idents = Vec::new(); + let mut field_indices = Vec::new(); + for (index, _field) in fields.unnamed.iter().enumerate() { + field_names.push(tuple_field_name(index)); + field_idents.push(tuple_field_ident(index)); + field_indices.push(index); + } + let fields_len = field_indices.len(); + let match_fields = quote!( + #variant_with_fields_ident => (#(#field_idents,)*), + _ => unreachable!(), + ); + let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { + #match_fields + };); + let match_fields = quote!(let (#(#field_idents,)*) = match &self.0 { + #match_fields + };); + token_stream.extend(TokenStream::from(quote! { + #[repr(transparent)] + pub struct #wrapper_ident(#enum_name); + impl #bevy_reflect_path::Reflect for #wrapper_ident { + fn type_name(&self) -> &str { + #wrapper_name + } + + fn any(&self) -> &dyn std::any::Any { + self.0.any() + } + + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self.0.any_mut() + } + + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + self.0.apply(value); + } + + fn set(&mut self, value: Box) -> Result<(), Box> { + self.0.set(value) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Tuple(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Tuple(self) + } + + fn clone_value(&self) -> Box { + self.0.clone_value() + } + + fn reflect_hash(&self) -> Option { + self.0.reflect_hash() + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + self.0.reflect_partial_eq(value) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + self.0.serializable() + } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + impl #bevy_reflect_path::Tuple for #wrapper_ident { + fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #fields_len + } + + fn iter_fields(&self) -> #bevy_reflect_path::TupleFieldIter { + #bevy_reflect_path::TupleFieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTuple { + #match_fields + let mut dynamic = #bevy_reflect_path::DynamicTuple::default(); + #(dynamic.insert_boxed(#field_idents.clone_value());)* + dynamic + } + } + })); + } + token_stream +} struct ReflectDef { type_name: Ident, generics: Generics, diff --git a/crates/bevy_reflect/src/enum_trait.rs b/crates/bevy_reflect/src/enum_trait.rs new file mode 100644 index 0000000000000..16eb90c7f49fc --- /dev/null +++ b/crates/bevy_reflect/src/enum_trait.rs @@ -0,0 +1,103 @@ +use crate::{Reflect, ReflectRef, Struct, Tuple}; + +pub trait Enum: Reflect { + fn variant(&self) -> EnumVariant<'_>; + fn variant_mut(&mut self) -> EnumVariantMut<'_>; + fn variant_info(&self) -> VariantInfo<'_>; + fn iter_variants_info(&self) -> VariantInfoIter<'_>; + fn get_index_name(&self, index: usize) -> Option<&str>; + fn get_index_from_name(&self, name: &str) -> Option; +} +#[derive(PartialEq, Eq)] +pub struct VariantInfo<'a> { + pub index: usize, + pub name: &'a str, +} +pub struct VariantInfoIter<'a> { + pub(crate) value: &'a dyn Enum, + pub(crate) index: usize, +} +impl<'a> VariantInfoIter<'a> { + pub fn new(value: &'a dyn Enum) -> Self { + Self { value, index: 0 } + } +} +impl<'a> Iterator for VariantInfoIter<'a> { + type Item = VariantInfo<'a>; + + fn next(&mut self) -> Option { + let value = self + .value + .get_index_name(self.index) + .map(|name| VariantInfo { + index: self.index, + name, + }); + self.index += 1; + value + } +} + +pub enum EnumVariant<'a> { + Unit, + NewType(&'a dyn Reflect), + Tuple(&'a dyn Tuple), + Struct(&'a dyn Struct), +} +pub enum EnumVariantMut<'a> { + Unit, + NewType(&'a mut dyn Reflect), + Tuple(&'a mut dyn Tuple), + Struct(&'a mut dyn Struct), +} + +#[inline] +pub fn enum_partial_eq(enum_a: &E, reflect_b: &dyn Reflect) -> Option { + let enum_b = if let ReflectRef::Enum(e) = reflect_b.reflect_ref() { + e + } else { + return Some(false); + }; + + if enum_a.variant_info() != enum_b.variant_info() { + return Some(false); + } + + let variant_b = enum_b.variant(); + match enum_a.variant() { + EnumVariant::Unit => { + if let EnumVariant::Unit = variant_b { + } else { + return Some(false); + } + } + EnumVariant::NewType(t_a) => { + if let EnumVariant::NewType(t_b) = variant_b { + if let Some(false) | None = t_b.reflect_partial_eq(t_a) { + return Some(false); + } + } else { + return Some(false); + } + } + EnumVariant::Tuple(t_a) => { + if let EnumVariant::Tuple(t_b) = variant_b { + if let Some(false) | None = t_b.reflect_partial_eq(t_a.as_reflect()) { + return Some(false); + } + } else { + return Some(false); + } + } + EnumVariant::Struct(s_a) => { + if let EnumVariant::Struct(s_b) = variant_b { + if let Some(false) | None = s_b.reflect_partial_eq(s_a.as_reflect()) { + return Some(false); + } + } else { + return Some(false); + } + } + } + Some(true) +} diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index 589bd4c0b1327..f883bf0a8e1b0 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -93,4 +93,12 @@ where fn serializable(&self) -> Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index feae40b69a3fd..a97df087b8593 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,6 +1,7 @@ use crate::{ - map_partial_eq, serde::Serializable, DynamicMap, List, ListIter, Map, MapIter, Reflect, - ReflectDeserialize, ReflectMut, ReflectRef, + map_partial_eq, serde::Serializable, DynamicMap, Enum, EnumVariant, EnumVariantMut, + GetTypeRegistration, List, ListIter, Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, + ReflectRef, TypeRegistration, VariantInfo, VariantInfoIter, }; use bevy_reflect_derive::impl_reflect_value; @@ -29,7 +30,6 @@ impl_reflect_value!(isize(Hash, PartialEq, Serialize, Deserialize)); impl_reflect_value!(f32(Serialize, Deserialize)); impl_reflect_value!(f64(Serialize, Deserialize)); impl_reflect_value!(String(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(Option Deserialize<'de> + Reflect + 'static>(Serialize, Deserialize)); impl_reflect_value!(HashSet Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); impl_reflect_value!(Range Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); @@ -109,6 +109,14 @@ impl Reflect for Vec { fn serializable(&self) -> Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } impl Map for HashMap { @@ -203,6 +211,14 @@ impl Reflect for HashMap Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } impl Reflect for Cow<'static, str> { @@ -263,4 +279,131 @@ impl Reflect for Cow<'static, str> { fn serializable(&self) -> Option { Some(Serializable::Borrowed(self)) } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } +} + +impl GetTypeRegistration for Option { + fn get_type_registration() -> TypeRegistration { + TypeRegistration::of::>() + } +} +impl Enum for Option { + fn variant(&self) -> EnumVariant<'_> { + match self { + Option::Some(new_type) => EnumVariant::NewType(new_type as &dyn Reflect), + Option::None => EnumVariant::Unit, + } + } + + fn variant_mut(&mut self) -> EnumVariantMut<'_> { + match self { + Option::Some(new_type) => EnumVariantMut::NewType(new_type as &mut dyn Reflect), + Option::None => EnumVariantMut::Unit, + } + } + + fn variant_info(&self) -> VariantInfo<'_> { + let index = match self { + Option::Some(_) => 0usize, + Option::None => 1usize, + }; + VariantInfo { + index, + name: self.get_index_name(index).unwrap(), + } + } + + fn get_index_name(&self, index: usize) -> Option<&'_ str> { + match index { + 0usize => Some("Option::Some"), + 1usize => Some("Option::None"), + _ => None, + } + } + + fn get_index_from_name(&self, name: &str) -> Option { + match name { + "Option::Some" => Some(0usize), + "Option::None" => Some(1usize), + _ => None, + } + } + + fn iter_variants_info(&self) -> VariantInfoIter<'_> { + VariantInfoIter::new(self) + } +} +impl Reflect for Option { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone()) + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn Reflect) { + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + { + panic!("Enum is not {}.", &std::any::type_name::()); + }; + } + } + + fn reflect_ref(&self) -> ReflectRef { + ReflectRef::Enum(self) + } + + fn reflect_mut(&mut self) -> ReflectMut { + ReflectMut::Enum(self) + } + + fn serializable(&self) -> Option { + None + } + + fn reflect_hash(&self) -> Option { + None + } + + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + crate::enum_partial_eq(self, value) + } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index cdfbf0a19bd8f..4dde6801fe676 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -1,3 +1,4 @@ +mod enum_trait; mod list; mod map; mod path; @@ -41,6 +42,7 @@ pub mod prelude { }; } +pub use enum_trait::*; pub use impls::*; pub use list::*; pub use map::*; diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 4506bb3ba0be8..5fa66700fd5ce 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -122,6 +122,14 @@ impl Reflect for DynamicList { fn serializable(&self) -> Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } pub struct ListIter<'a> { diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index eb5a89467f299..03d3f5ec44c7e 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -141,6 +141,14 @@ impl Reflect for DynamicMap { fn serializable(&self) -> Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } pub struct MapIter<'a> { diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 28ccf3e428d74..c2461b9eb277f 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -1,4 +1,4 @@ -use crate::{serde::Serializable, List, Map, Struct, Tuple, TupleStruct}; +use crate::{serde::Serializable, Enum, List, Map, Struct, Tuple, TupleStruct}; use std::{any::Any, fmt::Debug}; pub use bevy_utils::AHasher as ReflectHasher; @@ -9,6 +9,7 @@ pub enum ReflectRef<'a> { Tuple(&'a dyn Tuple), List(&'a dyn List), Map(&'a dyn Map), + Enum(&'a dyn Enum), Value(&'a dyn Reflect), } @@ -18,6 +19,7 @@ pub enum ReflectMut<'a> { Tuple(&'a mut dyn Tuple), List(&'a mut dyn List), Map(&'a mut dyn Map), + Enum(&'a mut dyn Enum), Value(&'a mut dyn Reflect), } @@ -37,6 +39,8 @@ pub trait Reflect: Any + Send + Sync { fn reflect_partial_eq(&self, _value: &dyn Reflect) -> Option; /// Returns a serializable value, if serialization is supported. Otherwise `None` will be returned. fn serializable(&self) -> Option; + fn as_reflect(&self) -> &dyn Reflect; + fn as_reflect_mut(&mut self) -> &mut dyn Reflect; } impl Debug for dyn Reflect { diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index a865bd13b438b..db5341546e5f6 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -77,6 +77,9 @@ impl<'a> Serialize for ReflectSerializer<'a> { value, } .serialize(serializer), + ReflectRef::Enum(_value) => { + todo!() + } } } } diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 764d36a336cb8..7cbf040de308d 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -227,6 +227,14 @@ impl Reflect for DynamicStruct { fn serializable(&self) -> Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } #[inline] diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index 901a8610e5e11..a8a0120cf89ca 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -165,6 +165,14 @@ impl Reflect for DynamicTuple { fn serializable(&self) -> Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } #[inline] @@ -291,6 +299,14 @@ macro_rules! impl_reflect_tuple { fn serializable(&self) -> Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } } } diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 502d836f1c4e8..66f7d4553fd50 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -182,6 +182,14 @@ impl Reflect for DynamicTupleStruct { fn serializable(&self) -> Option { None } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } } #[inline] diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index 25de5fbb90048..611d82d4874ac 100644 --- a/examples/reflection/reflection_types.rs +++ b/examples/reflection/reflection_types.rs @@ -75,6 +75,9 @@ fn setup() { // operations on your type, allowing you to interact with fields via their indices. Tuple is automatically // implemented for tuples of arity 12 or less. ReflectRef::Tuple(_) => {} + // `Enum` is a trait automatically implemented for enums that derive Reflect. This trait allows you + // to interact list possible variants and interact with the currently active one + ReflectRef::Enum(_) => {} // `List` is a special trait that can be manually implemented (instead of deriving Reflect). This exposes "list" // operations on your type, such as indexing and insertion. List is automatically implemented for relevant core // types like Vec