diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 49190c83a239e..99513ed3acbd4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -97,7 +97,7 @@ pub(crate) enum TraitImpl { /// /// > __Note:__ Registering a custom function only works for special traits. /// -#[derive(Default)] +#[derive(Default, Clone)] pub(crate) struct ReflectTraits { debug: TraitImpl, hash: TraitImpl, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 219fe8158bcac..422809d6db804 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,61 +1,112 @@ use crate::container_attributes::ReflectTraits; use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; -use crate::utility::get_bevy_reflect_path; -use crate::{REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; -use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; - -pub(crate) enum DeriveType { - Struct, - TupleStruct, - UnitStruct, - Value, -} +use quote::quote; -/// Represents a field on a struct or tuple struct. -pub(crate) struct StructField<'a> { - /// The raw field. - pub data: &'a Field, - /// The reflection-based attributes on the field. - pub attrs: ReflectFieldAttr, - /// The index of this field within the struct. - pub index: usize, +use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant}; + +pub(crate) enum ReflectDerive<'a> { + Struct(ReflectStruct<'a>), + TupleStruct(ReflectStruct<'a>), + UnitStruct(ReflectStruct<'a>), + Enum(ReflectEnum<'a>), + Value(ReflectMeta<'a>), } -/// Data used by derive macros for `Reflect` and `FromReflect` +/// Metadata present on all reflected types, including name, generics, and attributes. /// /// # Example +/// /// ```ignore -/// // attrs +/// #[derive(Reflect)] +/// // traits /// // |----------------------------------------| /// #[reflect(PartialEq, Serialize, Deserialize, Default)] /// // type_name generics /// // |-------------------||----------| +/// struct ThingThatImReflecting {/* ... */} +/// ``` +pub(crate) struct ReflectMeta<'a> { + /// The registered traits for this type. + traits: ReflectTraits, + /// The name of this type. + type_name: &'a Ident, + /// The generics defined on this type. + generics: &'a Generics, + /// A cached instance of the path to the `bevy_reflect` crate. + bevy_reflect_path: Path, +} + +/// Struct data used by derive macros for `Reflect` and `FromReflect`. +/// +/// # Example +/// +/// ```ignore +/// #[derive(Reflect)] +/// #[reflect(PartialEq, Serialize, Deserialize, Default)] /// struct ThingThatImReflecting { /// x: T1, // | /// y: T2, // |- fields /// z: T3 // | /// } /// ``` -pub(crate) struct ReflectDeriveData<'a> { - derive_type: DeriveType, - traits: ReflectTraits, - type_name: &'a Ident, - generics: &'a Generics, +pub(crate) struct ReflectStruct<'a> { + meta: ReflectMeta<'a>, fields: Vec>, - bevy_reflect_path: Path, } -impl<'a> ReflectDeriveData<'a> { - pub fn from_input(input: &'a DeriveInput) -> Result { - let mut output = Self { - type_name: &input.ident, - derive_type: DeriveType::Value, - generics: &input.generics, - fields: Vec::new(), - traits: ReflectTraits::default(), - bevy_reflect_path: get_bevy_reflect_path(), - }; +/// Enum data used by derive macros for `Reflect` and `FromReflect`. +/// +/// # Example +/// +/// ```ignore +/// #[derive(Reflect)] +/// #[reflect(PartialEq, Serialize, Deserialize, Default)] +/// enum ThingThatImReflecting { +/// A(T1), // | +/// B, // |- variants +/// C { foo: T2, bar: T3 } // | +/// } +/// ``` +pub(crate) struct ReflectEnum<'a> { + meta: ReflectMeta<'a>, + variants: Vec>, +} +/// Represents a field on a struct or tuple struct. +pub(crate) struct StructField<'a> { + /// The raw field. + pub data: &'a Field, + /// The reflection-based attributes on the field. + pub attrs: ReflectFieldAttr, + /// The index of this field within the struct. + pub index: usize, +} + +/// Represents a variant on an enum. +pub(crate) struct EnumVariant<'a> { + /// The raw variant. + pub data: &'a Variant, + /// The fields within this variant. + pub fields: EnumVariantFields<'a>, + /// The reflection-based attributes on the variant. + pub attrs: ReflectFieldAttr, + /// The index of this variant within the enum. + #[allow(dead_code)] + pub index: usize, +} + +pub(crate) enum EnumVariantFields<'a> { + Named(Vec>), + Unnamed(Vec>), + Unit, +} + +impl<'a> ReflectDerive<'a> { + pub fn from_input(input: &'a DeriveInput) -> Result { + let mut traits = ReflectTraits::default(); // Should indicate whether `#[reflect_value]` was used let mut force_reflect_value = false; @@ -68,95 +119,105 @@ impl<'a> ReflectDeriveData<'a> { if let Some(ident) = meta_list.path.get_ident() { if ident == REFLECT_ATTRIBUTE_NAME { - output.traits = ReflectTraits::from_nested_metas(&meta_list.nested); + traits = ReflectTraits::from_nested_metas(&meta_list.nested); } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { force_reflect_value = true; - output.traits = ReflectTraits::from_nested_metas(&meta_list.nested); + traits = ReflectTraits::from_nested_metas(&meta_list.nested); } } } - let fields = match &input.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => { - if !force_reflect_value { - output.derive_type = DeriveType::Struct; - } - &fields.named - } - Data::Struct(DataStruct { - fields: Fields::Unnamed(fields), - .. - }) => { - if !force_reflect_value { - output.derive_type = DeriveType::TupleStruct; - } - &fields.unnamed - } - Data::Struct(DataStruct { - fields: Fields::Unit, - .. - }) => { - if !force_reflect_value { - output.derive_type = DeriveType::UnitStruct; + let meta = ReflectMeta::new(&input.ident, &input.generics, traits); + + if force_reflect_value { + return Ok(Self::Value(meta)); + } + + return match &input.data { + Data::Struct(data) => { + let reflect_struct = ReflectStruct { + meta, + fields: Self::collect_struct_fields(&data.fields)?, + }; + + match data.fields { + Fields::Named(..) => Ok(Self::Struct(reflect_struct)), + Fields::Unnamed(..) => Ok(Self::TupleStruct(reflect_struct)), + Fields::Unit => Ok(Self::UnitStruct(reflect_struct)), } - return Ok(output); } - _ => { - return Ok(output); + Data::Enum(data) => { + let reflect_enum = ReflectEnum { + meta, + variants: Self::collect_enum_variants(&data.variants)?, + }; + Ok(Self::Enum(reflect_enum)) } + Data::Union(..) => Err(syn::Error::new( + input.span(), + "reflection not supported for unions", + )), }; + } - let mut errors: Option = None; - output.fields = fields + fn collect_struct_fields(fields: &'a Fields) -> Result>, syn::Error> { + let sifter: utility::ResultSifter> = fields .iter() .enumerate() - .map(|(index, field)| { - let attrs = parse_field_attrs(&field.attrs).unwrap_or_else(|err| { - if let Some(ref mut errors) = errors { - errors.combine(err); - } else { - errors = Some(err); - } - ReflectFieldAttr::default() - }); - - StructField { + .map(|(index, field)| -> Result { + let attrs = parse_field_attrs(&field.attrs)?; + Ok(StructField { index, attrs, data: field, - } + }) }) - .collect::>(); - if let Some(errs) = errors { - return Err(errs); - } + .fold( + utility::ResultSifter::default(), + utility::ResultSifter::fold, + ); - Ok(output) + sifter.finish() } - /// Get an iterator over the active fields - pub fn active_fields(&self) -> impl Iterator> { - self.fields.iter().filter(|field| !field.attrs.ignore) - } + fn collect_enum_variants( + variants: &'a Punctuated, + ) -> Result>, syn::Error> { + let sifter: utility::ResultSifter> = variants + .iter() + .enumerate() + .map(|(index, variant)| -> Result { + let fields = Self::collect_struct_fields(&variant.fields)?; - /// Get an iterator over the ignored fields - pub fn ignored_fields(&self) -> impl Iterator> { - self.fields.iter().filter(|field| field.attrs.ignore) - } + let fields = match variant.fields { + Fields::Named(..) => EnumVariantFields::Named(fields), + Fields::Unnamed(..) => EnumVariantFields::Unnamed(fields), + Fields::Unit => EnumVariantFields::Unit, + }; + Ok(EnumVariant { + fields, + attrs: parse_field_attrs(&variant.attrs)?, + data: variant, + index, + }) + }) + .fold( + utility::ResultSifter::default(), + utility::ResultSifter::fold, + ); - /// Get a collection of all active types - pub fn active_types(&self) -> Vec { - self.active_fields() - .map(|field| field.data.ty.clone()) - .collect::>() + sifter.finish() } +} - /// The [`DeriveType`] of this struct. - pub fn derive_type(&self) -> &DeriveType { - &self.derive_type +impl<'a> ReflectMeta<'a> { + pub fn new(type_name: &'a Ident, generics: &'a Generics, traits: ReflectTraits) -> Self { + Self { + traits, + type_name, + generics, + bevy_reflect_path: utility::get_bevy_reflect_path(), + } } /// The registered reflect traits on this struct. @@ -174,12 +235,6 @@ impl<'a> ReflectDeriveData<'a> { self.generics } - /// The complete set of fields in this struct. - #[allow(dead_code)] - pub fn fields(&self) -> &[StructField<'a>] { - &self.fields - } - /// The cached `bevy_reflect` path. pub fn bevy_reflect_path(&self) -> &Path { &self.bevy_reflect_path @@ -195,3 +250,63 @@ impl<'a> ReflectDeriveData<'a> { ) } } + +impl<'a> ReflectStruct<'a> { + /// Access the metadata associated with this struct definition. + pub fn meta(&self) -> &ReflectMeta<'a> { + &self.meta + } + + /// Get an iterator over the active fields. + pub fn active_fields(&self) -> impl Iterator> { + self.fields.iter().filter(|field| !field.attrs.ignore) + } + + /// Get an iterator over the ignored fields. + pub fn ignored_fields(&self) -> impl Iterator> { + self.fields.iter().filter(|field| field.attrs.ignore) + } + + /// Get a collection of all active types. + pub fn active_types(&self) -> impl Iterator { + self.active_fields().map(|field| &field.data.ty) + } + + /// The complete set of fields in this struct. + #[allow(dead_code)] + pub fn fields(&self) -> &[StructField<'a>] { + &self.fields + } +} + +impl<'a> ReflectEnum<'a> { + /// Access the metadata associated with this enum definition. + pub fn meta(&self) -> &ReflectMeta<'a> { + &self.meta + } + + /// Get an iterator over the active variants. + pub fn active_variants(&self) -> impl Iterator> { + self.variants.iter().filter(|variant| !variant.attrs.ignore) + } + + /// Get an iterator over the ignored variants. + #[allow(dead_code)] + pub fn ignored_variants(&self) -> impl Iterator> { + self.variants.iter().filter(|variant| variant.attrs.ignore) + } + + /// Returns the given ident as a qualified unit variant of this enum. + pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream { + let name = self.meta.type_name; + quote! { + #name::#variant + } + } + + /// The complete set of variants in this enum. + #[allow(dead_code)] + pub fn variants(&self) -> &[EnumVariant<'a>] { + &self.variants + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs new file mode 100644 index 0000000000000..427db8a377e57 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -0,0 +1,84 @@ +use crate::{ + derive_data::{EnumVariantFields, ReflectEnum}, + utility::ident_or_index, +}; +use proc_macro2::Ident; +use quote::{quote, ToTokens}; + +/// Contains all data needed to construct all variants within an enum. +pub(crate) struct EnumVariantConstructors { + /// The names of each variant as a string. + pub variant_names: Vec, + /// The stream of tokens that will construct each variant. + pub variant_constructors: Vec, +} + +/// Gets the constructors for all variants in the given enum. +pub(crate) fn get_variant_constructors( + reflect_enum: &ReflectEnum, + ref_value: &Ident, + can_panic: bool, +) -> EnumVariantConstructors { + let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); + let variant_count = reflect_enum.variants().len(); + let mut variant_names = Vec::with_capacity(variant_count); + let mut variant_constructors = Vec::with_capacity(variant_count); + + for variant in reflect_enum.active_variants() { + let ident = &variant.data.ident; + let name = ident.to_string(); + let variant_constructor = reflect_enum.get_unit(ident); + + let fields = match &variant.fields { + EnumVariantFields::Unit => &[], + EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => { + fields.as_slice() + } + }; + let mut reflect_index: usize = 0; + let constructor_fields = fields.iter().enumerate().map(|(declar_index, field)| { + let field_ident = ident_or_index(field.data.ident.as_ref(), declar_index); + let field_value = if field.attrs.ignore { + quote! { Default::default() } + } else { + let error_repr = field.data.ident.as_ref().map_or_else( + || format!("at index {reflect_index}"), + |name| format!("`{name}`"), + ); + let unwrapper = if can_panic { + let type_err_message = format!( + "the field {error_repr} should be of type `{}`", + field.data.ty.to_token_stream() + ); + quote!(.expect(#type_err_message)) + } else { + quote!(?) + }; + let field_accessor = match &field.data.ident { + Some(ident) => { + let name = ident.to_string(); + quote!(.field(#name)) + } + None => quote!(.field_at(#reflect_index)), + }; + reflect_index += 1; + let missing_field_err_message = format!("the field {error_repr} was not declared"); + let accessor = quote!(#field_accessor .expect(#missing_field_err_message)); + quote! { + #bevy_reflect_path::FromReflect::from_reflect(#ref_value #accessor) + #unwrapper + } + }; + quote! { #field_ident : #field_value } + }); + variant_constructors.push(quote! { + #variant_constructor { #( #constructor_fields ),* } + }); + variant_names.push(name); + } + + EnumVariantConstructors { + variant_names, + variant_constructors, + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index b4c9c345f26eb..73e4cb6451108 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -1,28 +1,28 @@ use crate::container_attributes::REFLECT_DEFAULT; +use crate::derive_data::ReflectEnum; +use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::field_attributes::DefaultBehavior; -use crate::ReflectDeriveData; +use crate::{ReflectMeta, ReflectStruct}; use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; -use syn::{Field, Generics, Ident, Index, Lit, LitInt, LitStr, Member, Path}; +use syn::{Field, Ident, Index, Lit, LitInt, LitStr, Member}; /// Implements `FromReflect` for the given struct -pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { - impl_struct_internal(derive_data, false) +pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { + impl_struct_internal(reflect_struct, false) } /// Implements `FromReflect` for the given tuple struct -pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { - impl_struct_internal(derive_data, true) +pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { + impl_struct_internal(reflect_struct, true) } /// Implements `FromReflect` for the given value type -pub(crate) fn impl_value( - type_name: &Ident, - generics: &Generics, - bevy_reflect_path: &Path, -) -> TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); +pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { + let type_name = meta.type_name(); + let bevy_reflect_path = meta.bevy_reflect_path(); + let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); TokenStream::from(quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { @@ -32,6 +32,35 @@ pub(crate) fn impl_value( }) } +/// Implements `FromReflect` for the given enum type +pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { + let type_name = reflect_enum.meta().type_name(); + let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); + + let ref_value = Ident::new("__param0", Span::call_site()); + let EnumVariantConstructors { + variant_names, + variant_constructors, + } = get_variant_constructors(reflect_enum, &ref_value, false); + + let (impl_generics, ty_generics, where_clause) = + reflect_enum.meta().generics().split_for_impl(); + TokenStream::from(quote! { + impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { + fn from_reflect(#ref_value: &dyn #bevy_reflect_path::Reflect) -> Option { + if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #ref_value.reflect_ref() { + match #ref_value.variant_name() { + #(#variant_names => Some(#variant_constructors),)* + name => panic!("variant with name `{}` does not exist on enum `{}`", name, std::any::type_name::()), + } + } else { + None + } + } + } + }) +} + /// Container for a struct's members (field name or index) and their /// corresponding values. struct MemberValuePair(Vec, Vec); @@ -42,10 +71,10 @@ impl MemberValuePair { } } -fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> TokenStream { - let struct_name = derive_data.type_name(); - let generics = derive_data.generics(); - let bevy_reflect_path = derive_data.bevy_reflect_path(); +fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> TokenStream { + let struct_name = reflect_struct.meta().type_name(); + let generics = reflect_struct.meta().generics(); + let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); let ref_struct = Ident::new("__ref_struct", Span::call_site()); let ref_struct_type = if is_tuple { @@ -54,11 +83,11 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke Ident::new("Struct", Span::call_site()) }; - let field_types = derive_data.active_types(); + let field_types = reflect_struct.active_types(); let MemberValuePair(active_members, active_values) = - get_active_fields(derive_data, &ref_struct, &ref_struct_type, is_tuple); + get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple); - let constructor = if derive_data.traits().contains(REFLECT_DEFAULT) { + let constructor = if reflect_struct.meta().traits().contains(REFLECT_DEFAULT) { quote!( let mut __this = Self::default(); #( @@ -71,7 +100,7 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke ) } else { let MemberValuePair(ignored_members, ignored_values) = - get_ignored_fields(derive_data, is_tuple); + get_ignored_fields(reflect_struct, is_tuple); quote!( Some( @@ -115,9 +144,9 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke /// /// Each value of the `MemberValuePair` is a token stream that generates a /// a default value for the ignored field. -fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> MemberValuePair { +fn get_ignored_fields(reflect_struct: &ReflectStruct, is_tuple: bool) -> MemberValuePair { MemberValuePair::new( - derive_data + reflect_struct .ignored_fields() .map(|field| { let member = get_ident(field.data, field.index, is_tuple); @@ -138,15 +167,15 @@ fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> Member /// Each value of the `MemberValuePair` is a token stream that generates a /// closure of type `fn() -> Option` where `T` is that field's type. fn get_active_fields( - derive_data: &ReflectDeriveData, + reflect_struct: &ReflectStruct, dyn_struct_name: &Ident, struct_type: &Ident, is_tuple: bool, ) -> MemberValuePair { - let bevy_reflect_path = derive_data.bevy_reflect_path(); + let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); MemberValuePair::new( - derive_data + reflect_struct .active_fields() .map(|field| { let member = get_ident(field.data, field.index, is_tuple); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs deleted file mode 100644 index 38680a35adf38..0000000000000 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs +++ /dev/null @@ -1,492 +0,0 @@ -use crate::container_attributes::ReflectTraits; -use crate::ReflectDeriveData; -use proc_macro::TokenStream; -use proc_macro2::Ident; -use quote::quote; -use syn::{Generics, Index, Member, Path}; - -/// Implements `Struct`, `GetTypeRegistration`, and `Reflect` for the given derive data. -pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { - let bevy_reflect_path = derive_data.bevy_reflect_path(); - let struct_name = derive_data.type_name(); - - let field_names = derive_data - .active_fields() - .map(|field| { - field - .data - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| field.index.to_string()) - }) - .collect::>(); - let field_idents = derive_data - .active_fields() - .map(|field| { - field - .data - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) - }) - .collect::>(); - let field_types = derive_data - .active_fields() - .map(|field| field.data.ty.clone()) - .collect::>(); - let field_count = field_idents.len(); - let field_indices = (0..field_count).collect::>(); - - let hash_fn = derive_data.traits().get_hash_impl(bevy_reflect_path); - let partial_eq_fn = derive_data - .traits() - .get_partial_eq_impl(bevy_reflect_path) - .unwrap_or_else(|| { - quote! { - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #bevy_reflect_path::struct_partial_eq(self, value) - } - } - }); - let debug_fn = derive_data.traits().get_debug_impl(); - - let typed_impl = impl_typed( - struct_name, - derive_data.generics(), - quote! { - let fields: [#bevy_reflect_path::NamedField; #field_count] = [ - #(#bevy_reflect_path::NamedField::new::<#field_types, _>(#field_names),)* - ]; - let info = #bevy_reflect_path::StructInfo::new::(&fields); - #bevy_reflect_path::TypeInfo::Struct(info) - }, - bevy_reflect_path, - ); - - let get_type_registration_impl = derive_data.get_type_registration(); - let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); - - TokenStream::from(quote! { - #get_type_registration_impl - - #typed_impl - - impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { - fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { - match name { - #(#field_names => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match name { - #(#field_names => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn name_at(&self, index: usize) -> Option<&str> { - match index { - #(#field_indices => Some(#field_names),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #field_count - } - - fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { - #bevy_reflect_path::FieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { - let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* - dynamic - } - } - - impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { - ::type_info() - } - - #[inline] - fn into_any(self: Box) -> Box { - self - } - - #[inline] - fn as_any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) - } - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - let name = struct_value.name_at(i).unwrap(); - #bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value)); - } - } else { - panic!("Attempted to apply non-struct type to struct type."); - } - } - - 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) - } - - #hash_fn - - #partial_eq_fn - - #debug_fn - } - }) -} - -/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data. -pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { - let bevy_reflect_path = derive_data.bevy_reflect_path(); - let struct_name = derive_data.type_name(); - let get_type_registration_impl = derive_data.get_type_registration(); - - let field_idents = derive_data - .active_fields() - .map(|field| Member::Unnamed(Index::from(field.index))) - .collect::>(); - let field_types = derive_data - .active_fields() - .map(|field| field.data.ty.clone()) - .collect::>(); - let field_count = field_idents.len(); - let field_indices = (0..field_count).collect::>(); - - let hash_fn = derive_data.traits().get_hash_impl(bevy_reflect_path); - let partial_eq_fn = derive_data - .traits() - .get_partial_eq_impl(bevy_reflect_path) - .unwrap_or_else(|| { - quote! { - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #bevy_reflect_path::tuple_struct_partial_eq(self, value) - } - } - }); - let debug_fn = derive_data.traits().get_debug_impl(); - - let typed_impl = impl_typed( - struct_name, - derive_data.generics(), - quote! { - let fields: [#bevy_reflect_path::UnnamedField; #field_count] = [ - #(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_indices),)* - ]; - let info = #bevy_reflect_path::TupleStructInfo::new::(&fields); - #bevy_reflect_path::TypeInfo::TupleStruct(info) - }, - bevy_reflect_path, - ); - - let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - #typed_impl - - impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause { - fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #field_count - } - - fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter { - #bevy_reflect_path::TupleStructFieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { - let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(self.#field_idents.clone_value());)* - dynamic - } - } - - impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { - ::type_info() - } - - #[inline] - fn into_any(self: Box) -> Box { - self - } - - #[inline] - fn as_any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self)) - } - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - #bevy_reflect_path::TupleStruct::field_mut(self, i).map(|v| v.apply(value)); - } - } else { - panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); - } - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::TupleStruct(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::TupleStruct(self) - } - - #hash_fn - - #partial_eq_fn - - #debug_fn - } - }) -} - -/// Implements `GetTypeRegistration` and `Reflect` for the given type data. -pub(crate) fn impl_value( - type_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_traits: &ReflectTraits, -) -> TokenStream { - let hash_fn = reflect_traits.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = reflect_traits.get_partial_eq_impl(bevy_reflect_path); - let debug_fn = reflect_traits.get_debug_impl(); - - let typed_impl = impl_typed( - type_name, - generics, - quote! { - let info = #bevy_reflect_path::ValueInfo::new::(); - #bevy_reflect_path::TypeInfo::Value(info) - }, - bevy_reflect_path, - ); - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - #typed_impl - - impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { - ::type_info() - } - - #[inline] - fn into_any(self: Box) -> Box { - self - } - - #[inline] - fn as_any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - let value = value.as_any(); - if let Some(value) = value.downcast_ref::() { - *self = value.clone(); - } else { - panic!("Value is not {}.", std::any::type_name::()); - } - } - - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::Value(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::Value(self) - } - - #hash_fn - - #partial_eq_fn - - #debug_fn - } - }) -} - -fn impl_typed( - type_name: &Ident, - generics: &Generics, - generator: proc_macro2::TokenStream, - bevy_reflect_path: &Path, -) -> proc_macro2::TokenStream { - let is_generic = !generics.params.is_empty(); - - let static_generator = if is_generic { - quote! { - static CELL: #bevy_reflect_path::utility::GenericTypeInfoCell = #bevy_reflect_path::utility::GenericTypeInfoCell::new(); - CELL.get_or_insert::(|| { - #generator - }) - } - } else { - quote! { - static CELL: #bevy_reflect_path::utility::NonGenericTypeInfoCell = #bevy_reflect_path::utility::NonGenericTypeInfoCell::new(); - CELL.get_or_set(|| { - #generator - }) - } - }; - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - quote! { - impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_clause { - fn type_info() -> &'static #bevy_reflect_path::TypeInfo { - #static_generator - } - } - } -} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs new file mode 100644 index 0000000000000..6140f853eee69 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -0,0 +1,363 @@ +use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField}; +use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; +use crate::impls::impl_typed; +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::quote; + +pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { + let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); + let enum_name = reflect_enum.meta().type_name(); + + let ref_name = Ident::new("__name_param", Span::call_site()); + let ref_index = Ident::new("__index_param", Span::call_site()); + let ref_value = Ident::new("__value_param", Span::call_site()); + + let EnumImpls { + variant_info, + enum_field, + enum_field_at, + enum_index_of, + enum_name_at, + enum_field_len, + enum_variant_name, + enum_variant_type, + } = generate_impls(reflect_enum, &ref_index, &ref_name); + + let EnumVariantConstructors { + variant_names, + variant_constructors, + } = get_variant_constructors(reflect_enum, &ref_value, true); + + let hash_fn = reflect_enum + .meta() + .traits() + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_hash(&self) -> Option { + #bevy_reflect_path::enum_hash(self) + } + } + }); + let debug_fn = reflect_enum.meta().traits().get_debug_impl(); + let partial_eq_fn = reflect_enum + .meta() + .traits() + .get_partial_eq_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #bevy_reflect_path::enum_partial_eq(self, value) + } + } + }); + + let typed_impl = impl_typed( + enum_name, + reflect_enum.meta().generics(), + quote! { + let variants = [#(#variant_info),*]; + let info = #bevy_reflect_path::EnumInfo::new::(&variants); + #bevy_reflect_path::TypeInfo::Enum(info) + }, + bevy_reflect_path, + ); + + let get_type_registration_impl = reflect_enum.meta().get_type_registration(); + let (impl_generics, ty_generics, where_clause) = + reflect_enum.meta().generics().split_for_impl(); + + TokenStream::from(quote! { + #get_type_registration_impl + + #typed_impl + + impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_clause { + fn field(&self, #ref_name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field,)* + _ => None, + } + } + + fn field_at(&self, #ref_index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field_at,)* + _ => None, + } + } + + fn field_mut(&mut self, #ref_name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field,)* + _ => None, + } + } + + fn field_at_mut(&mut self, #ref_index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field_at,)* + _ => None, + } + } + + fn index_of(&self, #ref_name: &str) -> Option { + match self { + #(#enum_index_of,)* + _ => None, + } + } + + fn name_at(&self, #ref_index: usize) -> Option<&str> { + match self { + #(#enum_name_at,)* + _ => None, + } + } + + fn iter_fields(&self) -> #bevy_reflect_path::VariantFieldIter { + #bevy_reflect_path::VariantFieldIter::new(self) + } + + #[inline] + fn field_len(&self) -> usize { + match self { + #(#enum_field_len,)* + _ => 0, + } + } + + #[inline] + fn variant_name(&self) -> &str { + match self { + #(#enum_variant_name,)* + _ => unreachable!(), + } + } + + #[inline] + fn variant_type(&self) -> #bevy_reflect_path::VariantType { + match self { + #(#enum_variant_type,)* + _ => unreachable!(), + } + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicEnum { + #bevy_reflect_path::DynamicEnum::from_ref::(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 get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::Enum::clone_dynamic(self)) + } + + #[inline] + fn set(&mut self, #ref_value: Box) -> Result<(), Box> { + *self = #ref_value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, #ref_value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #ref_value.reflect_ref() { + if #bevy_reflect_path::Enum::variant_name(self) == #ref_value.variant_name() { + // Same variant -> just update fields + match #ref_value.variant_type() { + #bevy_reflect_path::VariantType::Struct => { + for field in #ref_value.iter_fields() { + let name = field.name().unwrap(); + #bevy_reflect_path::Enum::field_mut(self, name).map(|v| v.apply(field.value())); + } + } + #bevy_reflect_path::VariantType::Tuple => { + for (index, field) in #ref_value.iter_fields().enumerate() { + #bevy_reflect_path::Enum::field_at_mut(self, index).map(|v| v.apply(field.value())); + } + } + _ => {} + } + } else { + // New variant -> perform a switch + match #ref_value.variant_name() { + #(#variant_names => { + *self = #variant_constructors + })* + name => panic!("variant with name `{}` does not exist on enum `{}`", name, std::any::type_name::()), + } + } + } else { + panic!("`{}` is not an enum", #ref_value.type_name()); + } + } + + 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) + } + + #hash_fn + + #partial_eq_fn + + #debug_fn + } + }) +} + +struct EnumImpls { + variant_info: Vec, + enum_field: Vec, + enum_field_at: Vec, + enum_index_of: Vec, + enum_name_at: Vec, + enum_field_len: Vec, + enum_variant_name: Vec, + enum_variant_type: Vec, +} + +fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Ident) -> EnumImpls { + let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); + + let mut variant_info = Vec::new(); + let mut enum_field = Vec::new(); + let mut enum_field_at = Vec::new(); + let mut enum_index_of = Vec::new(); + let mut enum_name_at = Vec::new(); + let mut enum_field_len = Vec::new(); + let mut enum_variant_name = Vec::new(); + let mut enum_variant_type = Vec::new(); + + for variant in reflect_enum.active_variants() { + let ident = &variant.data.ident; + let name = ident.to_string(); + let unit = reflect_enum.get_unit(ident); + + fn for_fields( + fields: &[StructField], + mut generate_for_field: impl FnMut(usize, usize, &StructField) -> proc_macro2::TokenStream, + ) -> (usize, Vec) { + let mut constructor_argument = Vec::new(); + let mut reflect_idx = 0; + for field in fields.iter() { + if field.attrs.ignore { + // Ignored field + continue; + } + constructor_argument.push(generate_for_field(reflect_idx, field.index, field)); + reflect_idx += 1; + } + (reflect_idx, constructor_argument) + } + let mut add_fields_branch = |variant, info_type, arguments, field_len| { + let variant = Ident::new(variant, Span::call_site()); + let info_type = Ident::new(info_type, Span::call_site()); + variant_info.push(quote! { + #bevy_reflect_path::VariantInfo::#variant( + #bevy_reflect_path::#info_type::new(#arguments) + ) + }); + enum_field_len.push(quote! { + #unit{..} => #field_len + }); + enum_variant_name.push(quote! { + #unit{..} => #name + }); + enum_variant_type.push(quote! { + #unit{..} => #bevy_reflect_path::VariantType::#variant + }); + }; + match &variant.fields { + EnumVariantFields::Unit => { + add_fields_branch("Unit", "UnitVariantInfo", quote!(#name), 0usize); + } + EnumVariantFields::Unnamed(fields) => { + let (field_len, argument) = for_fields(fields, |reflect_idx, declar, field| { + let declar_field = syn::Index::from(declar); + enum_field_at.push(quote! { + #unit { #declar_field : value, .. } if #ref_index == #reflect_idx => Some(value) + }); + let field_ty = &field.data.ty; + quote! { #bevy_reflect_path::UnnamedField::new::<#field_ty>(#reflect_idx) } + }); + let arguments = quote!(#name, &[ #(#argument),* ]); + add_fields_branch("Tuple", "TupleVariantInfo", arguments, field_len); + } + EnumVariantFields::Named(fields) => { + let (field_len, argument) = for_fields(fields, |reflect_idx, _, field| { + let field_ident = field.data.ident.as_ref().unwrap(); + let field_name = field_ident.to_string(); + enum_field.push(quote! { + #unit{ #field_ident, .. } if #ref_name == #field_name => Some(#field_ident) + }); + enum_field_at.push(quote! { + #unit{ #field_ident, .. } if #ref_index == #reflect_idx => Some(#field_ident) + }); + enum_index_of.push(quote! { + #unit{ .. } if #ref_name == #field_name => Some(#reflect_idx) + }); + enum_name_at.push(quote! { + #unit{ .. } if #ref_index == #reflect_idx => Some(#field_name) + }); + + let field_ty = &field.data.ty; + quote! { #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) } + }); + let arguments = quote!(#name, &[ #(#argument),* ]); + add_fields_branch("Struct", "StructVariantInfo", arguments, field_len); + } + }; + } + + EnumImpls { + variant_info, + enum_field, + enum_field_at, + enum_index_of, + enum_name_at, + enum_field_len, + enum_variant_name, + enum_variant_type, + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs new file mode 100644 index 0000000000000..19523fbf806ba --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs @@ -0,0 +1,11 @@ +mod enums; +mod structs; +mod tuple_structs; +mod typed; +mod values; + +pub(crate) use enums::impl_enum; +pub(crate) use structs::impl_struct; +pub(crate) use tuple_structs::impl_tuple_struct; +pub(crate) use typed::impl_typed; +pub(crate) use values::impl_value; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs new file mode 100644 index 0000000000000..4dca3a4feea16 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -0,0 +1,201 @@ +use crate::impls::impl_typed; +use crate::ReflectStruct; +use proc_macro::TokenStream; +use quote::quote; +use syn::{Index, Member}; + +/// Implements `Struct`, `GetTypeRegistration`, and `Reflect` for the given derive data. +pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { + let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); + let struct_name = reflect_struct.meta().type_name(); + + let field_names = reflect_struct + .active_fields() + .map(|field| { + field + .data + .ident + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| field.index.to_string()) + }) + .collect::>(); + let field_idents = reflect_struct + .active_fields() + .map(|field| { + field + .data + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) + }) + .collect::>(); + let field_types = reflect_struct.active_types(); + let field_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = reflect_struct + .meta() + .traits() + .get_hash_impl(bevy_reflect_path); + let debug_fn = reflect_struct.meta().traits().get_debug_impl(); + let partial_eq_fn = reflect_struct.meta() + .traits() + .get_partial_eq_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #bevy_reflect_path::struct_partial_eq(self, value) + } + } + }); + + let typed_impl = impl_typed( + struct_name, + reflect_struct.meta().generics(), + quote! { + let fields = [ + #(#bevy_reflect_path::NamedField::new::<#field_types, _>(#field_names),)* + ]; + let info = #bevy_reflect_path::StructInfo::new::(&fields); + #bevy_reflect_path::TypeInfo::Struct(info) + }, + bevy_reflect_path, + ); + + let get_type_registration_impl = reflect_struct.meta().get_type_registration(); + let (impl_generics, ty_generics, where_clause) = + reflect_struct.meta().generics().split_for_impl(); + + TokenStream::from(quote! { + #get_type_registration_impl + + #typed_impl + + impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { + fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + match name { + #(#field_names => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match name { + #(#field_names => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn name_at(&self, index: usize) -> Option<&str> { + match index { + #(#field_indices => Some(#field_names),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #field_count + } + + fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { + #bevy_reflect_path::FieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { + let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* + dynamic + } + } + + impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() { + for (i, value) in struct_value.iter_fields().enumerate() { + let name = struct_value.name_at(i).unwrap(); + #bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value)); + } + } else { + panic!("Attempted to apply non-struct type to struct type."); + } + } + + 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) + } + + #hash_fn + + #partial_eq_fn + + #debug_fn + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs new file mode 100644 index 0000000000000..0ad33ba4bb8ce --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -0,0 +1,162 @@ +use crate::impls::impl_typed; +use crate::ReflectStruct; +use proc_macro::TokenStream; +use quote::quote; +use syn::{Index, Member}; + +/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data. +pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { + let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); + let struct_name = reflect_struct.meta().type_name(); + let get_type_registration_impl = reflect_struct.meta().get_type_registration(); + + let field_idents = reflect_struct + .active_fields() + .map(|field| Member::Unnamed(Index::from(field.index))) + .collect::>(); + let field_types = reflect_struct.active_types(); + let field_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = reflect_struct + .meta() + .traits() + .get_hash_impl(bevy_reflect_path); + let debug_fn = reflect_struct.meta().traits().get_debug_impl(); + let partial_eq_fn = reflect_struct + .meta() + .traits() + .get_partial_eq_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #bevy_reflect_path::tuple_struct_partial_eq(self, value) + } + } + }); + + let typed_impl = impl_typed( + struct_name, + reflect_struct.meta().generics(), + quote! { + let fields = [ + #(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_idents),)* + ]; + let info = #bevy_reflect_path::TupleStructInfo::new::(&fields); + #bevy_reflect_path::TypeInfo::TupleStruct(info) + }, + bevy_reflect_path, + ); + + let (impl_generics, ty_generics, where_clause) = + reflect_struct.meta().generics().split_for_impl(); + + TokenStream::from(quote! { + #get_type_registration_impl + + #typed_impl + + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause { + fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #field_count + } + + fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter { + #bevy_reflect_path::TupleStructFieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { + let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(self.#field_idents.clone_value());)* + dynamic + } + } + + impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self)) + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { + for (i, value) in struct_value.iter_fields().enumerate() { + #bevy_reflect_path::TupleStruct::field_mut(self, i).map(|v| v.apply(value)); + } + } else { + panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::TupleStruct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::TupleStruct(self) + } + + #hash_fn + + #partial_eq_fn + + #debug_fn + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs new file mode 100644 index 0000000000000..76d36f5869bb1 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -0,0 +1,38 @@ +use proc_macro2::Ident; +use quote::quote; +use syn::{Generics, Path}; + +pub(crate) fn impl_typed( + type_name: &Ident, + generics: &Generics, + generator: proc_macro2::TokenStream, + bevy_reflect_path: &Path, +) -> proc_macro2::TokenStream { + let is_generic = !generics.params.is_empty(); + + let static_generator = if is_generic { + quote! { + static CELL: #bevy_reflect_path::utility::GenericTypeInfoCell = #bevy_reflect_path::utility::GenericTypeInfoCell::new(); + CELL.get_or_insert::(|| { + #generator + }) + } + } else { + quote! { + static CELL: #bevy_reflect_path::utility::NonGenericTypeInfoCell = #bevy_reflect_path::utility::NonGenericTypeInfoCell::new(); + CELL.get_or_set(|| { + #generator + }) + } + }; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + quote! { + impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_clause { + fn type_info() -> &'static #bevy_reflect_path::TypeInfo { + #static_generator + } + } + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs new file mode 100644 index 0000000000000..be01b2214fd11 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -0,0 +1,105 @@ +use crate::impls::impl_typed; +use crate::ReflectMeta; +use proc_macro::TokenStream; +use quote::quote; + +/// Implements `GetTypeRegistration` and `Reflect` for the given type data. +pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { + let bevy_reflect_path = meta.bevy_reflect_path(); + let type_name = meta.type_name(); + + let hash_fn = meta.traits().get_hash_impl(bevy_reflect_path); + let partial_eq_fn = meta.traits().get_partial_eq_impl(bevy_reflect_path); + let debug_fn = meta.traits().get_debug_impl(); + + let typed_impl = impl_typed( + type_name, + meta.generics(), + quote! { + let info = #bevy_reflect_path::ValueInfo::new::(); + #bevy_reflect_path::TypeInfo::Value(info) + }, + bevy_reflect_path, + ); + + let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); + let get_type_registration_impl = meta.get_type_registration(); + + TokenStream::from(quote! { + #get_type_registration_impl + + #typed_impl + + impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + let value = value.as_any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + panic!("Value is not {}.", std::any::type_name::()); + } + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Value(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Value(self) + } + + #hash_fn + + #partial_eq_fn + + #debug_fn + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 7cf9fe560b004..d7a7515683a11 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -16,6 +16,7 @@ extern crate proc_macro; mod container_attributes; mod derive_data; +mod enum_utility; mod field_attributes; mod from_reflect; mod impls; @@ -25,11 +26,11 @@ mod trait_reflection; mod type_uuid; mod utility; -use crate::derive_data::ReflectDeriveData; -use derive_data::DeriveType; +use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct}; use proc_macro::TokenStream; use quote::quote; use reflect_value::ReflectValueDef; +use syn::spanned::Spanned; use syn::{parse_macro_input, DeriveInput}; pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; @@ -39,21 +40,18 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let derive_data = match ReflectDeriveData::from_input(&ast) { + let derive_data = match ReflectDerive::from_input(&ast) { Ok(data) => data, Err(err) => return err.into_compile_error().into(), }; - match derive_data.derive_type() { - DeriveType::Struct | DeriveType::UnitStruct => impls::impl_struct(&derive_data), - DeriveType::TupleStruct => impls::impl_tuple_struct(&derive_data), - DeriveType::Value => impls::impl_value( - derive_data.type_name(), - derive_data.generics(), - derive_data.get_type_registration(), - derive_data.bevy_reflect_path(), - derive_data.traits(), - ), + match derive_data { + ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => { + impls::impl_struct(&struct_data) + } + ReflectDerive::TupleStruct(struct_data) => impls::impl_tuple_struct(&struct_data), + ReflectDerive::Enum(meta) => impls::impl_enum(&meta), + ReflectDerive::Value(meta) => impls::impl_value(&meta), } } @@ -68,19 +66,18 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { pub fn derive_from_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let derive_data = match ReflectDeriveData::from_input(&ast) { + let derive_data = match ReflectDerive::from_input(&ast) { Ok(data) => data, Err(err) => return err.into_compile_error().into(), }; - match derive_data.derive_type() { - DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(&derive_data), - DeriveType::TupleStruct => from_reflect::impl_tuple_struct(&derive_data), - DeriveType::Value => from_reflect::impl_value( - derive_data.type_name(), - &ast.generics, - derive_data.bevy_reflect_path(), - ), + match derive_data { + ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => { + from_reflect::impl_struct(&struct_data) + } + ReflectDerive::TupleStruct(struct_data) => from_reflect::impl_tuple_struct(&struct_data), + ReflectDerive::Enum(meta) => from_reflect::impl_enum(&meta), + ReflectDerive::Value(meta) => from_reflect::impl_value(&meta), } } @@ -97,25 +94,12 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - - let bevy_reflect_path = utility::get_bevy_reflect_path(); - let ty = &reflect_value_def.type_name; - let reflect_traits = reflect_value_def.traits.unwrap_or_default(); - let registration_data = &reflect_traits.idents(); - let get_type_registration_impl = registration::impl_get_type_registration( - ty, - &bevy_reflect_path, - registration_data, - &reflect_value_def.generics, - ); - impls::impl_value( - ty, - &reflect_value_def.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_traits, - ) + let def = parse_macro_input!(input as ReflectValueDef); + impls::impl_value(&ReflectMeta::new( + &def.type_name, + &def.generics, + def.traits.unwrap_or_default(), + )) } /// A replacement for `#[derive(Reflect)]` to be used with foreign types which @@ -149,26 +133,47 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream { #[proc_macro] pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let derive_data = match ReflectDeriveData::from_input(&ast) { + let derive_data = match ReflectDerive::from_input(&ast) { Ok(data) => data, Err(err) => return err.into_compile_error().into(), }; - let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&derive_data).into(); - let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(&derive_data).into(); - - TokenStream::from(quote! { - #impl_struct - - #impl_from_struct - }) + match derive_data { + ReflectDerive::Struct(struct_data) => { + let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&struct_data).into(); + let impl_from_struct: proc_macro2::TokenStream = + from_reflect::impl_struct(&struct_data).into(); + + TokenStream::from(quote! { + #impl_struct + + #impl_from_struct + }) + } + ReflectDerive::TupleStruct(..) => syn::Error::new( + ast.span(), + "impl_reflect_struct does not support tuple structs", + ) + .into_compile_error() + .into(), + ReflectDerive::UnitStruct(..) => syn::Error::new( + ast.span(), + "impl_reflect_struct does not support unit structs", + ) + .into_compile_error() + .into(), + _ => syn::Error::new(ast.span(), "impl_reflect_struct only supports structs") + .into_compile_error() + .into(), + } } #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - - let bevy_reflect_path = utility::get_bevy_reflect_path(); - let ty = &reflect_value_def.type_name; - from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path) + let def = parse_macro_input!(input as ReflectValueDef); + from_reflect::impl_value(&ReflectMeta::new( + &def.type_name, + &def.generics, + def.traits.unwrap_or_default(), + )) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 34fbdf186cc94..f8894689a3858 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -2,7 +2,7 @@ use bevy_macro_utils::BevyManifest; use proc_macro2::{Ident, Span}; -use syn::Path; +use syn::{Member, Path}; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { @@ -21,3 +21,78 @@ pub(crate) fn get_reflect_ident(name: &str) -> Ident { let reflected = format!("Reflect{}", name); Ident::new(&reflected, Span::call_site()) } + +/// Helper struct used to process an iterator of `Result, syn::Error>`, +/// combining errors into one along the way. +pub(crate) struct ResultSifter { + items: Vec, + errors: Option, +} + +/// Returns a `Member` made of `ident` or `index` if `ident` is None. +/// +/// Rust struct syntax allows for `Struct { foo: "string" }` with explicitly +/// named fields. It allows the `Struct { 0: "string" }` syntax when the struct +/// is declared as a tuple struct. +/// +/// ``` +/// # fn main() { +/// struct Foo { field: &'static str } +/// struct Bar(&'static str); +/// let Foo { field } = Foo { field: "hi" }; +/// let Bar { 0: field } = Bar { 0: "hello" }; +/// let Bar(field) = Bar("hello"); // more common syntax +/// # } +/// ``` +/// +/// This function helps field access in context where you are declaring either +/// a tuple struct or a struct with named fields. If you don't have a field name, +/// it means you need to access the struct through an index. +pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { + // TODO(Quality) when #4761 is merged, code that does this should be replaced + // by a call to `ident_or_index`. + ident.map_or_else( + || Member::Unnamed(index.into()), + |ident| Member::Named(ident.clone()), + ) +} + +impl Default for ResultSifter { + fn default() -> Self { + Self { + items: Vec::new(), + errors: None, + } + } +} + +impl ResultSifter { + /// Sift the given result, combining errors if necessary. + pub fn sift(&mut self, result: Result) { + match result { + Ok(data) => self.items.push(data), + Err(err) => { + if let Some(ref mut errors) = self.errors { + errors.combine(err); + } else { + self.errors = Some(err); + } + } + } + } + + /// Associated method that provides a convenient implementation for [`Iterator::fold`]. + pub fn fold(mut sifter: Self, result: Result) -> Self { + sifter.sift(result); + sifter + } + + /// Complete the sifting process and return the final result. + pub fn finish(self) -> Result, syn::Error> { + if let Some(errors) = self.errors { + Err(errors) + } else { + Ok(self.items) + } + } +} diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs new file mode 100644 index 0000000000000..d0f097e977b1e --- /dev/null +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -0,0 +1,375 @@ +use crate::utility::NonGenericTypeInfoCell; +use crate::{ + enum_debug, enum_hash, enum_partial_eq, DynamicInfo, DynamicStruct, DynamicTuple, Enum, + Reflect, ReflectMut, ReflectRef, Struct, Tuple, TypeInfo, Typed, VariantFieldIter, VariantType, +}; +use std::any::Any; +use std::fmt::Formatter; + +/// A dynamic representation of an enum variant. +pub enum DynamicVariant { + 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 Default for DynamicVariant { + fn default() -> Self { + DynamicVariant::Unit + } +} + +impl From for DynamicVariant { + fn from(dyn_tuple: DynamicTuple) -> Self { + Self::Tuple(dyn_tuple) + } +} + +impl From for DynamicVariant { + fn from(dyn_struct: DynamicStruct) -> Self { + Self::Struct(dyn_struct) + } +} + +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}; +/// +/// // The original enum value +/// let mut value: Option = Some(123); +/// +/// // Create a DynamicEnum to represent the new value +/// let mut dyn_enum = DynamicEnum::new( +/// Reflect::type_name(&value), +/// "None", +/// DynamicVariant::Unit +/// ); +/// +/// // Apply the DynamicEnum as a patch to the original value +/// value.apply(&dyn_enum); +/// +/// // Tada! +/// assert_eq!(None, value); +/// ``` +#[derive(Default)] +pub struct DynamicEnum { + name: String, + variant_name: String, + variant: DynamicVariant, +} + +impl DynamicEnum { + /// Create a new [`DynamicEnum`] to represent an enum at runtime. + /// + /// # Arguments + /// + /// * `name`: The type name of the enum + /// * `variant_name`: The name of the variant to set + /// * `variant`: The variant data + /// + pub fn new, V: Into>( + name: I, + variant_name: I, + variant: V, + ) -> Self { + Self { + name: name.into(), + variant_name: variant_name.into(), + variant: variant.into(), + } + } + + /// Returns the type name of the enum. + pub fn name(&self) -> &str { + &self.name + } + + /// Sets the type name of the enum. + pub fn set_name(&mut self, name: String) { + self.name = name; + } + + /// 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(); + } + + /// 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 { + match value.variant_type() { + VariantType::Unit => DynamicEnum::new( + value.type_name(), + 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( + value.type_name(), + 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( + value.type_name(), + value.variant_name(), + DynamicVariant::Struct(data), + ) + } + } + } +} + +impl Enum for DynamicEnum { + fn field(&self, name: &str) -> Option<&dyn Reflect> { + if let DynamicVariant::Struct(data) = &self.variant { + data.field(name) + } else { + None + } + } + + fn field_at(&self, index: usize) -> Option<&dyn Reflect> { + if let DynamicVariant::Tuple(data) = &self.variant { + data.field(index) + } else { + None + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> { + 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 Reflect> { + 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_type(&self) -> VariantType { + match &self.variant { + DynamicVariant::Unit => VariantType::Unit, + DynamicVariant::Tuple(..) => VariantType::Tuple, + DynamicVariant::Struct(..) => VariantType::Struct, + } + } + + fn clone_dynamic(&self) -> DynamicEnum { + Self { + name: self.name.clone(), + variant_name: self.variant_name.clone(), + variant: self.variant.clone(), + } + } +} + +impl Reflect for DynamicEnum { + #[inline] + fn type_name(&self) -> &str { + &self.name + } + + #[inline] + fn get_type_info(&self) -> &'static TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + #[inline] + fn apply(&mut self, value: &dyn Reflect) { + if let ReflectRef::Enum(value) = value.reflect_ref() { + 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.apply(field.value()); + } + } + } + VariantType::Tuple => { + for (index, field) in value.iter_fields().enumerate() { + if let Some(v) = Enum::field_at_mut(self, index) { + v.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); + } + } else { + panic!("`{}` is not an enum", value.type_name()); + } + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn reflect_ref(&self) -> ReflectRef { + ReflectRef::Enum(self) + } + + #[inline] + fn reflect_mut(&mut self) -> ReflectMut { + ReflectMut::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 Reflect) -> Option { + enum_partial_eq(self, value) + } + + #[inline] + fn debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DynamicEnum(")?; + enum_debug(self, f)?; + write!(f, ")") + } +} + +impl Typed for DynamicEnum { + fn type_info() -> &'static TypeInfo { + static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); + CELL.get_or_set(|| TypeInfo::Dynamic(DynamicInfo::new::())) + } +} diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs new file mode 100644 index 0000000000000..9ee110d8f0a05 --- /dev/null +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -0,0 +1,280 @@ +use crate::{DynamicEnum, Reflect, VariantInfo, VariantType}; +use bevy_utils::HashMap; +use std::any::{Any, TypeId}; +use std::borrow::Cow; +use std::slice::Iter; + +/// A trait representing a [reflected] enum. +/// +/// This allows enums to be processed and modified dynamically at runtime without +/// necessarily knowing the actual type. +/// Enums are much more complex than their struct counterparts. +/// As a result, users will need to be mindful of conventions, considerations, +/// and complications when working with this trait. +/// +/// # Variants +/// +/// An enum is a set of choices called _variants_. +/// An instance of an enum can only exist as one of these choices at any given time. +/// Consider Rust's [`Option`]. It's an enum with two variants: [`None`] and [`Some`]. +/// If you're `None`, you can't be `Some` and vice versa. +/// +/// > ⚠️ __This is very important:__ +/// > The [`Enum`] trait represents an enum _as one of its variants_. +/// > It does not represent the entire enum since that's not true to how enums work. +/// +/// Variants come in a few [flavors](VariantType): +/// +/// | Variant Type | Syntax | +/// | ------------ | ------------------------------ | +/// | Unit | `MyEnum::Foo` | +/// | Tuple | `MyEnum::Foo( i32, i32 )` | +/// | Struct | `MyEnum::Foo{ value: String }` | +/// +/// As you can see, a unit variant contains no fields, while tuple and struct variants +/// can contain one or more fields. +/// The fields in a tuple variant is defined by their _order_ within the variant. +/// Index `0` represents the first field in the variant and so on. +/// Fields in struct variants (excluding tuple structs), on the other hand, are +/// represented by a _name_. +/// +/// # Implementation +/// +/// > 💡 This trait can be automatically implemented using the [`Reflect`] derive macro +/// > on an enum definition. +/// +/// Despite the fact that enums can represent multiple states, traits only exist in one state +/// and must be applied to the entire enum rather than a particular variant. +/// Because of this limitation, the [`Enum`] trait must not only _represent_ any of the +/// three variant types, but also define the _methods_ for all three as well. +/// +/// What does this mean? It means that even though a unit variant contains no fields, a +/// representation of that variant using the [`Enum`] trait will still contain methods for +/// accessing fields! +/// Again, this is to account for _all three_ variant types. +/// +/// We recommend using the built-in [`Reflect`] derive macro to automatically handle all the +/// implementation details for you. +/// However, if you _must_ implement this trait manually, there are a few things to keep in mind... +/// +/// ## Field Order +/// +/// While tuple variants identify their fields by the order in which they are defined, struct +/// variants identify fields by their name. +/// However, both should allow access to fields by their defined order. +/// +/// The reason all fields, regardless of variant type, need to be accessible by their order is +/// due to field iteration. +/// We need a way to iterate through each field in a variant, and the easiest way of achieving +/// that is through the use of field order. +/// +/// The derive macro adds proper struct variant handling for [`Enum::index_of`], [`Enum::name_at`] +/// and [`Enum::field_at[_mut]`](Enum::field_at) methods. +/// The first two methods are __required__ for all struct variant types. +/// By convention, implementors should also handle the last method as well, but this is not +/// a strict requirement. +/// +/// ## Field Names +/// +/// Implementors may choose to handle [`Enum::index_of`], [`Enum::name_at`], and +/// [`Enum::field[_mut]`](Enum::field) for tuple variants by considering stringified `usize`s to be +/// valid names (such as `"3"`). +/// This isn't wrong to do, but the convention set by the derive macro is that it isn't supported. +/// It's preferred that these strings be converted to their proper `usize` representations and +/// the [`Enum::field_at[_mut]`](Enum::field_at) methods be used instead. +/// +/// [reflected]: crate +/// [`None`]: core::option::Option::None +/// [`Some`]: core::option::Option::Some +/// [`Reflect`]: bevy_reflect_derive::Reflect +pub trait Enum: Reflect { + /// Returns a reference to the value of the field (in the current variant) with the given name. + /// + /// For non-[`VariantType::Struct`] variants, this should return `None`. + fn field(&self, name: &str) -> Option<&dyn Reflect>; + /// Returns a reference to the value of the field (in the current variant) at the given index. + fn field_at(&self, index: usize) -> Option<&dyn Reflect>; + /// Returns a mutable reference to the value of the field (in the current variant) with the given name. + /// + /// For non-[`VariantType::Struct`] variants, this should return `None`. + fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect>; + /// Returns a mutable reference to the value of the field (in the current variant) at the given index. + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>; + /// Returns the index of the field (in the current variant) with the given name. + /// + /// For non-[`VariantType::Struct`] variants, this should return `None`. + fn index_of(&self, name: &str) -> Option; + /// Returns the name of the field (in the current variant) with the given index. + /// + /// For non-[`VariantType::Struct`] variants, this should return `None`. + fn name_at(&self, index: usize) -> Option<&str>; + /// Returns an iterator over the values of the current variant's fields. + fn iter_fields(&self) -> VariantFieldIter; + /// Returns the number of fields in the current variant. + fn field_len(&self) -> usize; + /// The name of the current variant. + fn variant_name(&self) -> &str; + /// The type of the current variant. + fn variant_type(&self) -> VariantType; + // Clones the enum into a [`DynamicEnum`]. + fn clone_dynamic(&self) -> DynamicEnum; + /// Returns true if the current variant's type matches the given one. + fn is_variant(&self, variant_type: VariantType) -> bool { + self.variant_type() == variant_type + } + /// Returns the full path to the current variant. + fn variant_path(&self) -> String { + format!("{}::{}", self.type_name(), self.variant_name()) + } +} + +/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo). +#[derive(Clone, Debug)] +pub struct EnumInfo { + type_name: &'static str, + type_id: TypeId, + variants: Box<[VariantInfo]>, + variant_indices: HashMap, usize>, +} + +impl EnumInfo { + /// Create a new [`EnumInfo`]. + /// + /// # Arguments + /// + /// * `variants`: The variants of this enum in the order they are defined + /// + pub fn new(variants: &[VariantInfo]) -> Self { + let variant_indices = variants + .iter() + .enumerate() + .map(|(index, variant)| { + let name = variant.name().clone(); + (name, index) + }) + .collect::>(); + + Self { + type_name: std::any::type_name::(), + type_id: TypeId::of::(), + variants: variants.to_vec().into_boxed_slice(), + variant_indices, + } + } + + /// Get a variant with the given name. + pub fn variant(&self, name: &str) -> Option<&VariantInfo> { + self.variant_indices + .get(name) + .map(|index| &self.variants[*index]) + } + + /// Get a variant at the given index. + pub fn variant_at(&self, index: usize) -> Option<&VariantInfo> { + self.variants.get(index) + } + + /// Get the index of the variant with the given name. + pub fn index_of(&self, name: &str) -> Option { + self.variant_indices.get(name).copied() + } + + /// Returns the full path to the given variant. + /// + /// This does _not_ check if the given variant exists. + pub fn variant_path(&self, name: &str) -> String { + format!("{}::{}", self.type_name(), name) + } + + /// Checks if a variant with the given name exists within this enum. + pub fn contains_variant(&self, name: &str) -> bool { + self.variant_indices.contains_key(name) + } + + /// Iterate over the variants of this enum. + pub fn iter(&self) -> Iter<'_, VariantInfo> { + self.variants.iter() + } + + /// The number of variants in this enum. + pub fn variant_len(&self) -> usize { + self.variants.len() + } + + /// The [type name] of the enum. + /// + /// [type name]: std::any::type_name + pub fn type_name(&self) -> &'static str { + self.type_name + } + + /// The [`TypeId`] of the enum. + pub fn type_id(&self) -> TypeId { + self.type_id + } + + /// Check if the given type matches the enum type. + pub fn is(&self) -> bool { + TypeId::of::() == self.type_id + } +} + +/// An iterator over the fields in the current enum variant. +pub struct VariantFieldIter<'a> { + container: &'a dyn Enum, + index: usize, +} + +impl<'a> VariantFieldIter<'a> { + pub fn new(container: &'a dyn Enum) -> Self { + Self { + container, + index: 0, + } + } +} + +impl<'a> Iterator for VariantFieldIter<'a> { + type Item = VariantField<'a>; + + fn next(&mut self) -> Option { + let value = match self.container.variant_type() { + VariantType::Unit => None, + VariantType::Tuple => Some(VariantField::Tuple(self.container.field_at(self.index)?)), + VariantType::Struct => { + let name = self.container.name_at(self.index)?; + Some(VariantField::Struct(name, self.container.field(name)?)) + } + }; + self.index += 1; + value + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.container.field_len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for VariantFieldIter<'a> {} + +pub enum VariantField<'a> { + Struct(&'a str, &'a dyn Reflect), + Tuple(&'a dyn Reflect), +} + +impl<'a> VariantField<'a> { + pub fn name(&self) -> Option<&'a str> { + if let Self::Struct(name, ..) = self { + Some(*name) + } else { + None + } + } + + pub fn value(&self) -> &'a dyn Reflect { + match self { + Self::Struct(.., value) | Self::Tuple(value) => *value, + } + } +} diff --git a/crates/bevy_reflect/src/enums/helpers.rs b/crates/bevy_reflect/src/enums/helpers.rs new file mode 100644 index 0000000000000..c14913d19dede --- /dev/null +++ b/crates/bevy_reflect/src/enums/helpers.rs @@ -0,0 +1,121 @@ +use crate::{Enum, Reflect, ReflectRef, VariantType}; +use std::fmt::Debug; +use std::hash::{Hash, Hasher}; + +/// Returns the `u64` hash of the given [enum](Enum). +#[inline] +pub fn enum_hash(value: &TEnum) -> Option { + let mut hasher = crate::ReflectHasher::default(); + std::any::Any::type_id(value).hash(&mut hasher); + value.variant_name().hash(&mut hasher); + value.variant_type().hash(&mut hasher); + for field in value.iter_fields() { + hasher.write_u64(field.value().reflect_hash()?); + } + Some(hasher.finish()) +} + +/// Compares an [`Enum`] with a [`Reflect`] value. +/// +/// Returns true if and only if all of the following are true: +/// - `b` is an enum; +/// - `b` is the same variant as `a`; +/// - For each field in `a`, `b` contains a field with the same name and +/// [`Reflect::reflect_partial_eq`] returns `Some(true)` for the two field +/// values. +#[inline] +pub fn enum_partial_eq(a: &TEnum, b: &dyn Reflect) -> Option { + // Both enums? + let b = if let ReflectRef::Enum(e) = b.reflect_ref() { + e + } else { + return Some(false); + }; + + // Same variant name? + if a.variant_name() != b.variant_name() { + return Some(false); + } + + // Same variant type? + if !a.is_variant(b.variant_type()) { + return Some(false); + } + + match a.variant_type() { + VariantType::Struct => { + // Same struct fields? + for field in a.iter_fields() { + let field_name = field.name().unwrap(); + if let Some(field_value) = b.field(field_name) { + if let Some(false) | None = field_value.reflect_partial_eq(field.value()) { + // Fields failed comparison + return Some(false); + } + } else { + // Field does not exist + return Some(false); + } + } + Some(true) + } + VariantType::Tuple => { + // Same tuple fields? + for (i, field) in a.iter_fields().enumerate() { + if let Some(field_value) = b.field_at(i) { + if let Some(false) | None = field_value.reflect_partial_eq(field.value()) { + // Fields failed comparison + return Some(false); + } + } else { + // Field does not exist + return Some(false); + } + } + Some(true) + } + _ => Some(true), + } +} + +/// The default debug formatter for [`Enum`] types. +/// +/// # Example +/// ``` +/// use bevy_reflect::Reflect; +/// #[derive(Reflect)] +/// enum MyEnum { +/// A, +/// B (usize), +/// C {value: i32} +/// } +/// +/// let my_enum: &dyn Reflect = &MyEnum::B(123); +/// println!("{:#?}", my_enum); +/// +/// // Output: +/// +/// // B ( +/// // 123, +/// // ) +/// ``` +#[inline] +pub fn enum_debug(dyn_enum: &dyn Enum, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match dyn_enum.variant_type() { + VariantType::Unit => f.write_str(dyn_enum.variant_name()), + VariantType::Tuple => { + let mut debug = f.debug_tuple(dyn_enum.variant_name()); + for field in dyn_enum.iter_fields() { + debug.field(&field.value() as &dyn Debug); + } + debug.finish() + } + VariantType::Struct => { + let mut debug = f.debug_struct(dyn_enum.variant_name()); + for field in dyn_enum.iter_fields() { + debug.field(field.name().unwrap(), &field.value() as &dyn Debug); + } + debug.finish() + } + } +} diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs new file mode 100644 index 0000000000000..6a1f80fd38cf3 --- /dev/null +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -0,0 +1,651 @@ +mod dynamic_enum; +mod enum_trait; +mod helpers; +mod variants; + +pub use dynamic_enum::*; +pub use enum_trait::*; +pub use helpers::*; +pub use variants::*; + +#[cfg(test)] +mod tests { + use crate as bevy_reflect; + use crate::*; + + #[derive(Reflect, Debug, PartialEq)] + enum MyEnum { + A, + B(usize, i32), + C { foo: f32, bar: bool }, + } + + #[test] + fn should_get_enum_type_info() { + let info = MyEnum::type_info(); + if let TypeInfo::Enum(info) = info { + assert!(info.is::(), "expected type to be `MyEnum`"); + assert_eq!(std::any::type_name::(), info.type_name()); + + // === MyEnum::A === // + assert_eq!("A", info.variant_at(0).unwrap().name()); + assert_eq!("A", info.variant("A").unwrap().name()); + if let VariantInfo::Unit(variant) = info.variant("A").unwrap() { + assert_eq!("A", variant.name()); + } else { + panic!("Expected `VariantInfo::Unit`"); + } + + // === MyEnum::B === // + assert_eq!("B", info.variant_at(1).unwrap().name()); + assert_eq!("B", info.variant("B").unwrap().name()); + if let VariantInfo::Tuple(variant) = info.variant("B").unwrap() { + assert!(variant.field_at(0).unwrap().is::()); + assert!(variant.field_at(1).unwrap().is::()); + } else { + panic!("Expected `VariantInfo::Tuple`"); + } + + // === MyEnum::C === // + assert_eq!("C", info.variant_at(2).unwrap().name()); + assert_eq!("C", info.variant("C").unwrap().name()); + if let VariantInfo::Struct(variant) = info.variant("C").unwrap() { + assert!(variant.field_at(0).unwrap().is::()); + assert!(variant.field("foo").unwrap().is::()); + } else { + panic!("Expected `VariantInfo::Struct`"); + } + } else { + panic!("Expected `TypeInfo::Enum`"); + } + } + + #[test] + fn dynamic_enum_should_set_variant_fields() { + // === Unit === // + let mut value = MyEnum::A; + let dyn_enum = DynamicEnum::from(MyEnum::A); + value.apply(&dyn_enum); + assert_eq!(MyEnum::A, value); + + // === Tuple === // + let mut value = MyEnum::B(0, 0); + let dyn_enum = DynamicEnum::from(MyEnum::B(123, 321)); + value.apply(&dyn_enum); + assert_eq!(MyEnum::B(123, 321), value); + + // === Struct === // + let mut value = MyEnum::C { + foo: 0.0, + bar: false, + }; + let dyn_enum = DynamicEnum::from(MyEnum::C { + foo: 1.23, + bar: true, + }); + value.apply(&dyn_enum); + assert_eq!( + MyEnum::C { + foo: 1.23, + bar: true, + }, + value + ); + } + + #[test] + fn partial_dynamic_enum_should_set_variant_fields() { + // === Tuple === // + let mut value = MyEnum::B(0, 0); + + let mut data = DynamicTuple::default(); + data.insert(123usize); + + let mut dyn_enum = DynamicEnum::default(); + dyn_enum.set_variant("B", data); + value.apply(&dyn_enum); + assert_eq!(MyEnum::B(123, 0), value); + + // === Struct === // + let mut value = MyEnum::C { + foo: 1.23, + bar: false, + }; + + let mut data = DynamicStruct::default(); + data.insert("bar", true); + + let mut dyn_enum = DynamicEnum::default(); + dyn_enum.set_variant("C", data); + value.apply(&dyn_enum); + assert_eq!( + MyEnum::C { + foo: 1.23, + bar: true, + }, + value + ); + } + + #[test] + fn dynamic_enum_should_apply_dynamic_enum() { + let mut a = DynamicEnum::from(MyEnum::B(123, 321)); + let b = DynamicEnum::from(MyEnum::B(123, 321)); + + // Sanity check that equality check works + assert!( + a.reflect_partial_eq(&b).unwrap_or_default(), + "dynamic enums should be equal" + ); + + a.set_variant("A", ()); + assert!( + !a.reflect_partial_eq(&b).unwrap_or_default(), + "dynamic enums should not be equal" + ); + + a.apply(&b); + assert!(a.reflect_partial_eq(&b).unwrap_or_default()); + } + + #[test] + fn dynamic_enum_should_change_variant() { + let mut value = MyEnum::A; + + // === MyEnum::A -> MyEnum::B === // + let mut dyn_enum = DynamicEnum::from(MyEnum::B(123, 321)); + value.apply(&dyn_enum); + assert_eq!(MyEnum::B(123, 321), value); + + // === MyEnum::B -> MyEnum::C === // + let mut data = DynamicStruct::default(); + data.insert("foo", 1.23_f32); + data.insert("bar", true); + dyn_enum.set_variant("C", data); + value.apply(&dyn_enum); + assert_eq!( + MyEnum::C { + foo: 1.23, + bar: true + }, + value + ); + + // === MyEnum::C -> MyEnum::B === // + let mut data = DynamicTuple::default(); + data.insert(123_usize); + data.insert(321_i32); + dyn_enum.set_variant("B", data); + value.apply(&dyn_enum); + assert_eq!(MyEnum::B(123, 321), value); + + // === MyEnum::B -> MyEnum::A === // + dyn_enum.set_variant("A", ()); + value.apply(&dyn_enum); + assert_eq!(MyEnum::A, value); + } + + #[test] + fn enum_should_iterate_fields() { + // === Unit === // + let value: &dyn Enum = &MyEnum::A; + assert_eq!(0, value.field_len()); + let mut iter = value.iter_fields(); + assert!(iter.next().is_none()); + + // === Tuple === // + let value: &dyn Enum = &MyEnum::B(123, 321); + assert_eq!(2, value.field_len()); + let mut iter = value.iter_fields(); + assert!(iter + .next() + .and_then(|field| field.value().reflect_partial_eq(&123_usize)) + .unwrap_or_default()); + assert!(iter + .next() + .and_then(|field| field.value().reflect_partial_eq(&321_i32)) + .unwrap_or_default()); + + // === Struct === // + let value: &dyn Enum = &MyEnum::C { + foo: 1.23, + bar: true, + }; + assert_eq!(2, value.field_len()); + let mut iter = value.iter_fields(); + assert!(iter + .next() + .and_then(|field| field + .value() + .reflect_partial_eq(&1.23_f32) + .and(field.name().map(|name| name == "foo"))) + .unwrap_or_default()); + assert!(iter + .next() + .and_then(|field| field + .value() + .reflect_partial_eq(&true) + .and(field.name().map(|name| name == "bar"))) + .unwrap_or_default()); + } + + #[test] + fn enum_should_return_correct_variant_type() { + // === Unit === // + let value = MyEnum::A; + assert_eq!(VariantType::Unit, value.variant_type()); + + // === Tuple === // + let value = MyEnum::B(0, 0); + assert_eq!(VariantType::Tuple, value.variant_type()); + + // === Struct === // + let value = MyEnum::C { + foo: 1.23, + bar: true, + }; + assert_eq!(VariantType::Struct, value.variant_type()); + } + + #[test] + fn enum_should_return_correct_variant_path() { + // === Unit === // + let value = MyEnum::A; + assert_eq!( + "bevy_reflect::enums::tests::MyEnum::A", + value.variant_path() + ); + + // === Tuple === // + let value = MyEnum::B(0, 0); + assert_eq!( + "bevy_reflect::enums::tests::MyEnum::B", + value.variant_path() + ); + + // === Struct === // + let value = MyEnum::C { + foo: 1.23, + bar: true, + }; + assert_eq!( + "bevy_reflect::enums::tests::MyEnum::C", + value.variant_path() + ); + } + + #[test] + #[should_panic(expected = "`((usize, i32))` is not an enum")] + fn applying_non_enum_should_panic() { + let mut value = MyEnum::B(0, 0); + let mut dyn_tuple = DynamicTuple::default(); + dyn_tuple.insert((123_usize, 321_i32)); + value.apply(&dyn_tuple); + } + + #[test] + #[allow(dead_code)] + fn should_skip_ignored_variants() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + #[reflect(ignore)] + B, + C, + } + + if let TypeInfo::Enum(info) = TestEnum::type_info() { + assert_eq!( + 2, + info.variant_len(), + "expected one of the variants to be ignored" + ); + assert_eq!("A", info.variant_at(0).unwrap().name()); + assert_eq!("C", info.variant_at(1).unwrap().name()); + } else { + panic!("expected `TypeInfo::Enum`"); + } + } + + #[test] + fn should_skip_ignored_fields() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B, + C { + #[reflect(ignore)] + foo: f32, + bar: bool, + }, + } + + if let TypeInfo::Enum(info) = TestEnum::type_info() { + assert_eq!(3, info.variant_len()); + if let VariantInfo::Struct(variant) = info.variant("C").unwrap() { + assert_eq!( + 1, + variant.field_len(), + "expected one of the fields to be ignored" + ); + assert!(variant.field_at(0).unwrap().is::()); + } else { + panic!("expected `VariantInfo::Struct`"); + } + } else { + panic!("expected `TypeInfo::Enum`"); + } + } + + #[test] + fn enum_should_allow_generics() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B(T), + C { value: T }, + } + + if let TypeInfo::Enum(info) = TestEnum::::type_info() { + if let VariantInfo::Tuple(variant) = info.variant("B").unwrap() { + assert!(variant.field_at(0).unwrap().is::()); + } else { + panic!("expected `VariantInfo::Struct`"); + } + if let VariantInfo::Struct(variant) = info.variant("C").unwrap() { + assert!(variant.field("value").unwrap().is::()); + } else { + panic!("expected `VariantInfo::Struct`"); + } + } else { + panic!("expected `TypeInfo::Enum`"); + } + + let mut value = TestEnum::::A; + + // === Tuple === // + let mut data = DynamicTuple::default(); + data.insert(1.23_f32); + let dyn_enum = DynamicEnum::new(std::any::type_name::>(), "B", data); + value.apply(&dyn_enum); + assert_eq!(TestEnum::B(1.23), value); + + // === Struct === // + let mut data = DynamicStruct::default(); + data.insert("value", 1.23_f32); + let dyn_enum = DynamicEnum::new(std::any::type_name::>(), "C", data); + value.apply(&dyn_enum); + assert_eq!(TestEnum::C { value: 1.23 }, value); + } + + #[test] + fn enum_should_allow_struct_fields() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B(TestStruct), + C { value: TestStruct }, + } + + #[derive(Reflect, FromReflect, Debug, PartialEq)] + struct TestStruct(usize); + + let mut value = TestEnum::A; + + // === Tuple === // + let mut data = DynamicTuple::default(); + data.insert(TestStruct(123)); + let dyn_enum = DynamicEnum::new(std::any::type_name::(), "B", data); + value.apply(&dyn_enum); + assert_eq!(TestEnum::B(TestStruct(123)), value); + + // === Struct === // + let mut data = DynamicStruct::default(); + data.insert("value", TestStruct(123)); + let dyn_enum = DynamicEnum::new(std::any::type_name::(), "C", data); + value.apply(&dyn_enum); + assert_eq!( + TestEnum::C { + value: TestStruct(123) + }, + value + ); + } + + #[test] + fn enum_should_allow_nesting_enums() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B(OtherEnum), + C { value: OtherEnum }, + } + + #[derive(Reflect, FromReflect, Debug, PartialEq)] + enum OtherEnum { + A, + B(usize), + C { value: f32 }, + } + + let mut value = TestEnum::A; + + // === Tuple === // + let mut data = DynamicTuple::default(); + data.insert(OtherEnum::B(123)); + let dyn_enum = DynamicEnum::new(std::any::type_name::(), "B", data); + value.apply(&dyn_enum); + assert_eq!(TestEnum::B(OtherEnum::B(123)), value); + + // === Struct === // + let mut data = DynamicStruct::default(); + data.insert("value", OtherEnum::C { value: 1.23 }); + let dyn_enum = DynamicEnum::new(std::any::type_name::(), "C", data); + value.apply(&dyn_enum); + assert_eq!( + TestEnum::C { + value: OtherEnum::C { value: 1.23 } + }, + value + ); + } + + #[test] + fn enum_should_apply() { + let mut value: Box = Box::new(MyEnum::A); + + // === MyEnum::A -> MyEnum::A === // + value.apply(&MyEnum::A); + assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default()); + + // === MyEnum::A -> MyEnum::B === // + value.apply(&MyEnum::B(123, 321)); + assert!(value + .reflect_partial_eq(&MyEnum::B(123, 321)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::B === // + value.apply(&MyEnum::B(321, 123)); + assert!(value + .reflect_partial_eq(&MyEnum::B(321, 123)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::C === // + value.apply(&MyEnum::C { + foo: 1.23, + bar: true, + }); + assert!(value + .reflect_partial_eq(&MyEnum::C { + foo: 1.23, + bar: true + }) + .unwrap_or_default()); + + // === MyEnum::C -> MyEnum::C === // + value.apply(&MyEnum::C { + foo: 3.21, + bar: false, + }); + assert!(value + .reflect_partial_eq(&MyEnum::C { + foo: 3.21, + bar: false + }) + .unwrap_or_default()); + + // === MyEnum::C -> MyEnum::B === // + value.apply(&MyEnum::B(123, 321)); + assert!(value + .reflect_partial_eq(&MyEnum::B(123, 321)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::A === // + value.apply(&MyEnum::A); + assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default()); + } + + #[test] + fn enum_should_set() { + let mut value: Box = Box::new(MyEnum::A); + + // === MyEnum::A -> MyEnum::A === // + value.set(Box::new(MyEnum::A)).unwrap(); + assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default()); + + // === MyEnum::A -> MyEnum::B === // + value.set(Box::new(MyEnum::B(123, 321))).unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::B(123, 321)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::B === // + value.set(Box::new(MyEnum::B(321, 123))).unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::B(321, 123)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::C === // + value + .set(Box::new(MyEnum::C { + foo: 1.23, + bar: true, + })) + .unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::C { + foo: 1.23, + bar: true + }) + .unwrap_or_default()); + + // === MyEnum::C -> MyEnum::C === // + value + .set(Box::new(MyEnum::C { + foo: 3.21, + bar: false, + })) + .unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::C { + foo: 3.21, + bar: false + }) + .unwrap_or_default()); + + // === MyEnum::C -> MyEnum::B === // + value.set(Box::new(MyEnum::B(123, 321))).unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::B(123, 321)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::A === // + value.set(Box::new(MyEnum::A)).unwrap(); + assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default()); + } + + #[test] + fn enum_should_partial_eq() { + #[derive(Reflect)] + enum TestEnum { + A, + A1, + B(usize), + B1(usize), + B2(usize, usize), + C { value: i32 }, + C1 { value: i32 }, + C2 { value: f32 }, + } + + let a: &dyn Reflect = &TestEnum::A; + let b: &dyn Reflect = &TestEnum::A; + assert!( + a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::A == TestEnum::A" + ); + + let a: &dyn Reflect = &TestEnum::A; + let b: &dyn Reflect = &TestEnum::A1; + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::A != TestEnum::A1" + ); + + let a: &dyn Reflect = &TestEnum::B(123); + let b: &dyn Reflect = &TestEnum::B(123); + assert!( + a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::B(123) == TestEnum::B(123)" + ); + + let a: &dyn Reflect = &TestEnum::B(123); + let b: &dyn Reflect = &TestEnum::B(321); + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::B(123) != TestEnum::B(321)" + ); + + let a: &dyn Reflect = &TestEnum::B(123); + let b: &dyn Reflect = &TestEnum::B1(123); + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::B(123) != TestEnum::B1(123)" + ); + + let a: &dyn Reflect = &TestEnum::B(123); + let b: &dyn Reflect = &TestEnum::B2(123, 123); + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::B(123) != TestEnum::B2(123, 123)" + ); + + let a: &dyn Reflect = &TestEnum::C { value: 123 }; + let b: &dyn Reflect = &TestEnum::C { value: 123 }; + assert!( + a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::C{{value: 123}} == TestEnum::C{{value: 123}}" + ); + + let a: &dyn Reflect = &TestEnum::C { value: 123 }; + let b: &dyn Reflect = &TestEnum::C { value: 321 }; + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::C{{value: 123}} != TestEnum::C{{value: 321}}" + ); + + let a: &dyn Reflect = &TestEnum::C { value: 123 }; + let b: &dyn Reflect = &TestEnum::C1 { value: 123 }; + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::C{{value: 123}} != TestEnum::C1{{value: 123}}" + ); + + let a: &dyn Reflect = &TestEnum::C { value: 123 }; + let b: &dyn Reflect = &TestEnum::C2 { value: 1.23 }; + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::C{{value: 123}} != TestEnum::C2{{value: 1.23}}" + ); + } +} diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs new file mode 100644 index 0000000000000..b1a9740d21c75 --- /dev/null +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -0,0 +1,231 @@ +use crate::{NamedField, UnnamedField}; +use bevy_utils::HashMap; +use std::borrow::Cow; +use std::slice::Iter; + +/// Describes the form of an enum variant. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum VariantType { + /// Struct enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A { + /// foo: usize + /// } + /// } + /// ``` + Struct, + /// Tuple enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A(usize) + /// } + /// ``` + Tuple, + /// Unit enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A + /// } + /// ``` + Unit, +} + +/// A container for compile-time enum variant info. +#[derive(Clone, Debug)] +pub enum VariantInfo { + /// Struct enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A { + /// foo: usize + /// } + /// } + /// ``` + Struct(StructVariantInfo), + /// Tuple enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A(usize) + /// } + /// ``` + Tuple(TupleVariantInfo), + /// Unit enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A + /// } + /// ``` + Unit(UnitVariantInfo), +} + +impl VariantInfo { + pub fn name(&self) -> &Cow<'static, str> { + match self { + Self::Struct(info) => info.name(), + Self::Tuple(info) => info.name(), + Self::Unit(info) => info.name(), + } + } +} + +/// Type info for struct variants. +#[derive(Clone, Debug)] +pub struct StructVariantInfo { + name: Cow<'static, str>, + fields: Box<[NamedField]>, + field_indices: HashMap, usize>, +} + +impl StructVariantInfo { + /// Create a new [`StructVariantInfo`]. + pub fn new(name: &str, fields: &[NamedField]) -> Self { + let field_indices = Self::collect_field_indices(fields); + + Self { + name: Cow::Owned(name.into()), + fields: fields.to_vec().into_boxed_slice(), + field_indices, + } + } + + /// Create a new [`StructVariantInfo`] using a static string. + /// + /// This helps save an allocation when the string has a static lifetime, such + /// as when using defined sa a literal. + pub fn new_static(name: &'static str, fields: &[NamedField]) -> Self { + let field_indices = Self::collect_field_indices(fields); + Self { + name: Cow::Borrowed(name), + fields: fields.to_vec().into_boxed_slice(), + field_indices, + } + } + + /// The name of this variant. + pub fn name(&self) -> &Cow<'static, str> { + &self.name + } + + /// Get the field with the given name. + pub fn field(&self, name: &str) -> Option<&NamedField> { + self.field_indices + .get(name) + .map(|index| &self.fields[*index]) + } + + /// Get the field at the given index. + pub fn field_at(&self, index: usize) -> Option<&NamedField> { + self.fields.get(index) + } + + /// Get the index of the field with the given name. + pub fn index_of(&self, name: &str) -> Option { + self.field_indices.get(name).copied() + } + + /// Iterate over the fields of this variant. + pub fn iter(&self) -> Iter<'_, NamedField> { + self.fields.iter() + } + + /// The total number of fields in this variant. + pub fn field_len(&self) -> usize { + self.fields.len() + } + + fn collect_field_indices(fields: &[NamedField]) -> HashMap, usize> { + fields + .iter() + .enumerate() + .map(|(index, field)| { + let name = field.name().clone(); + (name, index) + }) + .collect() + } +} + +/// Type info for tuple variants. +#[derive(Clone, Debug)] +pub struct TupleVariantInfo { + name: Cow<'static, str>, + fields: Box<[UnnamedField]>, +} + +impl TupleVariantInfo { + /// Create a new [`TupleVariantInfo`]. + pub fn new(name: &str, fields: &[UnnamedField]) -> Self { + Self { + name: Cow::Owned(name.into()), + fields: fields.to_vec().into_boxed_slice(), + } + } + + /// Create a new [`TupleVariantInfo`] using a static string. + /// + /// This helps save an allocation when the string has a static lifetime, such + /// as when using defined sa a literal. + pub fn new_static(name: &'static str, fields: &[UnnamedField]) -> Self { + Self { + name: Cow::Borrowed(name), + fields: fields.to_vec().into_boxed_slice(), + } + } + + /// The name of this variant. + pub fn name(&self) -> &Cow<'static, str> { + &self.name + } + + /// Get the field at the given index. + pub fn field_at(&self, index: usize) -> Option<&UnnamedField> { + self.fields.get(index) + } + + /// Iterate over the fields of this variant. + pub fn iter(&self) -> Iter<'_, UnnamedField> { + self.fields.iter() + } + + /// The total number of fields in this variant. + pub fn field_len(&self) -> usize { + self.fields.len() + } +} + +/// Type info for unit variants. +#[derive(Clone, Debug)] +pub struct UnitVariantInfo { + name: Cow<'static, str>, +} + +impl UnitVariantInfo { + /// Create a new [`UnitVariantInfo`]. + pub fn new(name: &str) -> Self { + Self { + name: Cow::Owned(name.into()), + } + } + + /// Create a new [`UnitVariantInfo`] using a static string. + /// + /// This helps save an allocation when the string has a static lifetime, such + /// as when using defined sa a literal. + pub fn new_static(name: &'static str) -> Self { + Self { + name: Cow::Borrowed(name), + } + } + + /// The name of this variant. + pub fn name(&self) -> &Cow<'static, str> { + &self.name + } +} diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 9f601d25cb049..7b66aba360249 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,8 +1,10 @@ use crate::{self as bevy_reflect, ReflectFromPtr}; use crate::{ - map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, FromType, - GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, ReflectDeserialize, - ReflectMut, ReflectRef, ReflectSerialize, TypeInfo, TypeRegistration, Typed, ValueInfo, + map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum, + EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, + Reflect, ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TupleVariantInfo, + TypeInfo, TypeRegistration, Typed, UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter, + VariantInfo, VariantType, }; use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; @@ -36,7 +38,6 @@ impl_reflect_value!(isize(Debug, Hash, PartialEq, Serialize, Deserialize)); impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize)); impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize)); impl_reflect_value!(String(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(Option()); impl_reflect_value!(Result()); impl_reflect_value!(HashSet()); impl_reflect_value!(Range()); @@ -72,7 +73,6 @@ impl_from_reflect_value!(isize); impl_from_reflect_value!(f32); impl_from_reflect_value!(f64); impl_from_reflect_value!(String); -impl_from_reflect_value!(Option); impl_from_reflect_value!(HashSet); impl_from_reflect_value!(Range); impl_from_reflect_value!(Duration); @@ -585,6 +585,218 @@ impl Reflect for Cow<'static, str> { } } +impl GetTypeRegistration for Option { + fn get_type_registration() -> TypeRegistration { + TypeRegistration::of::>() + } +} + +impl Enum for Option { + fn field(&self, _name: &str) -> Option<&dyn Reflect> { + None + } + + fn field_at(&self, index: usize) -> Option<&dyn Reflect> { + match self { + Some(value) if index == 0 => Some(value), + _ => None, + } + } + + fn field_mut(&mut self, _name: &str) -> Option<&mut dyn Reflect> { + None + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> { + match self { + Some(value) if index == 0 => Some(value), + _ => None, + } + } + + fn index_of(&self, _name: &str) -> Option { + None + } + + fn name_at(&self, _index: usize) -> Option<&str> { + None + } + + fn iter_fields(&self) -> VariantFieldIter { + VariantFieldIter::new(self) + } + + #[inline] + fn field_len(&self) -> usize { + match self { + Some(..) => 1, + None => 0, + } + } + + #[inline] + fn variant_name(&self) -> &str { + match self { + Some(..) => "Some", + None => "None", + } + } + + #[inline] + fn variant_type(&self) -> VariantType { + match self { + Some(..) => VariantType::Tuple, + None => VariantType::Unit, + } + } + + fn clone_dynamic(&self) -> DynamicEnum { + DynamicEnum::from_ref::(self) + } +} + +impl Reflect for Option { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn get_type_info(&self) -> &'static TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + #[inline] + fn apply(&mut self, value: &dyn Reflect) { + if let ReflectRef::Enum(value) = value.reflect_ref() { + if self.variant_name() == value.variant_name() { + // Same variant -> just update fields + for (index, field) in value.iter_fields().enumerate() { + let name = value.name_at(index).unwrap(); + if let Some(v) = self.field_mut(name) { + v.apply(field.value()); + } + } + } else { + // New variant -> perform a switch + match value.variant_name() { + "Some" => { + let field = value + .field_at(0) + .expect("Field at index 0 should exist") + .clone_value(); + let field = field.take::().unwrap_or_else(|_| { + panic!( + "Field at index 0 should be of type {}", + std::any::type_name::() + ) + }); + *self = Some(field); + } + "None" => { + *self = None; + } + _ => panic!("Enum is not a {}.", std::any::type_name::()), + } + } + } + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + fn reflect_ref(&self) -> ReflectRef { + ReflectRef::Enum(self) + } + + fn reflect_mut(&mut self) -> ReflectMut { + ReflectMut::Enum(self) + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone()) + } + + fn reflect_hash(&self) -> Option { + crate::enum_hash(self) + } + + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + crate::enum_partial_eq(self, value) + } +} + +impl FromReflect for Option { + fn from_reflect(reflect: &dyn Reflect) -> Option { + if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() { + match dyn_enum.variant_name() { + "Some" => { + let field = dyn_enum + .field_at(0) + .expect("Field at index 0 should exist") + .clone_value(); + let field = field.take::().unwrap_or_else(|_| { + panic!( + "Field at index 0 should be of type {}", + std::any::type_name::() + ) + }); + Some(Some(field)) + } + "None" => Some(None), + name => panic!( + "variant with name `{}` does not exist on enum `{}`", + name, + std::any::type_name::() + ), + } + } else { + None + } + } +} + +impl Typed for Option { + fn type_info() -> &'static TypeInfo { + static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); + CELL.get_or_insert::(|| { + let none_variant = VariantInfo::Unit(UnitVariantInfo::new_static("None")); + let some_variant = VariantInfo::Tuple(TupleVariantInfo::new_static( + "Some", + &[UnnamedField::new::(0)], + )); + TypeInfo::Enum(EnumInfo::new::(&[none_variant, some_variant])) + }) + } +} + impl Typed for Cow<'static, str> { fn type_info() -> &'static TypeInfo { static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); @@ -615,7 +827,9 @@ impl FromReflect for Cow<'static, str> { #[cfg(test)] mod tests { - use crate::{Reflect, ReflectSerialize, TypeRegistry}; + use crate::{ + Enum, Reflect, ReflectSerialize, TypeInfo, TypeRegistry, Typed, VariantInfo, VariantType, + }; use bevy_utils::HashMap; use std::f32::consts::{PI, TAU}; @@ -691,11 +905,71 @@ mod tests { } #[test] - fn should_not_partial_eq_option() { - // Option does not contain a `PartialEq` implementation, so it should return `None` + fn should_partial_eq_option() { let a: &dyn Reflect = &Some(123); let b: &dyn Reflect = &Some(123); - assert_eq!(None, a.reflect_partial_eq(b)); + assert_eq!(Some(true), a.reflect_partial_eq(b)); + } + + #[test] + fn option_should_impl_enum() { + let mut value = Some(123usize); + + assert!(value + .reflect_partial_eq(&Some(123usize)) + .unwrap_or_default()); + assert!(!value + .reflect_partial_eq(&Some(321usize)) + .unwrap_or_default()); + + assert_eq!("Some", value.variant_name()); + assert_eq!("core::option::Option::Some", value.variant_path()); + + if value.is_variant(VariantType::Tuple) { + if let Some(field) = value + .field_at_mut(0) + .and_then(|field| field.downcast_mut::()) + { + *field = 321; + } + } else { + panic!("expected `VariantType::Tuple`"); + } + + assert_eq!(Some(321), value); + } + + #[test] + fn option_should_impl_typed() { + type MyOption = Option; + let info = MyOption::type_info(); + if let TypeInfo::Enum(info) = info { + assert_eq!( + "None", + info.variant_at(0).unwrap().name(), + "Expected `None` to be variant at index `0`" + ); + assert_eq!( + "Some", + info.variant_at(1).unwrap().name(), + "Expected `Some` to be variant at index `1`" + ); + assert_eq!("Some", info.variant("Some").unwrap().name()); + if let VariantInfo::Tuple(variant) = info.variant("Some").unwrap() { + assert!( + variant.field_at(0).unwrap().is::(), + "Expected `Some` variant to contain `i32`" + ); + assert!( + variant.field_at(1).is_none(), + "Expected `Some` variant to only contain 1 field" + ); + } else { + panic!("Expected `VariantInfo::Tuple`"); + } + } else { + panic!("Expected `TypeInfo::Enum`"); + } } #[test] fn nonzero_usize_impl_reflect_from_reflect() { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 7fd60f0f928b3..f6dbc37ce67f3 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -26,6 +26,7 @@ mod impls { pub use self::std::*; } +mod enums; pub mod serde; pub mod std_traits; pub mod utility; @@ -40,6 +41,7 @@ pub mod prelude { } pub use array::*; +pub use enums::*; pub use fields::*; pub use impls::*; pub use list::*; @@ -675,10 +677,6 @@ mod tests { panic!("Expected `TypeInfo::TupleStruct`"); } - let value: &dyn Reflect = &MyTupleStruct(123, 321, MyStruct { foo: 123, bar: 321 }); - let info = value.get_type_info(); - assert!(info.is::()); - // Tuple type MyTuple = (u32, f32, String); @@ -829,8 +827,10 @@ mod tests { map: HashMap, a_struct: SomeStruct, a_tuple_struct: SomeTupleStruct, + enum_unit: SomeEnum, + enum_tuple: SomeEnum, + enum_struct: SomeEnum, custom: CustomDebug, - unknown: Option, #[reflect(ignore)] #[allow(dead_code)] ignored: isize, @@ -841,6 +841,13 @@ mod tests { foo: String, } + #[derive(Reflect)] + enum SomeEnum { + A, + B(usize), + C { value: i32 }, + } + #[derive(Reflect)] struct SomeTupleStruct(String); @@ -865,8 +872,10 @@ mod tests { foo: String::from("A Struct!"), }, a_tuple_struct: SomeTupleStruct(String::from("A Tuple Struct!")), + enum_unit: SomeEnum::A, + enum_tuple: SomeEnum::B(123), + enum_struct: SomeEnum::C { value: 321 }, custom: CustomDebug, - unknown: Some(String::from("Enums aren't supported yet :(")), ignored: 321, }; @@ -893,8 +902,14 @@ bevy_reflect::tests::should_reflect_debug::Test { a_tuple_struct: bevy_reflect::tests::should_reflect_debug::SomeTupleStruct( "A Tuple Struct!", ), + enum_unit: A, + enum_tuple: B( + 123, + ), + enum_struct: C { + value: 321, + }, custom: Cool debug!, - unknown: Reflect(core::option::Option), }"#; assert_eq!(expected, format!("\n{:#?}", reflected)); diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index c1d41710e6f0d..3b3b7c274445a 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -1,6 +1,7 @@ use crate::{ - array_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, - tuple_struct_debug, Array, List, Map, Struct, Tuple, TupleStruct, TypeInfo, Typed, ValueInfo, + array_debug, enum_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, + tuple_struct_debug, Array, Enum, List, Map, Struct, Tuple, TupleStruct, TypeInfo, Typed, + ValueInfo, }; use std::{ any::{self, Any, TypeId}, @@ -23,6 +24,7 @@ pub enum ReflectRef<'a> { List(&'a dyn List), Array(&'a dyn Array), Map(&'a dyn Map), + Enum(&'a dyn Enum), Value(&'a dyn Reflect), } @@ -39,6 +41,7 @@ pub enum ReflectMut<'a> { List(&'a mut dyn List), Array(&'a mut dyn Array), Map(&'a mut dyn Map), + Enum(&'a mut dyn Enum), Value(&'a mut dyn Reflect), } @@ -170,6 +173,7 @@ pub trait Reflect: Any + Send + Sync { ReflectRef::List(dyn_list) => list_debug(dyn_list, f), ReflectRef::Array(dyn_array) => array_debug(dyn_array, f), ReflectRef::Map(dyn_map) => map_debug(dyn_map, f), + ReflectRef::Enum(dyn_enum) => enum_debug(dyn_enum, f), _ => write!(f, "Reflect({})", self.type_name()), } } diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index 950208fbea8e3..ad24185de15f3 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -1,9 +1,9 @@ use crate::{ - serde::type_fields, DynamicArray, DynamicList, DynamicMap, DynamicStruct, DynamicTuple, - DynamicTupleStruct, Map, Reflect, ReflectDeserialize, TypeRegistry, + serde::type_fields, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, + DynamicTuple, DynamicTupleStruct, Map, Reflect, ReflectDeserialize, TypeRegistry, }; use erased_serde::Deserializer; -use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor}; +use serde::de::{self, DeserializeSeed, Error, MapAccess, SeqAccess, Visitor}; pub trait DeserializeValue { fn deserialize( @@ -203,6 +203,16 @@ impl<'a, 'de> Visitor<'de> for ReflectVisitor<'a> { })?; return Ok(Box::new(array)); } + type_fields::ENUM => { + let type_name = type_name + .take() + .ok_or_else(|| de::Error::missing_field(type_fields::TYPE))?; + let mut dynamic_enum = map.next_value_seed(EnumDeserializer { + registry: self.registry, + })?; + dynamic_enum.set_name(type_name); + return Ok(Box::new(dynamic_enum)); + } type_fields::VALUE => { let type_name = type_name .take() @@ -507,3 +517,187 @@ impl<'a, 'de> Visitor<'de> for TupleVisitor<'a> { Ok(tuple) } } + +struct EnumDeserializer<'a> { + registry: &'a TypeRegistry, +} + +impl<'a, 'de> DeserializeSeed<'de> for EnumDeserializer<'a> { + type Value = DynamicEnum; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_map(EnumVisitor { + registry: self.registry, + }) + } +} + +struct EnumVisitor<'a> { + registry: &'a TypeRegistry, +} + +impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> { + type Value = DynamicEnum; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("enum value") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let key = map.next_key::()?; + match key.as_deref() { + Some(type_fields::VARIANT) => {} + Some(key) => return Err(V::Error::unknown_field(key, &[type_fields::VARIANT])), + _ => { + return Err(V::Error::missing_field(type_fields::VARIANT)); + } + } + + let variant_name = map.next_value::()?; + + let mut dynamic_enum = DynamicEnum::default(); + + let key = map.next_key::()?; + match key.as_deref() { + Some(type_fields::STRUCT) => { + let dynamic_struct = map.next_value_seed(StructDeserializer { + registry: self.registry, + })?; + dynamic_enum.set_variant(variant_name, dynamic_struct); + } + Some(type_fields::TUPLE) => { + let dynamic_tuple = map.next_value_seed(TupleDeserializer { + registry: self.registry, + })?; + dynamic_enum.set_variant(variant_name, dynamic_tuple); + } + Some(invalid_key) => { + return Err(V::Error::unknown_field( + invalid_key, + &[type_fields::STRUCT, type_fields::TUPLE], + )); + } + None => dynamic_enum.set_variant(variant_name, ()), + } + + Ok(dynamic_enum) + } +} + +#[cfg(test)] +mod tests { + use super::ReflectDeserializer; + use crate as bevy_reflect; + use crate::prelude::*; + use crate::{DynamicEnum, TypeRegistry}; + use ::serde::de::DeserializeSeed; + + fn get_registry() -> TypeRegistry { + let mut registry = TypeRegistry::default(); + registry.register::(); + registry.register::(); + registry.register::(); + registry.register::<(f32, f32)>(); + registry + } + + #[test] + fn enum_should_deserialize() { + #[derive(Reflect)] + enum MyEnum { + Unit, + NewType(usize), + Tuple(f32, f32), + Struct { value: String }, + } + + let mut registry = get_registry(); + registry.register::(); + + // === Unit Variant === // + let input = r#"{ + "type": "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum", + "enum": { + "variant": "Unit", + }, +}"#; + let reflect_deserializer = ReflectDeserializer::new(®istry); + let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); + let output = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + + let expected = DynamicEnum::from(MyEnum::Unit); + assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); + + // === NewType Variant === // + let input = r#"{ + "type": "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum", + "enum": { + "variant": "NewType", + "tuple": [ + { + "type": "usize", + "value": 123, + }, + ], + }, +}"#; + let reflect_deserializer = ReflectDeserializer::new(®istry); + let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); + let output = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + + let expected = DynamicEnum::from(MyEnum::NewType(123)); + assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); + + // === Tuple Variant === // + let input = r#"{ + "type": "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum", + "enum": { + "variant": "Tuple", + "tuple": [ + { + "type": "f32", + "value": 1.23, + }, + { + "type": "f32", + "value": 3.21, + }, + ], + }, +}"#; + let reflect_deserializer = ReflectDeserializer::new(®istry); + let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); + let output = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + + let expected = DynamicEnum::from(MyEnum::Tuple(1.23, 3.21)); + assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); + + // === Struct Variant === // + let input = r#"{ + "type": "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum", + "enum": { + "variant": "Struct", + "struct": { + "value": { + "type": "alloc::string::String", + "value": "I <3 Enums", + }, + }, + }, +}"#; + let reflect_deserializer = ReflectDeserializer::new(®istry); + let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); + let output = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + + let expected = DynamicEnum::from(MyEnum::Struct { + value: String::from("I <3 Enums"), + }); + assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); + } +} diff --git a/crates/bevy_reflect/src/serde/mod.rs b/crates/bevy_reflect/src/serde/mod.rs index 5e664112a8860..7c7c18f4ea954 100644 --- a/crates/bevy_reflect/src/serde/mod.rs +++ b/crates/bevy_reflect/src/serde/mod.rs @@ -9,6 +9,8 @@ pub(crate) mod type_fields { pub const MAP: &str = "map"; pub const STRUCT: &str = "struct"; pub const TUPLE_STRUCT: &str = "tuple_struct"; + pub const ENUM: &str = "enum"; + pub const VARIANT: &str = "variant"; pub const TUPLE: &str = "tuple"; pub const LIST: &str = "list"; pub const ARRAY: &str = "array"; diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index de0ad7760a6fa..0dd239d6305a7 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -1,10 +1,11 @@ use crate::{ - serde::type_fields, Array, List, Map, Reflect, ReflectRef, ReflectSerialize, Struct, Tuple, - TupleStruct, TypeRegistry, + serde::type_fields, Array, Enum, List, Map, Reflect, ReflectRef, ReflectSerialize, Struct, + Tuple, TupleStruct, TypeRegistry, VariantType, }; +use serde::ser::Error; use serde::{ ser::{SerializeMap, SerializeSeq}, - Serialize, + Serialize, Serializer, }; pub enum Serializable<'a> { @@ -84,6 +85,11 @@ impl<'a> Serialize for ReflectSerializer<'a> { registry: self.registry, } .serialize(serializer), + ReflectRef::Enum(value) => EnumSerializer { + enum_value: value, + registry: self.registry, + } + .serialize(serializer), ReflectRef::Value(value) => ReflectValueSerializer { registry: self.registry, value, @@ -198,6 +204,117 @@ impl<'a> Serialize for TupleStructValueSerializer<'a> { } } +pub struct EnumSerializer<'a> { + pub enum_value: &'a dyn Enum, + pub registry: &'a TypeRegistry, +} + +impl<'a> Serialize for EnumSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_map(Some(2))?; + + state.serialize_entry(type_fields::TYPE, self.enum_value.type_name())?; + state.serialize_entry( + type_fields::ENUM, + &EnumValueSerializer { + enum_value: self.enum_value, + registry: self.registry, + }, + )?; + state.end() + } +} + +pub struct EnumValueSerializer<'a> { + pub enum_value: &'a dyn Enum, + pub registry: &'a TypeRegistry, +} + +impl<'a> Serialize for EnumValueSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let variant_type = self.enum_value.variant_type(); + let variant_name = self.enum_value.variant_name(); + + let mut state = if matches!(variant_type, VariantType::Unit) { + serializer.serialize_map(Some(1))? + } else { + serializer.serialize_map(Some(2))? + }; + + state.serialize_entry(type_fields::VARIANT, variant_name)?; + + match self.enum_value.variant_type() { + VariantType::Struct => { + state.serialize_key(type_fields::STRUCT)?; + state.serialize_value(&StructVariantSerializer { + enum_value: self.enum_value, + registry: self.registry, + })?; + } + VariantType::Tuple => { + state.serialize_key(type_fields::TUPLE)?; + state.serialize_value(&TupleVariantSerializer { + enum_value: self.enum_value, + registry: self.registry, + })?; + } + _ => {} + } + + state.end() + } +} + +pub struct TupleVariantSerializer<'a> { + pub enum_value: &'a dyn Enum, + pub registry: &'a TypeRegistry, +} + +impl<'a> Serialize for TupleVariantSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let field_len = self.enum_value.field_len(); + let mut state = serializer.serialize_seq(Some(field_len))?; + for field in self.enum_value.iter_fields() { + state.serialize_element(&ReflectSerializer::new(field.value(), self.registry))?; + } + state.end() + } +} + +pub struct StructVariantSerializer<'a> { + pub enum_value: &'a dyn Enum, + pub registry: &'a TypeRegistry, +} + +impl<'a> Serialize for StructVariantSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let field_len = self.enum_value.field_len(); + let mut state = serializer.serialize_map(Some(field_len))?; + for (index, field) in self.enum_value.iter_fields().enumerate() { + let name = field.name().ok_or_else(|| { + S::Error::custom(format_args!( + "struct variant missing name for field at index {}", + index + )) + })?; + state.serialize_entry(name, &ReflectSerializer::new(field.value(), self.registry))?; + } + state.end() + } +} + pub struct TupleSerializer<'a> { pub tuple: &'a dyn Tuple, pub registry: &'a TypeRegistry, @@ -366,3 +483,109 @@ impl<'a> Serialize for ArrayValueSerializer<'a> { state.end() } } + +#[cfg(test)] +mod tests { + use super::ReflectSerializer; + use crate as bevy_reflect; + use crate::prelude::*; + use crate::TypeRegistry; + use ron::ser::PrettyConfig; + + fn get_registry() -> TypeRegistry { + let mut registry = TypeRegistry::default(); + registry.register::(); + registry.register::(); + registry.register::(); + registry.register::<(f32, f32)>(); + registry + } + + #[test] + fn enum_should_serialize() { + #[derive(Reflect)] + enum MyEnum { + Unit, + NewType(usize), + Tuple(f32, f32), + Struct { value: String }, + } + + let mut registry = get_registry(); + registry.register::(); + + let config = PrettyConfig::default().new_line(String::from("\n")); + + // === Unit Variant === // + let value = MyEnum::Unit; + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); + let expected = r#"{ + "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", + "enum": { + "variant": "Unit", + }, +}"#; + assert_eq!(expected, output); + + // === NewType Variant === // + let value = MyEnum::NewType(123); + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); + let expected = r#"{ + "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", + "enum": { + "variant": "NewType", + "tuple": [ + { + "type": "usize", + "value": 123, + }, + ], + }, +}"#; + assert_eq!(expected, output); + + // === Tuple Variant === // + let value = MyEnum::Tuple(1.23, 3.21); + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); + let expected = r#"{ + "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", + "enum": { + "variant": "Tuple", + "tuple": [ + { + "type": "f32", + "value": 1.23, + }, + { + "type": "f32", + "value": 3.21, + }, + ], + }, +}"#; + assert_eq!(expected, output); + + // === Struct Variant === // + let value = MyEnum::Struct { + value: String::from("I <3 Enums"), + }; + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, config).unwrap(); + let expected = r#"{ + "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", + "enum": { + "variant": "Struct", + "struct": { + "value": { + "type": "alloc::string::String", + "value": "I <3 Enums", + }, + }, + }, +}"#; + assert_eq!(expected, output.replace('\r', "")); + } +} diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 60a9ba9e6125e..4f0b7819f258d 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -276,6 +276,11 @@ impl DynamicStruct { self.insert_boxed(name, Box::new(value)); } } + + /// Gets the index of the field with the given name. + pub fn index_of(&self, name: &str) -> Option { + self.field_indices.get(name).copied() + } } impl Struct for DynamicStruct { diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 942ba84fdf6e3..afbe037b7243d 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -1,4 +1,6 @@ -use crate::{ArrayInfo, ListInfo, MapInfo, Reflect, StructInfo, TupleInfo, TupleStructInfo}; +use crate::{ + ArrayInfo, EnumInfo, ListInfo, MapInfo, Reflect, StructInfo, TupleInfo, TupleStructInfo, +}; use std::any::{Any, TypeId}; /// A static accessor to compile-time type information. @@ -99,6 +101,7 @@ pub enum TypeInfo { List(ListInfo), Array(ArrayInfo), Map(MapInfo), + Enum(EnumInfo), Value(ValueInfo), /// Type information for "dynamic" types whose metadata can't be known at compile-time. /// @@ -116,6 +119,7 @@ impl TypeInfo { Self::List(info) => info.type_id(), Self::Array(info) => info.type_id(), Self::Map(info) => info.type_id(), + Self::Enum(info) => info.type_id(), Self::Value(info) => info.type_id(), Self::Dynamic(info) => info.type_id(), } @@ -132,6 +136,7 @@ impl TypeInfo { Self::List(info) => info.type_name(), Self::Array(info) => info.type_name(), Self::Map(info) => info.type_name(), + Self::Enum(info) => info.type_name(), Self::Value(info) => info.type_name(), Self::Dynamic(info) => info.type_name(), } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 11d27dee47024..f24e49e9943e5 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -19,6 +19,7 @@ use bevy_ecs::{ }; use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_reflect::prelude::*; +use bevy_reflect::FromReflect; use bevy_transform::components::GlobalTransform; use bevy_utils::HashSet; use bevy_window::{WindowCreated, WindowId, WindowResized, Windows}; @@ -309,7 +310,7 @@ impl RenderTarget { } } -#[derive(Debug, Clone, Copy, Default, Reflect, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Default, Reflect, FromReflect, Serialize, Deserialize)] #[reflect_value(Serialize, Deserialize)] pub enum DepthCalculation { /// Pythagorean distance; works everywhere, more expensive to compute. diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index aaf0e761bba37..a54f7fd5dea58 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -5,7 +5,8 @@ use bevy_app::{App, CoreStage, Plugin, StartupStage}; use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_math::Mat4; use bevy_reflect::{ - std_traits::ReflectDefault, GetTypeRegistration, Reflect, ReflectDeserialize, ReflectSerialize, + std_traits::ReflectDefault, FromReflect, GetTypeRegistration, Reflect, ReflectDeserialize, + ReflectSerialize, }; use bevy_window::ModifiesWindows; use serde::{Deserialize, Serialize}; @@ -101,7 +102,7 @@ impl Default for Projection { } } -#[derive(Component, Debug, Clone, Reflect)] +#[derive(Component, Debug, Clone, Reflect, FromReflect)] #[reflect(Component, Default)] pub struct PerspectiveProjection { pub fov: f32, @@ -140,14 +141,14 @@ impl Default for PerspectiveProjection { } // TODO: make this a component instead of a property -#[derive(Debug, Clone, Reflect, Serialize, Deserialize)] +#[derive(Debug, Clone, Reflect, FromReflect, Serialize, Deserialize)] #[reflect_value(Serialize, Deserialize)] pub enum WindowOrigin { Center, BottomLeft, } -#[derive(Debug, Clone, Reflect, Serialize, Deserialize)] +#[derive(Debug, Clone, Reflect, FromReflect, Serialize, Deserialize)] #[reflect_value(Serialize, Deserialize)] pub enum ScalingMode { /// Manually specify left/right/top/bottom values. @@ -166,7 +167,7 @@ pub enum ScalingMode { FixedHorizontal(f32), } -#[derive(Component, Debug, Clone, Reflect)] +#[derive(Component, Debug, Clone, Reflect, FromReflect)] #[reflect(Component, Default)] pub struct OrthographicProjection { pub left: f32, diff --git a/crates/bevy_window/Cargo.toml b/crates/bevy_window/Cargo.toml index 8a93fd404ddb6..4a27f70fca320 100644 --- a/crates/bevy_window/Cargo.toml +++ b/crates/bevy_window/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["bevy"] bevy_app = { path = "../bevy_app", version = "0.8.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.8.0" } bevy_math = { path = "../bevy_math", version = "0.8.0" } +bevy_reflect = { path = "../bevy_reflect", version = "0.8.0" } bevy_utils = { path = "../bevy_utils", version = "0.8.0" } # Used for close_on_esc bevy_input = { path = "../bevy_input", version = "0.8.0" } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 3972046781d29..a12ca415de64c 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -1,8 +1,10 @@ use bevy_math::{DVec2, IVec2, UVec2, Vec2}; +use bevy_reflect::{FromReflect, Reflect}; use bevy_utils::{tracing::warn, Uuid}; use raw_window_handle::RawWindowHandle; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Reflect, FromReflect)] +#[reflect_value(PartialEq, Hash)] /// A unique ID for a [`Window`]. pub struct WindowId(Uuid); diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index e5600066e35c2..db9dc4b61d995 100644 --- a/examples/reflection/reflection_types.rs +++ b/examples/reflection/reflection_types.rs @@ -24,32 +24,40 @@ pub struct A { z: HashMap, } -/// Deriving reflect on a unit struct will implement `Reflect` and `Struct` traits +/// Deriving reflect on a unit struct will implement the `Reflect` and `Struct` traits #[derive(Reflect)] pub struct B; -/// Deriving reflect on a tuple struct will implement `Reflect` and `TupleStruct` traits +/// Deriving reflect on a tuple struct will implement the `Reflect` and `TupleStruct` traits #[derive(Reflect)] pub struct C(usize); +/// Deriving reflect on an enum will implement the `Reflect` and `Enum` traits +#[derive(Reflect)] +pub enum D { + A, + B(usize), + C { value: f32 }, +} + /// Reflect has "built in" support for some common traits like `PartialEq`, `Hash`, and `Serialize`. -/// These are exposed via methods like `Reflect::hash()`, `Reflect::partial_eq()`, and -/// `Reflect::serialize()`. You can force these implementations to use the actual trait +/// These are exposed via methods like `Reflect::reflect_hash()`, `Reflect::reflect_partial_eq()`, and +/// `Reflect::serializable()`. You can force these implementations to use the actual trait /// implementations (instead of their defaults) like this: #[derive(Reflect, Hash, Serialize, PartialEq, Eq)] #[reflect(Hash, Serialize, PartialEq)] -pub struct D { +pub struct E { x: usize, } -/// By default, deriving with Reflect assumes the type is a "struct". You can tell reflect to treat -/// your type as a "value type" by using the `reflect_value` attribute instead of `reflect`. It is -/// generally a good idea to implement (and reflect) the `PartialEq`, `Serialize`, and `Deserialize` -/// traits on `reflect_value` types to ensure that these values behave as expected when nested -/// underneath Reflect-ed structs. +/// By default, deriving with Reflect assumes the type is either a "struct" or an "enum". +/// You can tell reflect to treat your type instead as a "value type" by using the `reflect_value` +/// attribute in place of `reflect`. It is generally a good idea to implement (and reflect) +/// the `PartialEq`, `Serialize`, and `Deserialize` traits on `reflect_value` types to ensure +/// that these values behave as expected when nested underneath Reflect-ed structs. #[derive(Reflect, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[reflect_value(PartialEq, Serialize, Deserialize)] -pub enum E { +pub enum F { X, Y, } @@ -82,6 +90,9 @@ fn setup() { // 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 with the current variant and its fields (if it has any) + 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 insertion. `List` is automatically // implemented for relevant core types like Vec.