From a518351f767ffdd1a28e8756f9dd7b20de07d00a Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Fri, 29 Jan 2021 23:34:29 +0900 Subject: [PATCH 01/75] Add the Enum trait to reflect enum types --- crates/bevy_reflect/src/enum_trait.rs | 53 +++++++++++++++++++++++++ crates/bevy_reflect/src/lib.rs | 2 + crates/bevy_reflect/src/reflect.rs | 5 ++- crates/bevy_reflect/src/serde/ser.rs | 3 ++ examples/reflection/reflection_types.rs | 3 ++ 5 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 crates/bevy_reflect/src/enum_trait.rs diff --git a/crates/bevy_reflect/src/enum_trait.rs b/crates/bevy_reflect/src/enum_trait.rs new file mode 100644 index 0000000000000..8dadd82dd0f05 --- /dev/null +++ b/crates/bevy_reflect/src/enum_trait.rs @@ -0,0 +1,53 @@ +use crate::{Reflect, Struct, Tuple}; + +pub trait Enum: Reflect { + fn variant(&self) -> EnumVariant<'_>; + fn variant_mut(&mut self) -> EnumVariantMut<'_>; + fn variant_info(&self) -> VariantInfo<'_>; + fn iter_variants_info(&self) -> VariantInfoIter<'_>; + fn get_index_name(&self, index: usize) -> Option<&str>; + fn get_index_from_name(&self, name: &str) -> Option; +} +pub struct VariantInfo<'a> { + pub index: usize, + pub name: &'a str, +} +pub struct VariantInfoIter<'a> { + pub(crate) value: &'a dyn Enum, + pub(crate) index: usize, + pub(crate) len: usize, +} +impl<'a> Iterator for VariantInfoIter<'a> { + type Item = VariantInfo<'a>; + + fn next(&mut self) -> Option { + if self.index == self.len { + return None; + } + let item = VariantInfo { + index: self.index, + name: self.value.get_index_name(self.index).unwrap(), + }; + self.index += 1; + Some(item) + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.len - self.index; + (size, Some(size)) + } +} +impl<'a> ExactSizeIterator for VariantInfoIter<'a> {} + +pub enum EnumVariant<'a> { + Unit, + NewType(&'a dyn Reflect), + Tuple(&'a dyn Tuple), + Struct(&'a dyn Struct), +} +pub enum EnumVariantMut<'a> { + Unit, + NewType(&'a mut dyn Reflect), + Tuple(&'a mut dyn Tuple), + Struct(&'a mut dyn Struct), +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 7fd60f0f928b3..4cfc9f7adacff 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -1,6 +1,7 @@ #![doc = include_str!("../README.md")] mod array; +mod enum_trait; mod fields; mod list; mod map; @@ -40,6 +41,7 @@ pub mod prelude { } pub use array::*; +pub use enum_trait::*; pub use fields::*; pub use impls::*; pub use list::*; diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index c1d41710e6f0d..0176422fc1c26 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, + 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), } diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index de0ad7760a6fa..73edf2f13e426 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -89,6 +89,9 @@ impl<'a> Serialize for ReflectSerializer<'a> { value, } .serialize(serializer), + ReflectRef::Enum(_value) => { + todo!() + } } } } diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index e5600066e35c2..12ec630738502 100644 --- a/examples/reflection/reflection_types.rs +++ b/examples/reflection/reflection_types.rs @@ -82,6 +82,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 list possible variants and interact with the currently active one + ReflectRef::Enum(_) => {} // `List` is a special trait that can be manually implemented (instead of deriving Reflect). // This exposes "list" operations on your type, such as insertion. `List` is automatically // implemented for relevant core types like Vec. From 2f48a0fea7daa78bb95d266024f12adf27e4c7d7 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Sat, 30 Jan 2021 10:04:52 +0900 Subject: [PATCH 02/75] Derive Reflect on enums --- .../bevy_reflect_derive/src/impls.rs | 492 --------------- .../bevy_reflect_derive/src/impls/enums.rs | 560 ++++++++++++++++++ .../bevy_reflect_derive/src/impls/mod.rs | 11 + .../bevy_reflect_derive/src/impls/structs.rs | 200 +++++++ .../src/impls/tuple_structs.rs | 160 +++++ .../bevy_reflect_derive/src/impls/typed.rs | 38 ++ .../bevy_reflect_derive/src/impls/values.rs | 109 ++++ .../bevy_reflect_derive/src/lib.rs | 46 +- crates/bevy_reflect/src/enum_trait.rs | 85 ++- 9 files changed, 1192 insertions(+), 509 deletions(-) delete mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs 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..733fadd791084 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -0,0 +1,560 @@ +use crate::container_attributes::ReflectTraits; +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::quote; +use syn::{Fields, Generics, Path, Variant}; + +pub(crate) fn impl_enum( + enum_name: &Ident, + generics: &Generics, + get_type_registration_impl: proc_macro2::TokenStream, + bevy_reflect_path: &Path, + traits: &ReflectTraits, + active_variants: &[(&Variant, usize)], +) -> TokenStream { + let mut variant_indices = Vec::new(); + let mut struct_wrappers = Vec::new(); + let mut tuple_wrappers = Vec::new(); + let mut variant_names = Vec::new(); + let mut variant_idents = Vec::new(); + let mut variant_and_fields_idents = Vec::new(); + let mut reflect_variants = Vec::new(); + let mut reflect_variants_mut = Vec::new(); + for (variant, variant_index) in active_variants.iter() { + let ident = &variant.ident; + let variant_name = format!("{}::{}", enum_name, variant.ident); + let variant_ident = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!(#enum_name::#ident {..}) + } + Fields::Unnamed(tuple) => { + let tuple_fields = &tuple.unnamed; + if tuple_fields.len() == 1 { + quote!(#enum_name::#ident (_)) + } else { + quote!(#enum_name::#ident (..)) + } + } + Fields::Unit => { + quote!(#enum_name::#ident) + } + } + }; + let variant_and_fields_ident = { + match &variant.fields { + Fields::Named(struct_fields) => { + let field_names = struct_fields + .named + .iter() + .map(|field| field.ident.as_ref().unwrap()) + .collect::>(); + quote!(#enum_name::#ident {#(#field_names,)*}) + } + Fields::Unnamed(tuple_fields) => { + let field_names = (0..tuple_fields.unnamed.len()) + .map(|i| Ident::new(format!("t{}", i).as_str(), Span::call_site())) + .collect::>(); + if tuple_fields.unnamed.len() == 1 { + quote!(#enum_name::#ident (new_type)) + } else { + quote!(#enum_name::#ident (#(#field_names,)*)) + } + } + Fields::Unit => { + quote!(#enum_name::#ident) + } + } + }; + let wrapper_ident = if let Fields::Named(_) | Fields::Unnamed(_) = &variant.fields { + Ident::new( + format!("{}{}Wrapper", enum_name, variant.ident).as_str(), + Span::call_site(), + ) + } else { + Ident::new("unused", Span::call_site()) + }; + let wrapper_name = match &variant.fields { + Fields::Named(struct_fields) => quote!(#struct_fields).to_string(), + Fields::Unnamed(tuple_fields) => quote!(#tuple_fields).to_string(), + Fields::Unit => "unused".to_string(), + }; + let reflect_variant = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariant::Struct(wrapper_ref as &dyn Struct) + }) + } + Fields::Unnamed(tuple_fields) => { + if tuple_fields.unnamed.len() == 1 { + quote!(#bevy_reflect_path::EnumVariant::NewType(new_type as &dyn #bevy_reflect_path::Reflect)) + } else { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariant::Tuple(wrapper_ref as &dyn Tuple) + }) + } + } + Fields::Unit => { + quote!(#bevy_reflect_path::EnumVariant::Unit) + } + } + }; + let reflect_variant_mut = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariantMut::Struct(wrapper_ref as &mut dyn Struct) + }) + } + Fields::Unnamed(tuple) => { + let tuple_fields = &tuple.unnamed; + if tuple_fields.len() == 1 { + quote!(#bevy_reflect_path::EnumVariantMut::NewType(new_type as &mut dyn #bevy_reflect_path::Reflect)) + } else { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariantMut::Tuple(wrapper_ref as &mut dyn Tuple) + }) + } + } + Fields::Unit => { + quote!(#bevy_reflect_path::EnumVariantMut::Unit) + } + } + }; + match &variant.fields { + Fields::Named(struct_fields) => { + struct_wrappers.push(( + wrapper_ident, + wrapper_name, + variant_index, + variant_name.clone(), + variant_ident.clone(), + variant_and_fields_ident.clone(), + struct_fields.clone(), + )); + } + Fields::Unnamed(tuple_fields) => { + if tuple_fields.unnamed.len() > 1 { + tuple_wrappers.push(( + wrapper_ident, + wrapper_name, + variant_index, + variant_name.clone(), + variant_ident.clone(), + variant_and_fields_ident.clone(), + tuple_fields.clone(), + )); + } + } + Fields::Unit => {} + } + variant_indices.push(variant_index); + variant_names.push(variant_name); + variant_idents.push(variant_ident); + variant_and_fields_idents.push(variant_and_fields_ident); + reflect_variants.push(reflect_variant); + reflect_variants_mut.push(reflect_variant_mut); + } + let hash_fn = traits.get_hash_impl(bevy_reflect_path); + let partial_eq_fn = 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 (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let mut token_stream = TokenStream::from(quote! { + #get_type_registration_impl + + impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_clause { + fn variant(&self) -> #bevy_reflect_path::EnumVariant<'_> { + match self { + #(#variant_and_fields_idents => #reflect_variants,)* + } + } + + fn variant_mut(&mut self) -> #bevy_reflect_path::EnumVariantMut<'_> { + match self { + #(#variant_and_fields_idents => #reflect_variants_mut,)* + } + } + + fn variant_info(&self) -> #bevy_reflect_path::VariantInfo<'_> { + let index = match self { + #(#variant_idents => #variant_indices,)* + }; + #bevy_reflect_path::VariantInfo { + index, + name: self.get_index_name(index).unwrap(), + } + } + + fn get_index_name(&self, index: usize) -> Option<&'_ str> { + match index { + #(#variant_indices => Some(#variant_names),)* + _ => None, + } + } + + fn get_index_from_name(&self, name: &str) -> Option { + match name { + #(#variant_names => Some(#variant_indices),)* + _ => None, + } + } + + fn iter_variants_info(&self) -> #bevy_reflect_path::VariantInfoIter<'_> { + #bevy_reflect_path::VariantInfoIter::new(self) + } + } + + impl #impl_generics #bevy_reflect_path::Reflect for #enum_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + #[inline] + fn clone_value(&self) -> Box { + use #bevy_reflect_path::Enum; + Box::new(self.clone()) // FIXME: should it be clone_dynamic? + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { // FIXME + use #bevy_reflect_path::Enum; + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + panic!("Attempted to apply non-enum type to enum type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Enum(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Enum(self) + } + + #hash_fn + + #partial_eq_fn + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + }); + for ( + wrapper_ident, + wrapper_name, + variant_index, + _variant_name, + _variant_ident, + variant_and_fields_ident, + fields, + ) in struct_wrappers + { + let mut field_names = Vec::new(); + let mut field_idents = Vec::new(); + let mut field_indices = Vec::new(); + for (i, field) in fields.named.iter().enumerate() { + field_names.push(field.ident.as_ref().unwrap().to_string()); + field_idents.push(field.ident.clone()); + field_indices.push(i); + } + let fields_len = field_indices.len(); + let mut match_fields = quote!(); + for (i, variant_ident) in variant_idents.iter().enumerate() { + if i == *variant_index { + match_fields.extend(quote!( + #variant_and_fields_ident => (#(#field_idents,)*), + )); + } else { + match_fields.extend(quote!( + #variant_ident => unreachable!(), + )); + } + } + let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { + #match_fields + };); + let match_fields = quote!(let (#(#field_idents,)*) = match &self.0 { + #match_fields + };); + token_stream.extend(TokenStream::from(quote! { + #[repr(transparent)] + pub struct #wrapper_ident(TestEnum); + impl #bevy_reflect_path::Reflect for #wrapper_ident { + fn type_name(&self) -> &str { + #wrapper_name + } + + fn any(&self) -> &dyn std::any::Any { + self.0.any() + } + + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self.0.any_mut() + } + + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + self.0.apply(value); + } + + fn set(&mut self, value: Box) -> Result<(), Box> { + self.0.set(value) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Struct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Struct(self) + } + + fn clone_value(&self) -> Box { + self.0.clone_value() + } + + fn reflect_hash(&self) -> Option { + self.0.reflect_hash() + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + self.0.reflect_partial_eq(value) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + self.0.serializable() + } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + impl #bevy_reflect_path::Struct for #wrapper_ident { + fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match name { + #(#field_names => Some(#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match name { + #(#field_names => Some(#field_idents),)* + _ => None, + } + } + + fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + fn name_at(&self, index: usize) -> Option<&str> { + match index { + #(#field_indices => Some(#field_names),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #fields_len + } + + fn iter_fields(&self) -> bevy::reflect::FieldIter { + FieldIter::new(self) + } + + fn clone_dynamic(&self) -> bevy::reflect::DynamicStruct { + #match_fields + let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(#field_names, #field_idents.clone_value());)* + dynamic + } + } + })); + } + for ( + wrapper_ident, + wrapper_name, + variant_index, + _variant_name, + _variant_ident, + variant_and_fields_ident, + fields, + ) in tuple_wrappers + { + let mut field_names = Vec::new(); + let mut field_idents = Vec::new(); + let mut field_indices = Vec::new(); + for (index, _field) in fields.unnamed.iter().enumerate() { + let field_name = format!("t{}", index); // FIXME: done in 2 places + let field_ident = Ident::new(field_name.as_str(), Span::call_site()); + field_names.push(field_name); + field_idents.push(field_ident); + field_indices.push(index); + } + let fields_len = field_indices.len(); + let mut match_fields = quote!(); + for (i, variant_ident) in variant_idents.iter().enumerate() { + if i == *variant_index { + match_fields.extend(quote!( + #variant_and_fields_ident => (#(#field_idents,)*), + )); + } else { + match_fields.extend(quote!( + #variant_ident => unreachable!(), + )); + } + } + let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { + #match_fields + };); + let match_fields = quote!(let (#(#field_idents,)*) = match &self.0 { + #match_fields + };); + token_stream.extend(TokenStream::from(quote! { + #[repr(transparent)] + pub struct #wrapper_ident(TestEnum); + impl #bevy_reflect_path::Reflect for #wrapper_ident { + fn type_name(&self) -> &str { + #wrapper_name + } + + fn into_any(self: Box) -> Box { + self + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + self.0.apply(value); + } + + fn set(&mut self, value: Box) -> Result<(), Box> { + self.0.set(value) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Tuple(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Tuple(self) + } + + fn clone_value(&self) -> Box { + self.0.clone_value() + } + + fn reflect_hash(&self) -> Option { + self.0.reflect_hash() + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + self.0.reflect_partial_eq(value) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + self.0.serializable() + } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + impl #bevy_reflect_path::Tuple for #wrapper_ident { + fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #fields_len + } + + fn iter_fields(&self) -> bevy::reflect::TupleFieldIter { + TupleFieldIter::new(self) + } + + fn clone_dynamic(&self) -> bevy::reflect::DynamicTuple { + #match_fields + let mut dynamic = #bevy_reflect_path::DynamicTuple::default(); + #(dynamic.insert_boxed(#field_idents.clone_value());)* + dynamic + } + } + })); + } + token_stream +} 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..60afc22dda506 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -0,0 +1,200 @@ +use crate::impls::impl_typed; +use crate::ReflectDeriveData; +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(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 debug_fn = derive_data.traits().get_debug_impl(); + 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 typed_impl = impl_typed( + struct_name, + derive_data.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 = 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 + } + }) +} 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..e0dd0d0207dbc --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -0,0 +1,160 @@ +use crate::impls::impl_typed; +use crate::ReflectDeriveData; +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(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 debug_fn = derive_data.traits().get_debug_impl(); + 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 typed_impl = impl_typed( + struct_name, + derive_data.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) = 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 + } + }) +} 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..28e831081083e --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -0,0 +1,109 @@ +use crate::container_attributes::ReflectTraits; +use crate::impls::impl_typed; +use proc_macro::TokenStream; +use proc_macro2::Ident; +use quote::quote; +use syn::{Generics, Path}; + +/// 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_attrs: &ReflectTraits, +) -> TokenStream { + let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); + let partial_eq_fn = reflect_attrs.get_partial_eq_impl(bevy_reflect_path); + let debug_fn = reflect_attrs.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 + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 7cf9fe560b004..c91df3833a1b6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -25,12 +25,13 @@ mod trait_reflection; mod type_uuid; mod utility; +use crate::container_attributes::ReflectTraits; use crate::derive_data::ReflectDeriveData; use derive_data::DeriveType; use proc_macro::TokenStream; use quote::quote; use reflect_value::ReflectValueDef; -use syn::{parse_macro_input, DeriveInput}; +use syn::{parse_macro_input, Data, DeriveInput, Meta}; pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; @@ -39,6 +40,49 @@ 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); + // TODO: Update and replace + if let Data::Enum(enum_data) = ast.data { + let mut traits = ReflectTraits::default(); + for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { + let meta_list = if let Meta::List(meta_list) = attribute { + meta_list + } else { + continue; + }; + + if let Some(ident) = meta_list.path.get_ident() { + if ident == REFLECT_ATTRIBUTE_NAME { + traits = ReflectTraits::from_nested_metas(&meta_list.nested); + } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { + traits = ReflectTraits::from_nested_metas(&meta_list.nested); + } + } + } + + let reflect_path = utility::get_bevy_reflect_path(); + + let variants = enum_data + .variants + .iter() + .enumerate() + .map(|(index, variant)| (variant, index)) + .collect::>(); + + return impls::impl_enum( + &ast.ident, + &ast.generics, + registration::impl_get_type_registration( + &ast.ident, + &reflect_path, + traits.idents(), + &ast.generics, + ), + &reflect_path, + &traits, + &variants, + ); + } + let derive_data = match ReflectDeriveData::from_input(&ast) { Ok(data) => data, Err(err) => return err.into_compile_error().into(), diff --git a/crates/bevy_reflect/src/enum_trait.rs b/crates/bevy_reflect/src/enum_trait.rs index 8dadd82dd0f05..500d7b00216a8 100644 --- a/crates/bevy_reflect/src/enum_trait.rs +++ b/crates/bevy_reflect/src/enum_trait.rs @@ -1,4 +1,4 @@ -use crate::{Reflect, Struct, Tuple}; +use crate::{Reflect, ReflectRef, Struct, Tuple}; pub trait Enum: Reflect { fn variant(&self) -> EnumVariant<'_>; @@ -8,6 +8,8 @@ pub trait Enum: Reflect { fn get_index_name(&self, index: usize) -> Option<&str>; fn get_index_from_name(&self, name: &str) -> Option; } + +#[derive(PartialEq, Eq)] pub struct VariantInfo<'a> { pub index: usize, pub name: &'a str, @@ -15,29 +17,29 @@ pub struct VariantInfo<'a> { pub struct VariantInfoIter<'a> { pub(crate) value: &'a dyn Enum, pub(crate) index: usize, - pub(crate) len: usize, } + +impl<'a> VariantInfoIter<'a> { + pub fn new(value: &'a dyn Enum) -> Self { + Self { value, index: 0 } + } +} + impl<'a> Iterator for VariantInfoIter<'a> { type Item = VariantInfo<'a>; fn next(&mut self) -> Option { - if self.index == self.len { - return None; - } - let item = VariantInfo { - index: self.index, - name: self.value.get_index_name(self.index).unwrap(), - }; + let value = self + .value + .get_index_name(self.index) + .map(|name| VariantInfo { + index: self.index, + name, + }); self.index += 1; - Some(item) - } - - fn size_hint(&self) -> (usize, Option) { - let size = self.len - self.index; - (size, Some(size)) + value } } -impl<'a> ExactSizeIterator for VariantInfoIter<'a> {} pub enum EnumVariant<'a> { Unit, @@ -51,3 +53,54 @@ pub enum EnumVariantMut<'a> { Tuple(&'a mut dyn Tuple), Struct(&'a mut dyn Struct), } + +#[inline] +pub fn enum_partial_eq(enum_a: &E, reflect_b: &dyn Reflect) -> Option { + let enum_b = if let ReflectRef::Enum(e) = reflect_b.reflect_ref() { + e + } else { + return Some(false); + }; + + if enum_a.variant_info() != enum_b.variant_info() { + return Some(false); + } + + let variant_b = enum_b.variant(); + match enum_a.variant() { + EnumVariant::Unit => { + if let EnumVariant::Unit = variant_b { + } else { + return Some(false); + } + } + EnumVariant::NewType(t_a) => { + if let EnumVariant::NewType(t_b) = variant_b { + if let Some(false) | None = t_b.reflect_partial_eq(t_a) { + return Some(false); + } + } else { + return Some(false); + } + } + EnumVariant::Tuple(t_a) => { + if let EnumVariant::Tuple(t_b) = variant_b { + if let Some(false) | None = t_b.reflect_partial_eq(t_a.as_reflect()) { + return Some(false); + } + } else { + return Some(false); + } + } + EnumVariant::Struct(s_a) => { + if let EnumVariant::Struct(s_b) = variant_b { + if let Some(false) | None = s_b.reflect_partial_eq(s_a.as_reflect()) { + return Some(false); + } + } else { + return Some(false); + } + } + } + Some(true) +} From a1adf3691cb5ed1438bb85fefb5ab86aee8b53e2 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Sat, 30 Jan 2021 10:06:55 +0900 Subject: [PATCH 03/75] Implement Reflect and Enum for Option --- crates/bevy_reflect/src/impls/std.rs | 147 +++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 7 deletions(-) diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 6ce789f975709..9b48c2d2b2712 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,8 +1,9 @@ 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, DynamicMap, Enum, EnumVariant, + EnumVariantMut, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, + MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TypeInfo, + TypeRegistration, Typed, ValueInfo, VariantInfo, VariantInfoIter, }; use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; @@ -32,7 +33,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()); @@ -557,6 +557,140 @@ impl Reflect for Cow<'static, str> { } } +impl GetTypeRegistration for Option { + fn get_type_registration() -> TypeRegistration { + TypeRegistration::of::>() + } +} + +impl Enum for Option { + fn variant(&self) -> EnumVariant<'_> { + match self { + Option::Some(new_type) => EnumVariant::NewType(new_type as &dyn Reflect), + Option::None => EnumVariant::Unit, + } + } + + fn variant_mut(&mut self) -> EnumVariantMut<'_> { + match self { + Option::Some(new_type) => EnumVariantMut::NewType(new_type as &mut dyn Reflect), + Option::None => EnumVariantMut::Unit, + } + } + + fn variant_info(&self) -> VariantInfo<'_> { + let index = match self { + Option::Some(_) => 0usize, + Option::None => 1usize, + }; + VariantInfo { + index, + name: self.get_index_name(index).unwrap(), + } + } + + fn iter_variants_info(&self) -> VariantInfoIter<'_> { + VariantInfoIter::new(self) + } + + fn get_index_name(&self, index: usize) -> Option<&'_ str> { + match index { + 0usize => Some("Option::Some"), + 1usize => Some("Option::None"), + _ => None, + } + } + + fn get_index_from_name(&self, name: &str) -> Option { + match name { + "Option::Some" => Some(0usize), + "Option::None" => Some(1usize), + _ => None, + } + } +} +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) { + let value = value.as_any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + { + panic!("Enum is not {}.", &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 { + None + } + + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + crate::enum_partial_eq(self, value) + } +} + +impl Typed for Option { + fn type_info() -> &'static TypeInfo { + // TODO: Replace with EnumInfo + static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); + CELL.get_or_insert::(|| TypeInfo::Value(ValueInfo::new::())) + } +} + impl Typed for Cow<'static, str> { fn type_info() -> &'static TypeInfo { static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); @@ -663,10 +797,9 @@ 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)); } } From 11b16eb5836b3c7a9e1a6b52f35339510866870f Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Mon, 1 Feb 2021 14:37:49 +0900 Subject: [PATCH 04/75] refactoring --- .../bevy_reflect_derive/src/impls/enums.rs | 103 ++++++++---------- 1 file changed, 47 insertions(+), 56 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 733fadd791084..ebe0c071aa437 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -4,6 +4,14 @@ use proc_macro2::{Ident, Span}; use quote::quote; use syn::{Fields, Generics, Path, Variant}; +fn tuple_field_name(i: usize) -> String { + format!("t{}", i) +} + +fn tuple_field_ident(i: usize) -> Ident { + Ident::new(tuple_field_name(i).as_str(), Span::call_site()) +} + pub(crate) fn impl_enum( enum_name: &Ident, generics: &Generics, @@ -17,52 +25,56 @@ pub(crate) fn impl_enum( let mut tuple_wrappers = Vec::new(); let mut variant_names = Vec::new(); let mut variant_idents = Vec::new(); - let mut variant_and_fields_idents = Vec::new(); let mut reflect_variants = Vec::new(); let mut reflect_variants_mut = Vec::new(); + let mut variant_with_fields_idents = Vec::new(); + let mut variant_without_fields_idents = Vec::new(); for (variant, variant_index) in active_variants.iter() { - let ident = &variant.ident; - let variant_name = format!("{}::{}", enum_name, variant.ident); let variant_ident = { + let ident = &variant.ident; + quote!(#enum_name::#ident) + }; + let variant_name = variant_ident.to_string(); + let variant_without_fields_ident = { match &variant.fields { Fields::Named(_struct_fields) => { - quote!(#enum_name::#ident {..}) + quote!(#variant_ident {..}) } Fields::Unnamed(tuple) => { let tuple_fields = &tuple.unnamed; if tuple_fields.len() == 1 { - quote!(#enum_name::#ident (_)) + quote!(#variant_ident (_)) } else { - quote!(#enum_name::#ident (..)) + quote!(#variant_ident (..)) } } Fields::Unit => { - quote!(#enum_name::#ident) + quote!(#variant_ident) } } }; - let variant_and_fields_ident = { + let variant_with_fields_ident = { match &variant.fields { Fields::Named(struct_fields) => { - let field_names = struct_fields + let field_idents = struct_fields .named .iter() .map(|field| field.ident.as_ref().unwrap()) .collect::>(); - quote!(#enum_name::#ident {#(#field_names,)*}) + quote!(#variant_ident {#(#field_idents,)*}) } Fields::Unnamed(tuple_fields) => { - let field_names = (0..tuple_fields.unnamed.len()) - .map(|i| Ident::new(format!("t{}", i).as_str(), Span::call_site())) + let field_idents = (0..tuple_fields.unnamed.len()) + .map(|i| tuple_field_ident(i)) .collect::>(); if tuple_fields.unnamed.len() == 1 { - quote!(#enum_name::#ident (new_type)) + quote!(#variant_ident (new_type)) } else { - quote!(#enum_name::#ident (#(#field_names,)*)) + quote!(#variant_ident (#(#field_idents,)*)) } } Fields::Unit => { - quote!(#enum_name::#ident) + quote!(#variant_ident) } } }; @@ -132,9 +144,7 @@ pub(crate) fn impl_enum( wrapper_ident, wrapper_name, variant_index, - variant_name.clone(), - variant_ident.clone(), - variant_and_fields_ident.clone(), + variant_with_fields_ident.clone(), struct_fields.clone(), )); } @@ -144,9 +154,7 @@ pub(crate) fn impl_enum( wrapper_ident, wrapper_name, variant_index, - variant_name.clone(), - variant_ident.clone(), - variant_and_fields_ident.clone(), + variant_with_fields_ident.clone(), tuple_fields.clone(), )); } @@ -156,9 +164,10 @@ pub(crate) fn impl_enum( variant_indices.push(variant_index); variant_names.push(variant_name); variant_idents.push(variant_ident); - variant_and_fields_idents.push(variant_and_fields_ident); reflect_variants.push(reflect_variant); reflect_variants_mut.push(reflect_variant_mut); + variant_with_fields_idents.push(variant_with_fields_ident); + variant_without_fields_idents.push(variant_without_fields_ident); } let hash_fn = traits.get_hash_impl(bevy_reflect_path); let partial_eq_fn = traits.get_partial_eq_impl(bevy_reflect_path).unwrap_or_else(|| { @@ -177,19 +186,19 @@ pub(crate) fn impl_enum( impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_clause { fn variant(&self) -> #bevy_reflect_path::EnumVariant<'_> { match self { - #(#variant_and_fields_idents => #reflect_variants,)* + #(#variant_with_fields_idents => #reflect_variants,)* } } fn variant_mut(&mut self) -> #bevy_reflect_path::EnumVariantMut<'_> { match self { - #(#variant_and_fields_idents => #reflect_variants_mut,)* + #(#variant_with_fields_idents => #reflect_variants_mut,)* } } fn variant_info(&self) -> #bevy_reflect_path::VariantInfo<'_> { let index = match self { - #(#variant_idents => #variant_indices,)* + #(#variant_without_fields_idents => #variant_indices,)* }; #bevy_reflect_path::VariantInfo { index, @@ -233,7 +242,7 @@ pub(crate) fn impl_enum( #[inline] fn clone_value(&self) -> Box { use #bevy_reflect_path::Enum; - Box::new(self.clone()) // FIXME: should it be clone_dynamic? + Box::new(self.clone()) // FIXME: should be clone_dynamic, so that Clone is not a required bound } #[inline] fn set(&mut self, value: Box) -> Result<(), Box> { @@ -242,11 +251,11 @@ pub(crate) fn impl_enum( } #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { // FIXME + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { use #bevy_reflect_path::Enum; let value = value.any(); if let Some(value) = value.downcast_ref::() { - *self = value.clone(); + *self = value.clone_value(); } else { panic!("Attempted to apply non-enum type to enum type."); } @@ -273,15 +282,7 @@ pub(crate) fn impl_enum( } } }); - for ( - wrapper_ident, - wrapper_name, - variant_index, - _variant_name, - _variant_ident, - variant_and_fields_ident, - fields, - ) in struct_wrappers + for (wrapper_ident, wrapper_name, variant_index, variant_with_fields_ident, fields) in struct_wrappers { let mut field_names = Vec::new(); let mut field_idents = Vec::new(); @@ -293,14 +294,14 @@ pub(crate) fn impl_enum( } let fields_len = field_indices.len(); let mut match_fields = quote!(); - for (i, variant_ident) in variant_idents.iter().enumerate() { + for (i, _variant_ident) in variant_idents.iter().enumerate() { if i == *variant_index { match_fields.extend(quote!( - #variant_and_fields_ident => (#(#field_idents,)*), + #variant_with_fields_ident => (#(#field_idents,)*), )); } else { match_fields.extend(quote!( - #variant_ident => unreachable!(), + #variant_with_fields_ident => unreachable!(), )); } } @@ -423,36 +424,26 @@ pub(crate) fn impl_enum( } })); } - for ( - wrapper_ident, - wrapper_name, - variant_index, - _variant_name, - _variant_ident, - variant_and_fields_ident, - fields, - ) in tuple_wrappers + for (wrapper_ident, wrapper_name, variant_index, variant_with_fields_ident, fields) in tuple_wrappers { let mut field_names = Vec::new(); let mut field_idents = Vec::new(); let mut field_indices = Vec::new(); for (index, _field) in fields.unnamed.iter().enumerate() { - let field_name = format!("t{}", index); // FIXME: done in 2 places - let field_ident = Ident::new(field_name.as_str(), Span::call_site()); - field_names.push(field_name); - field_idents.push(field_ident); + field_names.push(tuple_field_name(index)); + field_idents.push(tuple_field_ident(index)); field_indices.push(index); } let fields_len = field_indices.len(); let mut match_fields = quote!(); - for (i, variant_ident) in variant_idents.iter().enumerate() { + for (i, _variant_ident) in variant_idents.iter().enumerate() { if i == *variant_index { match_fields.extend(quote!( - #variant_and_fields_ident => (#(#field_idents,)*), + #variant_with_fields_ident => (#(#field_idents,)*), )); } else { match_fields.extend(quote!( - #variant_ident => unreachable!(), + #variant_with_fields_ident => unreachable!(), )); } } From 80c3436c15119313762cec01060596934f49c75a Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Tue, 23 Feb 2021 11:14:14 +0900 Subject: [PATCH 05/75] Fix wrappers using wrong ident --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index ebe0c071aa437..4353aa6a68c48 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -313,7 +313,7 @@ pub(crate) fn impl_enum( };); token_stream.extend(TokenStream::from(quote! { #[repr(transparent)] - pub struct #wrapper_ident(TestEnum); + pub struct #wrapper_ident(enum_name); impl #bevy_reflect_path::Reflect for #wrapper_ident { fn type_name(&self) -> &str { #wrapper_name @@ -455,7 +455,7 @@ pub(crate) fn impl_enum( };); token_stream.extend(TokenStream::from(quote! { #[repr(transparent)] - pub struct #wrapper_ident(TestEnum); + pub struct #wrapper_ident(enum_name); impl #bevy_reflect_path::Reflect for #wrapper_ident { fn type_name(&self) -> &str { #wrapper_name From c6bac642ee54652287097a5068dae313cd65c5e3 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 10:54:49 -0700 Subject: [PATCH 06/75] Fix bevy path --- .../bevy_reflect_derive/src/impls/enums.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 4353aa6a68c48..01d2841df4c86 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -96,7 +96,7 @@ pub(crate) fn impl_enum( Fields::Named(_struct_fields) => { quote!({ let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; - #bevy_reflect_path::EnumVariant::Struct(wrapper_ref as &dyn Struct) + #bevy_reflect_path::EnumVariant::Struct(wrapper_ref as &dyn #bevy_reflect_path::Struct) }) } Fields::Unnamed(tuple_fields) => { @@ -105,7 +105,7 @@ pub(crate) fn impl_enum( } else { quote!({ let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; - #bevy_reflect_path::EnumVariant::Tuple(wrapper_ref as &dyn Tuple) + #bevy_reflect_path::EnumVariant::Tuple(wrapper_ref as &dyn #bevy_reflect_path::Tuple) }) } } @@ -119,7 +119,7 @@ pub(crate) fn impl_enum( Fields::Named(_struct_fields) => { quote!({ let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; - #bevy_reflect_path::EnumVariantMut::Struct(wrapper_ref as &mut dyn Struct) + #bevy_reflect_path::EnumVariantMut::Struct(wrapper_ref as &mut dyn #bevy_reflect_path::Struct) }) } Fields::Unnamed(tuple) => { @@ -129,7 +129,7 @@ pub(crate) fn impl_enum( } else { quote!({ let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; - #bevy_reflect_path::EnumVariantMut::Tuple(wrapper_ref as &mut dyn Tuple) + #bevy_reflect_path::EnumVariantMut::Tuple(wrapper_ref as &mut dyn #bevy_reflect_path::Tuple) }) } } @@ -410,11 +410,11 @@ pub(crate) fn impl_enum( #fields_len } - fn iter_fields(&self) -> bevy::reflect::FieldIter { - FieldIter::new(self) + fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { + #bevy_reflect_path::FieldIter::new(self) } - fn clone_dynamic(&self) -> bevy::reflect::DynamicStruct { + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { #match_fields let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); dynamic.set_name(self.type_name().to_string()); @@ -534,11 +534,11 @@ pub(crate) fn impl_enum( #fields_len } - fn iter_fields(&self) -> bevy::reflect::TupleFieldIter { - TupleFieldIter::new(self) + fn iter_fields(&self) -> #bevy_reflect_path::TupleFieldIter { + #bevy_reflect_path::TupleFieldIter::new(self) } - fn clone_dynamic(&self) -> bevy::reflect::DynamicTuple { + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTuple { #match_fields let mut dynamic = #bevy_reflect_path::DynamicTuple::default(); #(dynamic.insert_boxed(#field_idents.clone_value());)* From e8c3ea9b3b2c97f797a9cb3dacfe0c9e436d4db1 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Tue, 23 Feb 2021 11:34:55 +0900 Subject: [PATCH 07/75] Use Clone in apply for now --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 01d2841df4c86..a1df94c8580b5 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -255,7 +255,7 @@ pub(crate) fn impl_enum( use #bevy_reflect_path::Enum; let value = value.any(); if let Some(value) = value.downcast_ref::() { - *self = value.clone_value(); + *self = value.clone(); //FIXME: should apply the variant instead } else { panic!("Attempted to apply non-enum type to enum type."); } From a740c989d6bb1e8ee39c8844126120d124950f8e Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Tue, 23 Feb 2021 11:47:39 +0900 Subject: [PATCH 08/75] clippy --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index a1df94c8580b5..24194ee8a2b76 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -65,7 +65,7 @@ pub(crate) fn impl_enum( } Fields::Unnamed(tuple_fields) => { let field_idents = (0..tuple_fields.unnamed.len()) - .map(|i| tuple_field_ident(i)) + .map(tuple_field_ident) .collect::>(); if tuple_fields.unnamed.len() == 1 { quote!(#variant_ident (new_type)) From 8e3d71713c26cf4c84771c963e63ca25c5d90d23 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Thu, 25 Feb 2021 12:46:52 +0900 Subject: [PATCH 09/75] Fix duplicated ident when using multiple struct or tuple variants --- .../bevy_reflect_derive/src/impls/enums.rs | 36 ++++++------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 24194ee8a2b76..7d8f3dd30232f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -282,7 +282,7 @@ pub(crate) fn impl_enum( } } }); - for (wrapper_ident, wrapper_name, variant_index, variant_with_fields_ident, fields) in struct_wrappers + for (wrapper_ident, wrapper_name, _variant_index, variant_with_fields_ident, fields) in struct_wrappers { let mut field_names = Vec::new(); let mut field_idents = Vec::new(); @@ -293,18 +293,10 @@ pub(crate) fn impl_enum( field_indices.push(i); } let fields_len = field_indices.len(); - let mut match_fields = quote!(); - for (i, _variant_ident) in variant_idents.iter().enumerate() { - if i == *variant_index { - match_fields.extend(quote!( - #variant_with_fields_ident => (#(#field_idents,)*), - )); - } else { - match_fields.extend(quote!( - #variant_with_fields_ident => unreachable!(), - )); - } - } + let match_fields = quote!( + #variant_with_fields_ident => (#(#field_idents,)*), + _ => unreachable!(), + ); let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { #match_fields };); @@ -424,7 +416,7 @@ pub(crate) fn impl_enum( } })); } - for (wrapper_ident, wrapper_name, variant_index, variant_with_fields_ident, fields) in tuple_wrappers + for (wrapper_ident, wrapper_name, _variant_index, variant_with_fields_ident, fields) in tuple_wrappers { let mut field_names = Vec::new(); let mut field_idents = Vec::new(); @@ -435,18 +427,10 @@ pub(crate) fn impl_enum( field_indices.push(index); } let fields_len = field_indices.len(); - let mut match_fields = quote!(); - for (i, _variant_ident) in variant_idents.iter().enumerate() { - if i == *variant_index { - match_fields.extend(quote!( - #variant_with_fields_ident => (#(#field_idents,)*), - )); - } else { - match_fields.extend(quote!( - #variant_with_fields_ident => unreachable!(), - )); - } - } + let match_fields = quote!( + #variant_with_fields_ident => (#(#field_idents,)*), + _ => unreachable!(), + ); let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { #match_fields };); From aaf41e50c4fc242653aa87e8b202fa800de0cc5c Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Thu, 25 Feb 2021 12:52:46 +0900 Subject: [PATCH 10/75] Remove whitespace from variant names --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 7d8f3dd30232f..1c6bda51c993f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -34,7 +34,11 @@ pub(crate) fn impl_enum( let ident = &variant.ident; quote!(#enum_name::#ident) }; - let variant_name = variant_ident.to_string(); + let variant_name = variant_ident + .to_string() + .chars() + .filter(|c| !c.is_whitespace()) + .collect::(); let variant_without_fields_ident = { match &variant.fields { Fields::Named(_struct_fields) => { From 6dd12d8dd5a3e1eb668cab4a3d1583d12cbc84d6 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 11:48:55 -0700 Subject: [PATCH 11/75] Start adding support for enums in ReflectDerive --- .../src/container_attributes.rs | 2 +- .../bevy_reflect_derive/src/derive_data.rs | 330 ++++++++++++------ .../bevy_reflect_derive/src/from_reflect.rs | 50 +-- .../bevy_reflect_derive/src/impls/structs.rs | 30 +- .../src/impls/tuple_structs.rs | 29 +- .../bevy_reflect_derive/src/impls/values.rs | 26 +- .../bevy_reflect_derive/src/lib.rs | 135 +++---- .../bevy_reflect_derive/src/reflect_value.rs | 12 + .../bevy_reflect_derive/src/utility.rs | 49 +++ crates/bevy_reflect/src/impls/std.rs | 6 +- 10 files changed, 399 insertions(+), 270 deletions(-) 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..9a6856df3a4d4 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,110 @@ 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, -} -/// 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, 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. + pub traits: ReflectTraits, + /// The name of this type. + pub type_name: &'a Ident, + /// The generics defined on this type. + pub generics: &'a Generics, + /// A cached instance of the path to the `bevy_reflect` crate. + pub 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. + 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,97 +117,113 @@ 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 { + type_name: &input.ident, + generics: &input.generics, + traits, + bevy_reflect_path: utility::get_bevy_reflect_path(), + }; + + 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); - } - - Ok(output) - } + .fold( + utility::ResultSifter::default(), + utility::ResultSifter::fold, + ); - /// Get an iterator over the active fields - pub fn active_fields(&self) -> impl Iterator> { - self.fields.iter().filter(|field| !field.attrs.ignore) + sifter.finish() } - /// Get an iterator over the ignored fields - pub fn ignored_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 attrs = parse_field_attrs(&variant.attrs)?; + let fields = Self::collect_struct_fields(&variant.fields)?; - /// Get a collection of all active types - pub fn active_types(&self) -> Vec { - self.active_fields() - .map(|field| field.data.ty.clone()) - .collect::>() - } + Ok(match variant.fields { + Fields::Named(..) => EnumVariant { + data: variant, + fields: EnumVariantFields::Named(fields), + attrs, + index, + }, + Fields::Unnamed(..) => EnumVariant { + data: variant, + fields: EnumVariantFields::Unnamed(fields), + attrs, + index, + }, + Fields::Unit => EnumVariant { + data: variant, + fields: EnumVariantFields::Unit, + attrs, + index, + }, + }) + }) + .fold( + utility::ResultSifter::default(), + utility::ResultSifter::fold, + ); - /// The [`DeriveType`] of this struct. - pub fn derive_type(&self) -> &DeriveType { - &self.derive_type + sifter.finish() } +} +impl<'a> ReflectMeta<'a> { /// The registered reflect traits on this struct. pub fn traits(&self) -> &ReflectTraits { &self.traits @@ -174,12 +239,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 +254,56 @@ 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) -> Vec { + self.active_fields() + .map(|field| field.data.ty.clone()) + .collect::>() + } + + /// 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. + pub fn ignored_variants(&self) -> impl Iterator> { + self.variants.iter().filter(|variant| variant.attrs.ignore) + } + + /// 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/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index b4c9c345f26eb..07f9d71e9fc82 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -1,27 +1,29 @@ use crate::container_attributes::REFLECT_DEFAULT; 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 { +pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { + let ReflectMeta { + type_name, + generics, + bevy_reflect_path, + .. + } = meta; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); TokenStream::from(quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { @@ -42,10 +44,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 +56,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 +73,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 +117,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 +140,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/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index 60afc22dda506..502e7c876d20b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -1,15 +1,15 @@ use crate::impls::impl_typed; -use crate::ReflectDeriveData; +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(derive_data: &ReflectDeriveData) -> TokenStream { - let bevy_reflect_path = derive_data.bevy_reflect_path(); - let struct_name = derive_data.type_name(); +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 = derive_data + let field_names = reflect_struct .active_fields() .map(|field| { field @@ -20,7 +20,7 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { .unwrap_or_else(|| field.index.to_string()) }) .collect::>(); - let field_idents = derive_data + let field_idents = reflect_struct .active_fields() .map(|field| { field @@ -31,16 +31,19 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) }) .collect::>(); - let field_types = derive_data + let field_types = reflect_struct .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 debug_fn = derive_data.traits().get_debug_impl(); - let partial_eq_fn = derive_data + 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(|| { @@ -53,7 +56,7 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { let typed_impl = impl_typed( struct_name, - derive_data.generics(), + reflect_struct.meta().generics(), quote! { let fields = [ #(#bevy_reflect_path::NamedField::new::<#field_types, _>(#field_names),)* @@ -64,8 +67,9 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { 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(); + 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 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 index e0dd0d0207dbc..0bf67081e1ac0 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -1,29 +1,33 @@ use crate::impls::impl_typed; -use crate::ReflectDeriveData; +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(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(); +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 = derive_data + let field_idents = reflect_struct .active_fields() .map(|field| Member::Unnamed(Index::from(field.index))) .collect::>(); - let field_types = derive_data + let field_types = reflect_struct .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 debug_fn = derive_data.traits().get_debug_impl(); - let partial_eq_fn = derive_data + 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(|| { @@ -36,7 +40,7 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream let typed_impl = impl_typed( struct_name, - derive_data.generics(), + reflect_struct.meta().generics(), quote! { let fields = [ #(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_idents),)* @@ -47,7 +51,8 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream bevy_reflect_path, ); - let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); + let (impl_generics, ty_generics, where_clause) = + reflect_struct.meta().generics().split_for_impl(); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 28e831081083e..037a3a39a0863 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -1,21 +1,20 @@ -use crate::container_attributes::ReflectTraits; use crate::impls::impl_typed; +use crate::ReflectMeta; use proc_macro::TokenStream; -use proc_macro2::Ident; use quote::quote; -use syn::{Generics, Path}; /// 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_attrs: &ReflectTraits, -) -> TokenStream { - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = reflect_attrs.get_partial_eq_impl(bevy_reflect_path); - let debug_fn = reflect_attrs.get_debug_impl(); +pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { + let ReflectMeta { + traits, + type_name, + generics, + bevy_reflect_path, + } = meta; + + let hash_fn = traits.get_hash_impl(bevy_reflect_path); + let partial_eq_fn = traits.get_partial_eq_impl(bevy_reflect_path); + let debug_fn = traits.get_debug_impl(); let typed_impl = impl_typed( type_name, @@ -28,6 +27,7 @@ pub(crate) fn impl_value( ); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let get_type_registration_impl = meta.get_type_registration(); TokenStream::from(quote! { #get_type_registration_impl diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index c91df3833a1b6..e2ce95419b171 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -25,13 +25,12 @@ mod trait_reflection; mod type_uuid; mod utility; -use crate::container_attributes::ReflectTraits; -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::{parse_macro_input, Data, DeriveInput, Meta}; +use syn::spanned::Spanned; +use syn::{parse_macro_input, DeriveInput}; pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; @@ -40,64 +39,17 @@ 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); - // TODO: Update and replace - if let Data::Enum(enum_data) = ast.data { - let mut traits = ReflectTraits::default(); - for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - let meta_list = if let Meta::List(meta_list) = attribute { - meta_list - } else { - continue; - }; - - if let Some(ident) = meta_list.path.get_ident() { - if ident == REFLECT_ATTRIBUTE_NAME { - traits = ReflectTraits::from_nested_metas(&meta_list.nested); - } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { - traits = ReflectTraits::from_nested_metas(&meta_list.nested); - } - } - } - - let reflect_path = utility::get_bevy_reflect_path(); - - let variants = enum_data - .variants - .iter() - .enumerate() - .map(|(index, variant)| (variant, index)) - .collect::>(); - - return impls::impl_enum( - &ast.ident, - &ast.generics, - registration::impl_get_type_registration( - &ast.ident, - &reflect_path, - traits.idents(), - &ast.generics, - ), - &reflect_path, - &traits, - &variants, - ); - } - - 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) => impls::impl_struct(&struct_data), + ReflectDerive::UnitStruct(struct_data) => impls::impl_struct(&struct_data), + ReflectDerive::TupleStruct(struct_data) => impls::impl_tuple_struct(&struct_data), + ReflectDerive::Value(meta) => impls::impl_value(&meta), + _ => todo!(), } } @@ -112,19 +64,17 @@ 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) => from_reflect::impl_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::Value(meta) => from_reflect::impl_value(&meta), + _ => todo!(), } } @@ -142,24 +92,8 @@ 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 meta = reflect_value_def.as_meta(); + impls::impl_value(&meta) } /// A replacement for `#[derive(Reflect)]` to be used with foreign types which @@ -193,26 +127,35 @@ 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(); + 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 + TokenStream::from(quote! { + #impl_struct - #impl_from_struct - }) + #impl_from_struct + }) + } + _ => syn::Error::new( + ast.span(), + "impl_reflect_struct is only supported for standard 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 meta = reflect_value_def.as_meta(); + from_reflect::impl_value(&meta) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index ec54b99a6f404..e9d32bf5d2273 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -1,4 +1,5 @@ use crate::container_attributes::ReflectTraits; +use crate::{utility, ReflectMeta}; use proc_macro2::Ident; use syn::parse::{Parse, ParseStream}; use syn::token::{Paren, Where}; @@ -24,6 +25,17 @@ pub(crate) struct ReflectValueDef { pub traits: Option, } +impl ReflectValueDef { + pub fn as_meta(&self) -> ReflectMeta { + ReflectMeta { + traits: self.traits.clone().unwrap_or_default(), + type_name: &self.type_name, + generics: &self.generics, + bevy_reflect_path: utility::get_bevy_reflect_path(), + } + } +} + impl Parse for ReflectValueDef { fn parse(input: ParseStream) -> syn::Result { let type_ident = input.parse::()?; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 34fbdf186cc94..0424a9b60b182 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -21,3 +21,52 @@ 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, +} + +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`]. + /// + /// [`Iterator::fold`]: core::iter::traits::iterator::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/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 9b48c2d2b2712..7a0ec4950b0b7 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -685,9 +685,11 @@ impl Reflect for Option { impl Typed for Option { fn type_info() -> &'static TypeInfo { - // TODO: Replace with EnumInfo static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); - CELL.get_or_insert::(|| TypeInfo::Value(ValueInfo::new::())) + CELL.get_or_insert::(|| { + // TODO: Replace with EnumInfo + TypeInfo::Value(ValueInfo::new::()) + }) } } From 637f961345ae71d5a9d04b77a4cf141dd254bbd0 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 11:50:06 -0700 Subject: [PATCH 12/75] Formatting --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 6 ++++-- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 1c6bda51c993f..0fc61e4e800f4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -286,7 +286,8 @@ pub(crate) fn impl_enum( } } }); - for (wrapper_ident, wrapper_name, _variant_index, variant_with_fields_ident, fields) in struct_wrappers + for (wrapper_ident, wrapper_name, _variant_index, variant_with_fields_ident, fields) in + struct_wrappers { let mut field_names = Vec::new(); let mut field_idents = Vec::new(); @@ -420,7 +421,8 @@ pub(crate) fn impl_enum( } })); } - for (wrapper_ident, wrapper_name, _variant_index, variant_with_fields_ident, fields) in tuple_wrappers + for (wrapper_ident, wrapper_name, _variant_index, variant_with_fields_ident, fields) in + tuple_wrappers { let mut field_names = Vec::new(); let mut field_idents = Vec::new(); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index e2ce95419b171..e72b2c0e823a3 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -148,8 +148,8 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { ast.span(), "impl_reflect_struct is only supported for standard structs", ) - .into_compile_error() - .into(), + .into_compile_error() + .into(), } } From df715b173c6208312d890e342790f72287bef534 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 11:52:52 -0700 Subject: [PATCH 13/75] Move enum_trait file to enums mod directory --- crates/bevy_reflect/src/{ => enums}/enum_trait.rs | 0 crates/bevy_reflect/src/enums/mod.rs | 3 +++ crates/bevy_reflect/src/lib.rs | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) rename crates/bevy_reflect/src/{ => enums}/enum_trait.rs (100%) create mode 100644 crates/bevy_reflect/src/enums/mod.rs diff --git a/crates/bevy_reflect/src/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs similarity index 100% rename from crates/bevy_reflect/src/enum_trait.rs rename to crates/bevy_reflect/src/enums/enum_trait.rs diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs new file mode 100644 index 0000000000000..1283a3e6bdfc2 --- /dev/null +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -0,0 +1,3 @@ +mod enum_trait; + +pub use enum_trait::*; diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 4cfc9f7adacff..77ea03ba60616 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -1,7 +1,6 @@ #![doc = include_str!("../README.md")] mod array; -mod enum_trait; mod fields; mod list; mod map; @@ -27,6 +26,7 @@ mod impls { pub use self::std::*; } +mod enums; pub mod serde; pub mod std_traits; pub mod utility; @@ -41,7 +41,7 @@ pub mod prelude { } pub use array::*; -pub use enum_trait::*; +pub use enums::*; pub use fields::*; pub use impls::*; pub use list::*; From f80e06883f042ff09786f31132280d219565342f Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 12:12:47 -0700 Subject: [PATCH 14/75] Added EnumInfo --- crates/bevy_reflect/src/enums/enum_trait.rs | 198 ++++++++++++-------- crates/bevy_reflect/src/enums/mod.rs | 2 + crates/bevy_reflect/src/enums/variants.rs | 129 +++++++++++++ crates/bevy_reflect/src/impls/std.rs | 33 +--- crates/bevy_reflect/src/lib.rs | 45 +++++ crates/bevy_reflect/src/type_info.rs | 7 +- 6 files changed, 306 insertions(+), 108 deletions(-) create mode 100644 crates/bevy_reflect/src/enums/variants.rs diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 500d7b00216a8..881898294dcc2 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,43 +1,90 @@ -use crate::{Reflect, ReflectRef, Struct, Tuple}; +use crate::{Reflect, ReflectRef, Struct, Tuple, VariantInfo}; +use bevy_utils::HashMap; +use std::any::{Any, TypeId}; +use std::borrow::Cow; +use std::slice::Iter; pub trait Enum: Reflect { fn variant(&self) -> EnumVariant<'_>; fn variant_mut(&mut self) -> EnumVariantMut<'_>; - fn variant_info(&self) -> VariantInfo<'_>; - fn iter_variants_info(&self) -> VariantInfoIter<'_>; - fn get_index_name(&self, index: usize) -> Option<&str>; - fn get_index_from_name(&self, name: &str) -> Option; } -#[derive(PartialEq, Eq)] -pub struct VariantInfo<'a> { - pub index: usize, - pub name: &'a str, -} -pub struct VariantInfoIter<'a> { - pub(crate) value: &'a dyn Enum, - pub(crate) index: usize, +/// A container for compile-time enum info. +#[derive(Clone, Debug)] +pub struct EnumInfo { + type_name: &'static str, + type_id: TypeId, + variants: Box<[VariantInfo]>, + variant_indices: HashMap, usize>, } -impl<'a> VariantInfoIter<'a> { - pub fn new(value: &'a dyn Enum) -> Self { - Self { value, index: 0 } +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, + } } -} -impl<'a> Iterator for VariantInfoIter<'a> { - type Item = VariantInfo<'a>; + /// 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() + } + + /// 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() + } - fn next(&mut self) -> Option { - let value = self - .value - .get_index_name(self.index) - .map(|name| VariantInfo { - index: self.index, - name, - }); - self.index += 1; - value + /// 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 } } @@ -56,51 +103,52 @@ pub enum EnumVariantMut<'a> { #[inline] pub fn enum_partial_eq(enum_a: &E, reflect_b: &dyn Reflect) -> Option { - let enum_b = if let ReflectRef::Enum(e) = reflect_b.reflect_ref() { - e - } else { - return Some(false); - }; - - if enum_a.variant_info() != enum_b.variant_info() { - return Some(false); - } - - let variant_b = enum_b.variant(); - match enum_a.variant() { - EnumVariant::Unit => { - if let EnumVariant::Unit = variant_b { - } else { - return Some(false); - } - } - EnumVariant::NewType(t_a) => { - if let EnumVariant::NewType(t_b) = variant_b { - if let Some(false) | None = t_b.reflect_partial_eq(t_a) { - return Some(false); - } - } else { - return Some(false); - } - } - EnumVariant::Tuple(t_a) => { - if let EnumVariant::Tuple(t_b) = variant_b { - if let Some(false) | None = t_b.reflect_partial_eq(t_a.as_reflect()) { - return Some(false); - } - } else { - return Some(false); - } - } - EnumVariant::Struct(s_a) => { - if let EnumVariant::Struct(s_b) = variant_b { - if let Some(false) | None = s_b.reflect_partial_eq(s_a.as_reflect()) { - return Some(false); - } - } else { - return Some(false); - } - } - } + // TODO: Uncomment and update once we figure out how we want to represent variants + // let enum_b = if let ReflectRef::Enum(e) = reflect_b.reflect_ref() { + // e + // } else { + // return Some(false); + // }; + // + // if enum_a.variant_info() != enum_b.variant_info() { + // return Some(false); + // } + // + // let variant_b = enum_b.variant(); + // match enum_a.variant() { + // EnumVariant::Unit => { + // if let EnumVariant::Unit = variant_b { + // } else { + // return Some(false); + // } + // } + // EnumVariant::NewType(t_a) => { + // if let EnumVariant::NewType(t_b) = variant_b { + // if let Some(false) | None = t_b.reflect_partial_eq(t_a) { + // return Some(false); + // } + // } else { + // return Some(false); + // } + // } + // EnumVariant::Tuple(t_a) => { + // if let EnumVariant::Tuple(t_b) = variant_b { + // if let Some(false) | None = t_b.reflect_partial_eq(t_a.as_reflect()) { + // return Some(false); + // } + // } else { + // return Some(false); + // } + // } + // EnumVariant::Struct(s_a) => { + // if let EnumVariant::Struct(s_b) = variant_b { + // if let Some(false) | None = s_b.reflect_partial_eq(s_a.as_reflect()) { + // return Some(false); + // } + // } else { + // return Some(false); + // } + // } + // } Some(true) } diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 1283a3e6bdfc2..d48db8ba56aaa 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -1,3 +1,5 @@ mod enum_trait; +mod variants; pub use enum_trait::*; +pub use variants::*; diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs new file mode 100644 index 0000000000000..b5060454df344 --- /dev/null +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -0,0 +1,129 @@ +use crate::{NamedField, UnnamedField}; +use bevy_utils::HashMap; +use std::borrow::Cow; +use std::slice::Iter; + +/// 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 { + /// 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() + } +} + +/// Type info for tuple variants. +#[derive(Clone, Debug)] +pub struct TupleVariantInfo { + name: Cow<'static, str>, + fields: Box<[UnnamedField]>, +} + +impl TupleVariantInfo { + /// 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 { + /// 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 7a0ec4950b0b7..29475a2782d61 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -3,7 +3,7 @@ use crate::{ map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, Enum, EnumVariant, EnumVariantMut, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TypeInfo, - TypeRegistration, Typed, ValueInfo, VariantInfo, VariantInfoIter, + TypeRegistration, Typed, ValueInfo, }; use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; @@ -577,37 +577,6 @@ impl Enum for Option { Option::None => EnumVariantMut::Unit, } } - - fn variant_info(&self) -> VariantInfo<'_> { - let index = match self { - Option::Some(_) => 0usize, - Option::None => 1usize, - }; - VariantInfo { - index, - name: self.get_index_name(index).unwrap(), - } - } - - fn iter_variants_info(&self) -> VariantInfoIter<'_> { - VariantInfoIter::new(self) - } - - fn get_index_name(&self, index: usize) -> Option<&'_ str> { - match index { - 0usize => Some("Option::Some"), - 1usize => Some("Option::None"), - _ => None, - } - } - - fn get_index_from_name(&self, name: &str) -> Option { - match name { - "Option::Some" => Some(0usize), - "Option::None" => Some(1usize), - _ => None, - } - } } impl Reflect for Option { #[inline] diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 77ea03ba60616..f8b8c4266bf9d 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -681,6 +681,51 @@ mod tests { let info = value.get_type_info(); assert!(info.is::()); + // TODO: Uncomment this section when deriving Reflect for enum is re-enabled + // Enum + // #[derive(Reflect)] + // enum MyEnum { + // A, + // B(usize, isize), + // C { foo: String }, + // } + // + // let info = MyEnum::type_info(); + // if let TypeInfo::Enum(info) = info { + // assert!(info.id().is::()); + // assert_eq!(std::any::type_name::(), info.id().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().id().is::()); + // assert!(variant.field_at(1).unwrap().id().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().id().is::()); + // assert!(variant.field("foo").unwrap().id().is::()); + // } else { + // panic!("Expected `VariantInfo::Struct`"); + // } + // } else { + // panic!("Expected `TypeInfo::Enum`"); + // } + // Tuple type MyTuple = (u32, f32, String); 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(), } From 6549c1c376ef8ec8590ed6c9aeb538e2cebbd8ae Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 15:10:53 -0700 Subject: [PATCH 15/75] Added VariantRef and VariantMut --- crates/bevy_reflect/src/enums/enum_trait.rs | 23 +- crates/bevy_reflect/src/enums/mod.rs | 2 + crates/bevy_reflect/src/enums/variant_ref.rs | 235 +++++++++++++++++++ crates/bevy_reflect/src/impls/std.rs | 34 ++- crates/bevy_reflect/src/lib.rs | 25 ++ 5 files changed, 293 insertions(+), 26 deletions(-) create mode 100644 crates/bevy_reflect/src/enums/variant_ref.rs diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 881898294dcc2..e3a5ca9c0fc44 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,12 +1,16 @@ -use crate::{Reflect, ReflectRef, Struct, Tuple, VariantInfo}; +use crate::{Reflect, ReflectRef, Struct, Tuple, VariantInfo, VariantMut, VariantRef}; use bevy_utils::HashMap; use std::any::{Any, TypeId}; use std::borrow::Cow; use std::slice::Iter; pub trait Enum: Reflect { - fn variant(&self) -> EnumVariant<'_>; - fn variant_mut(&mut self) -> EnumVariantMut<'_>; + /// Returns an immutable reference to the current variant. + fn variant(&self) -> VariantRef; + /// Returns a mutable reference to the current variant. + fn variant_mut(&mut self) -> VariantMut; + /// The name of the current variant. + fn variant_name(&self) -> &str; } /// A container for compile-time enum info. @@ -88,19 +92,6 @@ impl EnumInfo { } } -pub enum EnumVariant<'a> { - Unit, - NewType(&'a dyn Reflect), - Tuple(&'a dyn Tuple), - Struct(&'a dyn Struct), -} -pub enum EnumVariantMut<'a> { - Unit, - NewType(&'a mut dyn Reflect), - Tuple(&'a mut dyn Tuple), - Struct(&'a mut dyn Struct), -} - #[inline] pub fn enum_partial_eq(enum_a: &E, reflect_b: &dyn Reflect) -> Option { // TODO: Uncomment and update once we figure out how we want to represent variants diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index d48db8ba56aaa..86e8bbdc60261 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -1,5 +1,7 @@ mod enum_trait; +mod variant_ref; mod variants; pub use enum_trait::*; +pub use variant_ref::*; pub use variants::*; diff --git a/crates/bevy_reflect/src/enums/variant_ref.rs b/crates/bevy_reflect/src/enums/variant_ref.rs new file mode 100644 index 0000000000000..c300ae8bd6faf --- /dev/null +++ b/crates/bevy_reflect/src/enums/variant_ref.rs @@ -0,0 +1,235 @@ +use crate::{Reflect, Struct, Tuple, TupleStructFieldIter}; +use bevy_utils::HashMap; +use std::borrow::Cow; +use std::ops::{Deref, DerefMut}; +use std::slice::Iter; + +pub enum VariantRef<'a> { + Unit, + Tuple(TupleVariantRef<'a>), + Struct(StructVariantRef<'a>), +} + +pub enum VariantMut<'a> { + Unit, + Tuple(TupleVariantMut<'a>), + Struct(StructVariantMut<'a>), +} + +pub struct TupleVariantRef<'a> { + fields: Vec>, +} + +impl<'a> TupleVariantRef<'a> { + /// Creates a new [`TupleVariantRef`]. + pub fn new(fields: Vec>) -> Self { + Self { fields } + } + + /// Returns a reference to the value of the field at the given index. + pub fn field(&self, index: usize) -> Option<&dyn Reflect> { + self.fields.get(index).map(AsRef::as_ref) + } + + /// Returns an iterator over the values of the variant's fields. + pub fn iter_fields(&self) -> Iter<'_, VariantFieldRef<'a>> { + self.fields.iter() + } + + /// Returns the number of fields in the variant. + pub fn field_len(&self) -> usize { + self.fields.len() + } +} + +pub struct TupleVariantMut<'a> { + fields: Vec>, +} + +impl<'a> TupleVariantMut<'a> { + /// Creates a new [`TupleVariantMut`]. + pub fn new(fields: Vec>) -> Self { + Self { fields } + } + + /// Returns a reference to the value of the field at the given index. + pub fn field(&self, index: usize) -> Option<&dyn Reflect> { + self.fields.get(index).map(AsRef::as_ref) + } + + /// Returns a mutable reference to the value of the field at the given index. + pub fn field_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> { + self.fields.get_mut(index).map(AsMut::as_mut) + } + + /// Returns an iterator over the values of the variant's fields. + pub fn iter_fields(&self) -> Iter<'_, VariantFieldMut<'a>> { + self.fields.iter() + } + + /// Returns the number of fields in the variant. + pub fn field_len(&self) -> usize { + self.fields.len() + } +} + +pub struct StructVariantRef<'a> { + fields: Vec>, + field_indices: HashMap, usize>, +} + +impl<'a> StructVariantRef<'a> { + /// Creates a new [`StructVariantRef`]. + pub fn new( + fields: Vec>, + field_indices: HashMap, usize>, + ) -> Self { + Self { + fields, + field_indices, + } + } + + /// Returns a reference to the value of the field with the given name. + pub fn field(&self, name: &str) -> Option<&dyn Reflect> { + self.field_indices + .get(name) + .map(|index| self.fields[*index].as_ref()) + } + + /// Returns a reference to the value of the field at the given index. + pub fn field_at(&self, index: usize) -> Option<&dyn Reflect> { + self.fields.get(index).map(AsRef::as_ref) + } + + /// Returns the index of the field with the given name. + pub fn index_of(&self, name: &str) -> Option { + self.field_indices.get(name).copied() + } + + /// Returns an iterator over the values of the variant's fields. + pub fn iter_fields(&self) -> Iter<'_, VariantFieldRef<'a>> { + self.fields.iter() + } + + /// Returns the number of fields in the variant. + pub fn field_len(&self) -> usize { + self.fields.len() + } +} + +pub struct StructVariantMut<'a> { + fields: Vec>, + field_indices: HashMap, usize>, +} + +impl<'a> StructVariantMut<'a> { + /// Creates a new [`StructVariantMut`]. + pub fn new( + fields: Vec>, + field_indices: HashMap, usize>, + ) -> Self { + Self { + fields, + field_indices, + } + } + + /// Returns a reference to the value of the field with the given name. + pub fn field(&self, name: &str) -> Option<&dyn Reflect> { + self.field_indices + .get(name) + .map(|index| self.fields[*index].as_ref()) + } + + /// Returns a reference to the value of the field at the given index. + pub fn field_at(&self, index: usize) -> Option<&dyn Reflect> { + self.fields.get(index).map(AsRef::as_ref) + } + + /// Returns a mutable reference to the value of the field with the given name. + pub fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> { + self.field_indices + .get_mut(name) + .map(|index| self.fields[*index].as_mut()) + } + + /// Returns a mutable reference to the value of the field at the given index. + pub fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> { + self.fields.get_mut(index).map(AsMut::as_mut) + } + + /// Returns the index of the field with the given name. + pub fn index_of(&self, name: &str) -> Option { + self.field_indices.get(name).copied() + } + + /// Returns an iterator over the values of the variant's fields. + pub fn iter_fields(&self) -> Iter<'_, VariantFieldMut<'a>> { + self.fields.iter() + } + + /// Returns the number of fields in the variant. + pub fn field_len(&self) -> usize { + self.fields.len() + } +} + +/// A wrapper around an immutable reference to a variant's field. +#[derive(Copy, Clone)] +pub struct VariantFieldRef<'a>(&'a dyn Reflect); + +impl<'a> VariantFieldRef<'a> { + pub fn new(field: &'a dyn Reflect) -> Self { + Self(field) + } +} + +impl<'a> AsRef for VariantFieldRef<'a> { + fn as_ref(&self) -> &dyn Reflect { + self.0 + } +} + +impl<'a> Deref for VariantFieldRef<'a> { + type Target = dyn Reflect; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +/// A wrapper around a mutable reference to a variant's field. +pub struct VariantFieldMut<'a>(&'a mut dyn Reflect); + +impl<'a> VariantFieldMut<'a> { + pub fn new(field: &'a mut dyn Reflect) -> Self { + Self(field) + } +} + +impl<'a> AsRef for VariantFieldMut<'a> { + fn as_ref(&self) -> &dyn Reflect { + self.0 + } +} + +impl<'a> AsMut for VariantFieldMut<'a> { + fn as_mut(&mut self) -> &mut dyn Reflect { + self.0 + } +} + +impl<'a> Deref for VariantFieldMut<'a> { + type Target = dyn Reflect; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a> DerefMut for VariantFieldMut<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 29475a2782d61..7cf5252f3a1b0 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,9 +1,10 @@ use crate::{self as bevy_reflect, ReflectFromPtr}; use crate::{ - map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, Enum, EnumVariant, - EnumVariantMut, 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, DynamicMap, Enum, FromReflect, + FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, + ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, StructVariantRef, + TupleVariantMut, TupleVariantRef, TypeInfo, TypeRegistration, Typed, ValueInfo, + VariantFieldMut, VariantFieldRef, VariantMut, VariantRef, }; use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; @@ -564,17 +565,30 @@ impl GetTypeRegistration for Option< } impl Enum for Option { - fn variant(&self) -> EnumVariant<'_> { + fn variant(&self) -> VariantRef { match self { - Option::Some(new_type) => EnumVariant::NewType(new_type as &dyn Reflect), - Option::None => EnumVariant::Unit, + Some(value) => { + let field = VariantFieldRef::new(value); + VariantRef::Tuple(TupleVariantRef::new(vec![field])) + } + None => VariantRef::Unit, + } + } + + fn variant_mut(&mut self) -> VariantMut { + match self { + Some(value) => { + let field = VariantFieldMut::new(value); + VariantMut::Tuple(TupleVariantMut::new(vec![field])) + } + None => VariantMut::Unit, } } - fn variant_mut(&mut self) -> EnumVariantMut<'_> { + fn variant_name(&self) -> &str { match self { - Option::Some(new_type) => EnumVariantMut::NewType(new_type as &mut dyn Reflect), - Option::None => EnumVariantMut::Unit, + Some(..) => "Some", + None => "None", } } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index f8b8c4266bf9d..a94290f199206 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -208,6 +208,31 @@ mod tests { assert_eq!(4, *iter.next().unwrap().downcast_ref::().unwrap()); } + #[test] + fn reflect_enum() { + // TODO: Uncomment when derive Reflect is re-enabled for enums + // #[derive(Reflect)] + // enum Foo { + // A, + // B(usize), + // C { value: f32 }, + // } + + // Option (Tuple) + let mut value = Some(123usize); + let reflected_value = &mut value; + assert_eq!("Some", reflected_value.variant_name()); + + if let VariantMut::Tuple(mut variant) = reflected_value.variant_mut() { + variant + .field_mut(0) + .and_then(|field| field.downcast_mut::()) + .map(|field| *field = 321); + } + + assert_eq!(Some(321), value); + } + #[test] #[should_panic(expected = "the given key does not support hashing")] fn reflect_map_no_hash() { From 7b0146a108eba68720acec724d6281ea414e0264 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 15:11:50 -0700 Subject: [PATCH 16/75] Remove redundant trait bounds --- crates/bevy_reflect/src/impls/std.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 7cf5252f3a1b0..20bd8ab2809ab 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -558,13 +558,13 @@ impl Reflect for Cow<'static, str> { } } -impl GetTypeRegistration for Option { +impl GetTypeRegistration for Option { fn get_type_registration() -> TypeRegistration { TypeRegistration::of::>() } } -impl Enum for Option { +impl Enum for Option { fn variant(&self) -> VariantRef { match self { Some(value) => { @@ -592,7 +592,8 @@ impl Enum for Option { } } } -impl Reflect for Option { + +impl Reflect for Option { #[inline] fn type_name(&self) -> &str { std::any::type_name::() @@ -666,7 +667,7 @@ impl Reflect for Option { } } -impl Typed for Option { +impl Typed for Option { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| { From 65a8cf0514e62db4300a9115728d7351e4f102be Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 15:49:42 -0700 Subject: [PATCH 17/75] Updated Enum API with direct field access This prevents the Vec/HashMap allocations needed by the previous one. --- crates/bevy_reflect/src/enums/enum_trait.rs | 57 ++++- crates/bevy_reflect/src/enums/mod.rs | 2 - crates/bevy_reflect/src/enums/variant_ref.rs | 235 ------------------- crates/bevy_reflect/src/enums/variants.rs | 31 +++ crates/bevy_reflect/src/impls/std.rs | 50 ++-- crates/bevy_reflect/src/lib.rs | 6 +- 6 files changed, 121 insertions(+), 260 deletions(-) delete mode 100644 crates/bevy_reflect/src/enums/variant_ref.rs diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index e3a5ca9c0fc44..b99425c6e0a0e 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,16 +1,32 @@ -use crate::{Reflect, ReflectRef, Struct, Tuple, VariantInfo, VariantMut, VariantRef}; +use crate::{Reflect, ReflectRef, Struct, Tuple, VariantInfo, VariantType}; use bevy_utils::HashMap; use std::any::{Any, TypeId}; use std::borrow::Cow; use std::slice::Iter; pub trait Enum: Reflect { - /// Returns an immutable reference to the current variant. - fn variant(&self) -> VariantRef; - /// Returns a mutable reference to the current variant. - fn variant_mut(&mut self) -> VariantMut; + /// Returns a reference to the value of the field (in the current variant) with the given name. + 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. + 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. + fn index_of(&self, name: &str) -> Option; + /// 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; + /// 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 + } } /// A container for compile-time enum info. @@ -92,6 +108,37 @@ impl EnumInfo { } } +pub struct VariantFieldIter<'a> { + pub(crate) container: &'a dyn Enum, + pub(crate) 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 = &'a dyn Reflect; + + fn next(&mut self) -> Option { + let value = self.container.field_at(self.index); + 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> {} + #[inline] pub fn enum_partial_eq(enum_a: &E, reflect_b: &dyn Reflect) -> Option { // TODO: Uncomment and update once we figure out how we want to represent variants diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 86e8bbdc60261..d48db8ba56aaa 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -1,7 +1,5 @@ mod enum_trait; -mod variant_ref; mod variants; pub use enum_trait::*; -pub use variant_ref::*; pub use variants::*; diff --git a/crates/bevy_reflect/src/enums/variant_ref.rs b/crates/bevy_reflect/src/enums/variant_ref.rs deleted file mode 100644 index c300ae8bd6faf..0000000000000 --- a/crates/bevy_reflect/src/enums/variant_ref.rs +++ /dev/null @@ -1,235 +0,0 @@ -use crate::{Reflect, Struct, Tuple, TupleStructFieldIter}; -use bevy_utils::HashMap; -use std::borrow::Cow; -use std::ops::{Deref, DerefMut}; -use std::slice::Iter; - -pub enum VariantRef<'a> { - Unit, - Tuple(TupleVariantRef<'a>), - Struct(StructVariantRef<'a>), -} - -pub enum VariantMut<'a> { - Unit, - Tuple(TupleVariantMut<'a>), - Struct(StructVariantMut<'a>), -} - -pub struct TupleVariantRef<'a> { - fields: Vec>, -} - -impl<'a> TupleVariantRef<'a> { - /// Creates a new [`TupleVariantRef`]. - pub fn new(fields: Vec>) -> Self { - Self { fields } - } - - /// Returns a reference to the value of the field at the given index. - pub fn field(&self, index: usize) -> Option<&dyn Reflect> { - self.fields.get(index).map(AsRef::as_ref) - } - - /// Returns an iterator over the values of the variant's fields. - pub fn iter_fields(&self) -> Iter<'_, VariantFieldRef<'a>> { - self.fields.iter() - } - - /// Returns the number of fields in the variant. - pub fn field_len(&self) -> usize { - self.fields.len() - } -} - -pub struct TupleVariantMut<'a> { - fields: Vec>, -} - -impl<'a> TupleVariantMut<'a> { - /// Creates a new [`TupleVariantMut`]. - pub fn new(fields: Vec>) -> Self { - Self { fields } - } - - /// Returns a reference to the value of the field at the given index. - pub fn field(&self, index: usize) -> Option<&dyn Reflect> { - self.fields.get(index).map(AsRef::as_ref) - } - - /// Returns a mutable reference to the value of the field at the given index. - pub fn field_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> { - self.fields.get_mut(index).map(AsMut::as_mut) - } - - /// Returns an iterator over the values of the variant's fields. - pub fn iter_fields(&self) -> Iter<'_, VariantFieldMut<'a>> { - self.fields.iter() - } - - /// Returns the number of fields in the variant. - pub fn field_len(&self) -> usize { - self.fields.len() - } -} - -pub struct StructVariantRef<'a> { - fields: Vec>, - field_indices: HashMap, usize>, -} - -impl<'a> StructVariantRef<'a> { - /// Creates a new [`StructVariantRef`]. - pub fn new( - fields: Vec>, - field_indices: HashMap, usize>, - ) -> Self { - Self { - fields, - field_indices, - } - } - - /// Returns a reference to the value of the field with the given name. - pub fn field(&self, name: &str) -> Option<&dyn Reflect> { - self.field_indices - .get(name) - .map(|index| self.fields[*index].as_ref()) - } - - /// Returns a reference to the value of the field at the given index. - pub fn field_at(&self, index: usize) -> Option<&dyn Reflect> { - self.fields.get(index).map(AsRef::as_ref) - } - - /// Returns the index of the field with the given name. - pub fn index_of(&self, name: &str) -> Option { - self.field_indices.get(name).copied() - } - - /// Returns an iterator over the values of the variant's fields. - pub fn iter_fields(&self) -> Iter<'_, VariantFieldRef<'a>> { - self.fields.iter() - } - - /// Returns the number of fields in the variant. - pub fn field_len(&self) -> usize { - self.fields.len() - } -} - -pub struct StructVariantMut<'a> { - fields: Vec>, - field_indices: HashMap, usize>, -} - -impl<'a> StructVariantMut<'a> { - /// Creates a new [`StructVariantMut`]. - pub fn new( - fields: Vec>, - field_indices: HashMap, usize>, - ) -> Self { - Self { - fields, - field_indices, - } - } - - /// Returns a reference to the value of the field with the given name. - pub fn field(&self, name: &str) -> Option<&dyn Reflect> { - self.field_indices - .get(name) - .map(|index| self.fields[*index].as_ref()) - } - - /// Returns a reference to the value of the field at the given index. - pub fn field_at(&self, index: usize) -> Option<&dyn Reflect> { - self.fields.get(index).map(AsRef::as_ref) - } - - /// Returns a mutable reference to the value of the field with the given name. - pub fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> { - self.field_indices - .get_mut(name) - .map(|index| self.fields[*index].as_mut()) - } - - /// Returns a mutable reference to the value of the field at the given index. - pub fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> { - self.fields.get_mut(index).map(AsMut::as_mut) - } - - /// Returns the index of the field with the given name. - pub fn index_of(&self, name: &str) -> Option { - self.field_indices.get(name).copied() - } - - /// Returns an iterator over the values of the variant's fields. - pub fn iter_fields(&self) -> Iter<'_, VariantFieldMut<'a>> { - self.fields.iter() - } - - /// Returns the number of fields in the variant. - pub fn field_len(&self) -> usize { - self.fields.len() - } -} - -/// A wrapper around an immutable reference to a variant's field. -#[derive(Copy, Clone)] -pub struct VariantFieldRef<'a>(&'a dyn Reflect); - -impl<'a> VariantFieldRef<'a> { - pub fn new(field: &'a dyn Reflect) -> Self { - Self(field) - } -} - -impl<'a> AsRef for VariantFieldRef<'a> { - fn as_ref(&self) -> &dyn Reflect { - self.0 - } -} - -impl<'a> Deref for VariantFieldRef<'a> { - type Target = dyn Reflect; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -/// A wrapper around a mutable reference to a variant's field. -pub struct VariantFieldMut<'a>(&'a mut dyn Reflect); - -impl<'a> VariantFieldMut<'a> { - pub fn new(field: &'a mut dyn Reflect) -> Self { - Self(field) - } -} - -impl<'a> AsRef for VariantFieldMut<'a> { - fn as_ref(&self) -> &dyn Reflect { - self.0 - } -} - -impl<'a> AsMut for VariantFieldMut<'a> { - fn as_mut(&mut self) -> &mut dyn Reflect { - self.0 - } -} - -impl<'a> Deref for VariantFieldMut<'a> { - type Target = dyn Reflect; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl<'a> DerefMut for VariantFieldMut<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } -} diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs index b5060454df344..be3cb9ab42dc2 100644 --- a/crates/bevy_reflect/src/enums/variants.rs +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -3,6 +3,37 @@ 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)] +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 { diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 20bd8ab2809ab..d50c64b66a39f 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -2,9 +2,8 @@ use crate::{self as bevy_reflect, ReflectFromPtr}; use crate::{ map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, Enum, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, - ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, StructVariantRef, - TupleVariantMut, TupleVariantRef, TypeInfo, TypeRegistration, Typed, ValueInfo, - VariantFieldMut, VariantFieldRef, VariantMut, VariantRef, + ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TypeInfo, TypeRegistration, + Typed, ValueInfo, VariantFieldIter, VariantType, }; use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; @@ -565,32 +564,53 @@ impl GetTypeRegistration for Option { } impl Enum for Option { - fn variant(&self) -> VariantRef { + fn field(&self, _name: &str) -> Option<&dyn Reflect> { + None + } + + fn field_at(&self, index: usize) -> Option<&dyn Reflect> { match self { - Some(value) => { - let field = VariantFieldRef::new(value); - VariantRef::Tuple(TupleVariantRef::new(vec![field])) - } - None => VariantRef::Unit, + Some(value) if index == 0 => Some(value), + _ => None, } } - fn variant_mut(&mut self) -> VariantMut { + 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) => { - let field = VariantFieldMut::new(value); - VariantMut::Tuple(TupleVariantMut::new(vec![field])) - } - None => VariantMut::Unit, + Some(value) if index == 0 => Some(value), + _ => None, } } + fn index_of(&self, _name: &str) -> Option { + None + } + + fn iter_fields(&self) -> VariantFieldIter { + VariantFieldIter::new(self) + } + + #[inline] + fn field_len(&self) -> usize { + 1 + } + + #[inline] fn variant_name(&self) -> &str { match self { Some(..) => "Some", None => "None", } } + + #[inline] + fn variant_type(&self) -> VariantType { + VariantType::Tuple + } } impl Reflect for Option { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index a94290f199206..e87d32ef06be9 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -223,9 +223,9 @@ mod tests { let reflected_value = &mut value; assert_eq!("Some", reflected_value.variant_name()); - if let VariantMut::Tuple(mut variant) = reflected_value.variant_mut() { - variant - .field_mut(0) + if reflected_value.is_variant(VariantType::Tuple) { + reflected_value + .field_at_mut(0) .and_then(|field| field.downcast_mut::()) .map(|field| *field = 321); } From 88bf7887303932bf1e17b6717751aa2d6094a422 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 16:05:01 -0700 Subject: [PATCH 18/75] Re-enabled enum_partial_eq --- crates/bevy_reflect/src/enums/enum_trait.rs | 111 +++++++++++--------- crates/bevy_reflect/src/impls/std.rs | 4 + crates/bevy_reflect/src/lib.rs | 4 + 3 files changed, 70 insertions(+), 49 deletions(-) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index b99425c6e0a0e..71ae75bc0d747 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -15,6 +15,8 @@ pub trait Enum: Reflect { 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. fn index_of(&self, name: &str) -> Option; + /// Returns the name of the field (in the current variant) with the given index. + 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. @@ -139,54 +141,65 @@ impl<'a> Iterator for VariantFieldIter<'a> { impl<'a> ExactSizeIterator for VariantFieldIter<'a> {} +/// 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(enum_a: &E, reflect_b: &dyn Reflect) -> Option { - // TODO: Uncomment and update once we figure out how we want to represent variants - // let enum_b = if let ReflectRef::Enum(e) = reflect_b.reflect_ref() { - // e - // } else { - // return Some(false); - // }; - // - // if enum_a.variant_info() != enum_b.variant_info() { - // return Some(false); - // } - // - // let variant_b = enum_b.variant(); - // match enum_a.variant() { - // EnumVariant::Unit => { - // if let EnumVariant::Unit = variant_b { - // } else { - // return Some(false); - // } - // } - // EnumVariant::NewType(t_a) => { - // if let EnumVariant::NewType(t_b) = variant_b { - // if let Some(false) | None = t_b.reflect_partial_eq(t_a) { - // return Some(false); - // } - // } else { - // return Some(false); - // } - // } - // EnumVariant::Tuple(t_a) => { - // if let EnumVariant::Tuple(t_b) = variant_b { - // if let Some(false) | None = t_b.reflect_partial_eq(t_a.as_reflect()) { - // return Some(false); - // } - // } else { - // return Some(false); - // } - // } - // EnumVariant::Struct(s_a) => { - // if let EnumVariant::Struct(s_b) = variant_b { - // if let Some(false) | None = s_b.reflect_partial_eq(s_a.as_reflect()) { - // return Some(false); - // } - // } else { - // return Some(false); - // } - // } - // } - Some(true) +pub fn enum_partial_eq(a: &TEnum, b: &dyn Reflect) -> Option { + // Both enums? + let enum_b = if let ReflectRef::Enum(e) = b.reflect_ref() { + e + } else { + return Some(false); + }; + + // Same variant name? + if a.variant_name() != enum_b.variant_name() { + return Some(false); + } + + // Same variant type? + if !a.is_variant(enum_b.variant_type()) { + return Some(false); + } + + match a.variant_type() { + VariantType::Struct => { + // Same struct fields? + for (i, value) in a.iter_fields().enumerate() { + let field_name = a.name_at(i).unwrap(); + if let Some(field_value) = enum_b.field(field_name) { + if let Some(false) | None = field_value.reflect_partial_eq(value) { + // Fields failed comparison + return Some(false); + } + } else { + // Field does not exist + return Some(false); + } + } + Some(true) + } + VariantType::Tuple => { + // Same tuple fields? + for (i, value) in a.iter_fields().enumerate() { + if let Some(field_value) = enum_b.field_at(i) { + if let Some(false) | None = field_value.reflect_partial_eq(value) { + // Fields failed comparison + return Some(false); + } + } else { + // Field does not exist + return Some(false); + } + } + Some(true) + } + _ => Some(false), + } } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index d50c64b66a39f..427aedafbf57d 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -590,6 +590,10 @@ impl Enum for Option { None } + fn name_at(&self, _index: usize) -> Option<&str> { + None + } + fn iter_fields(&self) -> VariantFieldIter { VariantFieldIter::new(self) } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index e87d32ef06be9..326d202995e8f 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -221,6 +221,10 @@ mod tests { // Option (Tuple) let mut value = Some(123usize); let reflected_value = &mut value; + + assert!(reflected_value.reflect_partial_eq(&Some(123usize)).unwrap_or_default()); + assert!(!reflected_value.reflect_partial_eq(&Some(321usize)).unwrap_or_default()); + assert_eq!("Some", reflected_value.variant_name()); if reflected_value.is_variant(VariantType::Tuple) { From 875df202eca3ec3b084ef3c8ecc6c75f995771ea Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 16:19:16 -0700 Subject: [PATCH 19/75] Added clarifying comments to Enum methods --- crates/bevy_reflect/src/enums/enum_trait.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 71ae75bc0d747..36c5ad4ac70d0 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -6,16 +6,24 @@ use std::slice::Iter; 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; From 816989d1ca9ccb20cae1375736d85e0c1c00844c Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 16:21:36 -0700 Subject: [PATCH 20/75] Privatize fields --- crates/bevy_reflect/src/enums/enum_trait.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 36c5ad4ac70d0..910afbde9a64a 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -118,9 +118,10 @@ impl EnumInfo { } } +/// An iterator over the fields in the current enum variant. pub struct VariantFieldIter<'a> { - pub(crate) container: &'a dyn Enum, - pub(crate) index: usize, + container: &'a dyn Enum, + index: usize, } impl<'a> VariantFieldIter<'a> { From 5c34c0c3d8e5a7f1a62a9da2ae5ac6f41779deaa Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 16:35:36 -0700 Subject: [PATCH 21/75] Privatize ReflectMeta fields --- .../bevy_reflect_derive/src/derive_data.rs | 24 +++++++++++-------- .../bevy_reflect_derive/src/from_reflect.rs | 10 +++----- .../bevy_reflect_derive/src/impls/values.rs | 18 ++++++-------- .../bevy_reflect_derive/src/reflect_value.rs | 11 ++++----- 4 files changed, 29 insertions(+), 34 deletions(-) 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 9a6856df3a4d4..b3450a45a93fb 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -29,13 +29,13 @@ pub(crate) enum ReflectDerive<'a> { /// ``` pub(crate) struct ReflectMeta<'a> { /// The registered traits for this type. - pub traits: ReflectTraits, + traits: ReflectTraits, /// The name of this type. - pub type_name: &'a Ident, + type_name: &'a Ident, /// The generics defined on this type. - pub generics: &'a Generics, + generics: &'a Generics, /// A cached instance of the path to the `bevy_reflect` crate. - pub bevy_reflect_path: Path, + bevy_reflect_path: Path, } /// Struct data used by derive macros for `Reflect` and `FromReflect`. @@ -125,12 +125,7 @@ impl<'a> ReflectDerive<'a> { } } - let meta = ReflectMeta { - type_name: &input.ident, - generics: &input.generics, - traits, - bevy_reflect_path: utility::get_bevy_reflect_path(), - }; + let meta = ReflectMeta::new(&input.ident, &input.generics, traits); if force_reflect_value { return Ok(Self::Value(meta)); @@ -224,6 +219,15 @@ impl<'a> ReflectDerive<'a> { } 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. pub fn traits(&self) -> &ReflectTraits { &self.traits 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 07f9d71e9fc82..19c0dcfba3b1e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -18,13 +18,9 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { /// Implements `FromReflect` for the given value type pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { - let ReflectMeta { - type_name, - generics, - bevy_reflect_path, - .. - } = meta; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + 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 { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 037a3a39a0863..be01b2214fd11 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -5,20 +5,16 @@ use quote::quote; /// Implements `GetTypeRegistration` and `Reflect` for the given type data. pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { - let ReflectMeta { - traits, - type_name, - generics, - bevy_reflect_path, - } = meta; + let bevy_reflect_path = meta.bevy_reflect_path(); + let type_name = meta.type_name(); - let hash_fn = traits.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = traits.get_partial_eq_impl(bevy_reflect_path); - let debug_fn = traits.get_debug_impl(); + 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, - generics, + meta.generics(), quote! { let info = #bevy_reflect_path::ValueInfo::new::(); #bevy_reflect_path::TypeInfo::Value(info) @@ -26,7 +22,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { bevy_reflect_path, ); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); let get_type_registration_impl = meta.get_type_registration(); TokenStream::from(quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index e9d32bf5d2273..b20e098b88a21 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -27,12 +27,11 @@ pub(crate) struct ReflectValueDef { impl ReflectValueDef { pub fn as_meta(&self) -> ReflectMeta { - ReflectMeta { - traits: self.traits.clone().unwrap_or_default(), - type_name: &self.type_name, - generics: &self.generics, - bevy_reflect_path: utility::get_bevy_reflect_path(), - } + ReflectMeta::new( + &self.type_name, + &self.generics, + self.traits.clone().unwrap_or_default(), + ) } } From 65231ca98056e878d517db54fbdf78166bf22ce1 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 16:56:34 -0700 Subject: [PATCH 22/75] Updated Option Typed impl --- crates/bevy_reflect/src/enums/variants.rs | 71 +++++++++++++++++++++++ crates/bevy_reflect/src/impls/std.rs | 19 +++--- crates/bevy_reflect/src/lib.rs | 39 ++++++++++++- 3 files changed, 120 insertions(+), 9 deletions(-) diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs index be3cb9ab42dc2..76f19564174d9 100644 --- a/crates/bevy_reflect/src/enums/variants.rs +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -84,6 +84,30 @@ pub struct StructVariantInfo { } 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 @@ -115,6 +139,17 @@ impl StructVariantInfo { 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. @@ -125,6 +160,25 @@ pub struct TupleVariantInfo { } 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 @@ -153,6 +207,23 @@ pub struct UnitVariantInfo { } 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 427aedafbf57d..0ff9c32b0f150 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,9 +1,10 @@ use crate::{self as bevy_reflect, ReflectFromPtr}; use crate::{ - map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, Enum, FromReflect, - FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, - ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TypeInfo, TypeRegistration, - Typed, ValueInfo, VariantFieldIter, VariantType, + map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, 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}; @@ -658,7 +659,7 @@ impl Reflect for Option { *self = value.clone(); } else { { - panic!("Enum is not {}.", &std::any::type_name::()); + panic!("Enum is not a {}.", std::any::type_name::()); }; } } @@ -695,8 +696,12 @@ impl Typed for Option { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| { - // TODO: Replace with EnumInfo - TypeInfo::Value(ValueInfo::new::()) + 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])) }) } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 326d202995e8f..25f2410650920 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -222,8 +222,12 @@ mod tests { let mut value = Some(123usize); let reflected_value = &mut value; - assert!(reflected_value.reflect_partial_eq(&Some(123usize)).unwrap_or_default()); - assert!(!reflected_value.reflect_partial_eq(&Some(321usize)).unwrap_or_default()); + assert!(reflected_value + .reflect_partial_eq(&Some(123usize)) + .unwrap_or_default()); + assert!(!reflected_value + .reflect_partial_eq(&Some(321usize)) + .unwrap_or_default()); assert_eq!("Some", reflected_value.variant_name()); @@ -755,6 +759,37 @@ mod tests { // panic!("Expected `TypeInfo::Enum`"); // } + // Option (Enum) + 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`"); + } + // Tuple type MyTuple = (u32, f32, String); From 8546bae459ef9b9ce0072d1a24100b207841e4cb Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 14 May 2022 21:54:19 -0700 Subject: [PATCH 23/75] Added DynamicEnum --- crates/bevy_reflect/src/enums/dynamic_enum.rs | 309 ++++++++++++++++++ crates/bevy_reflect/src/enums/enum_trait.rs | 67 +--- crates/bevy_reflect/src/enums/helpers.rs | 88 +++++ crates/bevy_reflect/src/enums/mod.rs | 4 + crates/bevy_reflect/src/enums/variants.rs | 2 +- crates/bevy_reflect/src/impls/std.rs | 50 ++- crates/bevy_reflect/src/lib.rs | 15 + crates/bevy_reflect/src/struct_trait.rs | 5 + 8 files changed, 463 insertions(+), 77 deletions(-) create mode 100644 crates/bevy_reflect/src/enums/dynamic_enum.rs create mode 100644 crates/bevy_reflect/src/enums/helpers.rs 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..152f59ba8b1a7 --- /dev/null +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -0,0 +1,309 @@ +use crate::utility::NonGenericTypeInfoCell; +use crate::{ + enum_hash, enum_partial_eq, DynamicInfo, DynamicStruct, DynamicTuple, Enum, Reflect, + ReflectMut, ReflectRef, Struct, Tuple, TypeInfo, Typed, VariantFieldIter, VariantType, +}; +use std::any::Any; + +/// 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 + } +} + +/// 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>(name: I, variant_name: I, variant: DynamicVariant) -> Self { + Self { + name: name.into(), + variant_name: variant_name.into(), + variant, + } + } + + /// 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>(&mut self, name: I, variant: DynamicVariant) { + self.variant_name = name.into(); + self.variant = variant; + } + + /// 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.clone_value()); + } + DynamicEnum::new( + value.type_name(), + value.variant_name(), + DynamicVariant::Tuple(data), + ) + } + VariantType::Struct => { + let mut data = DynamicStruct::default(); + for (index, field) in value.iter_fields().enumerate() { + let name = value.name_at(index).unwrap(); + data.insert_boxed(name, field.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(enum_value) = value.reflect_ref() { + for (i, value) in enum_value.iter_fields().enumerate() { + let name = enum_value.name_at(i).unwrap(); + if let Some(v) = self.field_mut(name) { + v.apply(value); + } + } + } else { + panic!("Attempted to apply non-enum type to enum type."); + } + } + + #[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) + } +} + +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 index 910afbde9a64a..5196635aef872 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,4 +1,4 @@ -use crate::{Reflect, ReflectRef, Struct, Tuple, VariantInfo, VariantType}; +use crate::{DynamicEnum, Reflect, ReflectRef, Struct, Tuple, VariantInfo, VariantType}; use bevy_utils::HashMap; use std::any::{Any, TypeId}; use std::borrow::Cow; @@ -33,6 +33,8 @@ pub trait Enum: Reflect { 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 @@ -149,66 +151,3 @@ impl<'a> Iterator for VariantFieldIter<'a> { } impl<'a> ExactSizeIterator for VariantFieldIter<'a> {} - -/// 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 enum_b = if let ReflectRef::Enum(e) = b.reflect_ref() { - e - } else { - return Some(false); - }; - - // Same variant name? - if a.variant_name() != enum_b.variant_name() { - return Some(false); - } - - // Same variant type? - if !a.is_variant(enum_b.variant_type()) { - return Some(false); - } - - match a.variant_type() { - VariantType::Struct => { - // Same struct fields? - for (i, value) in a.iter_fields().enumerate() { - let field_name = a.name_at(i).unwrap(); - if let Some(field_value) = enum_b.field(field_name) { - if let Some(false) | None = field_value.reflect_partial_eq(value) { - // Fields failed comparison - return Some(false); - } - } else { - // Field does not exist - return Some(false); - } - } - Some(true) - } - VariantType::Tuple => { - // Same tuple fields? - for (i, value) in a.iter_fields().enumerate() { - if let Some(field_value) = enum_b.field_at(i) { - if let Some(false) | None = field_value.reflect_partial_eq(value) { - // Fields failed comparison - return Some(false); - } - } else { - // Field does not exist - return Some(false); - } - } - Some(true) - } - _ => Some(false), - } -} diff --git a/crates/bevy_reflect/src/enums/helpers.rs b/crates/bevy_reflect/src/enums/helpers.rs new file mode 100644 index 0000000000000..7e007521c5d55 --- /dev/null +++ b/crates/bevy_reflect/src/enums/helpers.rs @@ -0,0 +1,88 @@ +use crate::{Enum, Reflect, ReflectRef, VariantType}; +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.reflect_hash()?); + } + Some(hasher.finish()) +} + +// TODO: Add serializable. How do we handle enums? +// pub fn enum_serialize(value: &TEnum, serializer: S) -> Result +// where +// TEnum: Enum + ?Sized, +// S: serde::Serializer, +// { +// +// +// } + +/// 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 (i, value) in a.iter_fields().enumerate() { + let field_name = a.name_at(i).unwrap(); + if let Some(field_value) = b.field(field_name) { + if let Some(false) | None = field_value.reflect_partial_eq(value) { + // Fields failed comparison + return Some(false); + } + } else { + // Field does not exist + return Some(false); + } + } + Some(true) + } + VariantType::Tuple => { + // Same tuple fields? + for (i, value) in a.iter_fields().enumerate() { + if let Some(field_value) = b.field_at(i) { + if let Some(false) | None = field_value.reflect_partial_eq(value) { + // Fields failed comparison + return Some(false); + } + } else { + // Field does not exist + return Some(false); + } + } + Some(true) + } + _ => Some(false), + } +} diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index d48db8ba56aaa..43fc164dffd15 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -1,5 +1,9 @@ +mod dynamic_enum; mod enum_trait; +mod helpers; mod variants; +pub use dynamic_enum::*; pub use enum_trait::*; +pub use helpers::*; pub use variants::*; diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs index 76f19564174d9..b1a9740d21c75 100644 --- a/crates/bevy_reflect/src/enums/variants.rs +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::slice::Iter; /// Describes the form of an enum variant. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum VariantType { /// Struct enums take the form: /// diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 0ff9c32b0f150..78f6d6309bb70 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,9 +1,9 @@ use crate::{self as bevy_reflect, ReflectFromPtr}; use crate::{ - map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, Enum, EnumInfo, - FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, - ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TupleVariantInfo, TypeInfo, - TypeRegistration, Typed, UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter, + 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, }; @@ -616,6 +616,10 @@ impl Enum for Option { fn variant_type(&self) -> VariantType { VariantType::Tuple } + + fn clone_dynamic(&self) -> DynamicEnum { + DynamicEnum::from_ref::(self) + } } impl Reflect for Option { @@ -654,13 +658,35 @@ impl Reflect for Option { #[inline] fn apply(&mut self, value: &dyn Reflect) { - let value = value.as_any(); - if let Some(value) = value.downcast_ref::() { - *self = value.clone(); - } else { - { - panic!("Enum is not a {}.", std::any::type_name::()); - }; + 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(); + self.field_mut(name).map(|v| v.apply(field)); + } + } 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::()), + } + } } } @@ -684,7 +710,7 @@ impl Reflect for Option { } fn reflect_hash(&self) -> Option { - None + crate::enum_hash(self) } fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 25f2410650920..9e138d05f3a2e 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -241,6 +241,21 @@ mod tests { assert_eq!(Some(321), value); } + #[test] + fn dynamic_enum() { + // Some(usize) -> None + let mut value = Some(123usize); + let mut dyn_enum = DynamicEnum::from::>(None); + value.apply(&dyn_enum); + assert_eq!(None, value); + + // None -> Some(usize) + let data = (321usize,).clone_dynamic(); + dyn_enum.set_variant("Some".to_string(), DynamicVariant::Tuple(data)); + value.apply(&dyn_enum); + assert_eq!(Some(321), value); + } + #[test] #[should_panic(expected = "the given key does not support hashing")] fn reflect_map_no_hash() { 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 { From 30aa279bdaf1c25639eb6341ed54fbcc378ee115 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 01:42:25 -0700 Subject: [PATCH 24/75] Fix incorrect impl on Option --- crates/bevy_reflect/src/impls/std.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 78f6d6309bb70..d722ca8c11614 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -601,7 +601,10 @@ impl Enum for Option { #[inline] fn field_len(&self) -> usize { - 1 + match self { + Some(..) => 1, + None => 0, + } } #[inline] @@ -614,7 +617,10 @@ impl Enum for Option { #[inline] fn variant_type(&self) -> VariantType { - VariantType::Tuple + match self { + Some(..) => VariantType::Tuple, + None => VariantType::Unit, + } } fn clone_dynamic(&self) -> DynamicEnum { From f30906412cc271f45454888d98c4668d08f292d2 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 02:55:15 -0700 Subject: [PATCH 25/75] Add derive macro logic for Enum --- .../bevy_reflect_derive/src/derive_data.rs | 10 + .../bevy_reflect_derive/src/impls/enums.rs | 797 ++++++++---------- .../bevy_reflect_derive/src/lib.rs | 2 +- .../bevy_reflect_derive/src/reflect_value.rs | 2 +- crates/bevy_reflect/src/lib.rs | 53 +- 5 files changed, 386 insertions(+), 478 deletions(-) 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 b3450a45a93fb..4f95c86003152 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,3 +1,4 @@ +use quote::quote; use crate::container_attributes::ReflectTraits; use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; @@ -301,10 +302,19 @@ impl<'a> ReflectEnum<'a> { } /// 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>] { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 0fc61e4e800f4..71eb1adb6d372 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,231 +1,138 @@ -use crate::container_attributes::ReflectTraits; +use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField}; +use crate::impls::impl_typed; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; -use quote::quote; -use syn::{Fields, Generics, Path, Variant}; +use quote::{quote, ToTokens}; + +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, + enum_apply, + } = generate_impls(reflect_enum, &ref_index, &ref_name, &ref_value); + + let hash_fn = reflect_enum + .meta() + .traits() + .get_hash_impl(bevy_reflect_path); + 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) + } + } + }); -fn tuple_field_name(i: usize) -> String { - format!("t{}", i) -} + 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 -fn tuple_field_ident(i: usize) -> Ident { - Ident::new(tuple_field_name(i).as_str(), Span::call_site()) -} + #typed_impl -pub(crate) fn impl_enum( - enum_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - traits: &ReflectTraits, - active_variants: &[(&Variant, usize)], -) -> TokenStream { - let mut variant_indices = Vec::new(); - let mut struct_wrappers = Vec::new(); - let mut tuple_wrappers = Vec::new(); - let mut variant_names = Vec::new(); - let mut variant_idents = Vec::new(); - let mut reflect_variants = Vec::new(); - let mut reflect_variants_mut = Vec::new(); - let mut variant_with_fields_idents = Vec::new(); - let mut variant_without_fields_idents = Vec::new(); - for (variant, variant_index) in active_variants.iter() { - let variant_ident = { - let ident = &variant.ident; - quote!(#enum_name::#ident) - }; - let variant_name = variant_ident - .to_string() - .chars() - .filter(|c| !c.is_whitespace()) - .collect::(); - let variant_without_fields_ident = { - match &variant.fields { - Fields::Named(_struct_fields) => { - quote!(#variant_ident {..}) - } - Fields::Unnamed(tuple) => { - let tuple_fields = &tuple.unnamed; - if tuple_fields.len() == 1 { - quote!(#variant_ident (_)) - } else { - quote!(#variant_ident (..)) - } - } - Fields::Unit => { - quote!(#variant_ident) + 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, } } - }; - let variant_with_fields_ident = { - match &variant.fields { - Fields::Named(struct_fields) => { - let field_idents = struct_fields - .named - .iter() - .map(|field| field.ident.as_ref().unwrap()) - .collect::>(); - quote!(#variant_ident {#(#field_idents,)*}) - } - Fields::Unnamed(tuple_fields) => { - let field_idents = (0..tuple_fields.unnamed.len()) - .map(tuple_field_ident) - .collect::>(); - if tuple_fields.unnamed.len() == 1 { - quote!(#variant_ident (new_type)) - } else { - quote!(#variant_ident (#(#field_idents,)*)) - } - } - Fields::Unit => { - quote!(#variant_ident) + + fn field_at(&self, #ref_index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field_at,)* + _ => None, } } - }; - let wrapper_ident = if let Fields::Named(_) | Fields::Unnamed(_) = &variant.fields { - Ident::new( - format!("{}{}Wrapper", enum_name, variant.ident).as_str(), - Span::call_site(), - ) - } else { - Ident::new("unused", Span::call_site()) - }; - let wrapper_name = match &variant.fields { - Fields::Named(struct_fields) => quote!(#struct_fields).to_string(), - Fields::Unnamed(tuple_fields) => quote!(#tuple_fields).to_string(), - Fields::Unit => "unused".to_string(), - }; - let reflect_variant = { - match &variant.fields { - Fields::Named(_struct_fields) => { - quote!({ - let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; - #bevy_reflect_path::EnumVariant::Struct(wrapper_ref as &dyn #bevy_reflect_path::Struct) - }) - } - Fields::Unnamed(tuple_fields) => { - if tuple_fields.unnamed.len() == 1 { - quote!(#bevy_reflect_path::EnumVariant::NewType(new_type as &dyn #bevy_reflect_path::Reflect)) - } else { - quote!({ - let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; - #bevy_reflect_path::EnumVariant::Tuple(wrapper_ref as &dyn #bevy_reflect_path::Tuple) - }) - } - } - Fields::Unit => { - quote!(#bevy_reflect_path::EnumVariant::Unit) + + fn field_mut(&mut self, #ref_name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field,)* + _ => None, } } - }; - let reflect_variant_mut = { - match &variant.fields { - Fields::Named(_struct_fields) => { - quote!({ - let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; - #bevy_reflect_path::EnumVariantMut::Struct(wrapper_ref as &mut dyn #bevy_reflect_path::Struct) - }) - } - Fields::Unnamed(tuple) => { - let tuple_fields = &tuple.unnamed; - if tuple_fields.len() == 1 { - quote!(#bevy_reflect_path::EnumVariantMut::NewType(new_type as &mut dyn #bevy_reflect_path::Reflect)) - } else { - quote!({ - let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; - #bevy_reflect_path::EnumVariantMut::Tuple(wrapper_ref as &mut dyn #bevy_reflect_path::Tuple) - }) - } - } - Fields::Unit => { - quote!(#bevy_reflect_path::EnumVariantMut::Unit) + + fn field_at_mut(&mut self, #ref_index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field_at,)* + _ => None, } } - }; - match &variant.fields { - Fields::Named(struct_fields) => { - struct_wrappers.push(( - wrapper_ident, - wrapper_name, - variant_index, - variant_with_fields_ident.clone(), - struct_fields.clone(), - )); - } - Fields::Unnamed(tuple_fields) => { - if tuple_fields.unnamed.len() > 1 { - tuple_wrappers.push(( - wrapper_ident, - wrapper_name, - variant_index, - variant_with_fields_ident.clone(), - tuple_fields.clone(), - )); + + fn index_of(&self, #ref_name: &str) -> Option { + match self { + #(#enum_index_of,)* + _ => None, } } - Fields::Unit => {} - } - variant_indices.push(variant_index); - variant_names.push(variant_name); - variant_idents.push(variant_ident); - reflect_variants.push(reflect_variant); - reflect_variants_mut.push(reflect_variant_mut); - variant_with_fields_idents.push(variant_with_fields_ident); - variant_without_fields_idents.push(variant_without_fields_ident); - } - let hash_fn = traits.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = 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 (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let mut token_stream = TokenStream::from(quote! { - #get_type_registration_impl - impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_clause { - fn variant(&self) -> #bevy_reflect_path::EnumVariant<'_> { - match self { - #(#variant_with_fields_idents => #reflect_variants,)* + fn name_at(&self, #ref_index: usize) -> Option<&str> { + match self { + #(#enum_name_at,)* + _ => None, } } - fn variant_mut(&mut self) -> #bevy_reflect_path::EnumVariantMut<'_> { - match self { - #(#variant_with_fields_idents => #reflect_variants_mut,)* - } + fn iter_fields(&self) -> #bevy_reflect_path::VariantFieldIter { + VariantFieldIter::new(self) } - fn variant_info(&self) -> #bevy_reflect_path::VariantInfo<'_> { - let index = match self { - #(#variant_without_fields_idents => #variant_indices,)* - }; - #bevy_reflect_path::VariantInfo { - index, - name: self.get_index_name(index).unwrap(), + #[inline] + fn field_len(&self) -> usize { + match self { + #(#enum_field_len,)* + _ => 0, } } - fn get_index_name(&self, index: usize) -> Option<&'_ str> { - match index { - #(#variant_indices => Some(#variant_names),)* - _ => None, + #[inline] + fn variant_name(&self) -> &str { + match self { + #(#enum_variant_name,)* + _ => unreachable!(), } } - fn get_index_from_name(&self, name: &str) -> Option { - match name { - #(#variant_names => Some(#variant_indices),)* - _ => None, + #[inline] + fn variant_type(&self) -> #bevy_reflect_path::VariantType { + match self { + #(#enum_variant_type,)* + _ => unreachable!(), } } - fn iter_variants_info(&self) -> #bevy_reflect_path::VariantInfoIter<'_> { - #bevy_reflect_path::VariantInfoIter::new(self) + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicEnum { + #bevy_reflect_path::DynamicEnum::from_ref::(self) } } @@ -236,32 +143,62 @@ pub(crate) fn impl_enum( } #[inline] - fn any(&self) -> &dyn std::any::Any { + 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 any_mut(&mut self) -> &mut dyn std::any::Any { + 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 { - use #bevy_reflect_path::Enum; - Box::new(self.clone()) // FIXME: should be clone_dynamic, so that Clone is not a required bound + Box::new(#bevy_reflect_path::Enum::clone_dynamic(self)) } + #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; + fn set(&mut self, #ref_value: Box) -> Result<(), Box> { + *self = #ref_value.take()?; Ok(()) } #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - use #bevy_reflect_path::Enum; - let value = value.any(); - if let Some(value) = value.downcast_ref::() { - *self = value.clone(); //FIXME: should apply the variant instead - } else { - panic!("Attempted to apply non-enum type to enum type."); + fn 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 + for (index, field) in #ref_value.iter_fields().enumerate() { + let name = #ref_value.name_at(index).unwrap(); + #bevy_reflect_path::Enum::field_mut(self, name).map(|v| v.apply(field)); + } + } else { + // New variant -> perform a switch + match #ref_value.variant_name() { + #(#enum_apply,)* + _ => panic!("Enum is not a {}.", std::any::type_name::()), + } + } } } @@ -276,266 +213,204 @@ pub(crate) fn impl_enum( #hash_fn #partial_eq_fn - - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - } - }); - for (wrapper_ident, wrapper_name, _variant_index, variant_with_fields_ident, fields) in - struct_wrappers - { - let mut field_names = Vec::new(); - let mut field_idents = Vec::new(); - let mut field_indices = Vec::new(); - for (i, field) in fields.named.iter().enumerate() { - field_names.push(field.ident.as_ref().unwrap().to_string()); - field_idents.push(field.ident.clone()); - field_indices.push(i); } - let fields_len = field_indices.len(); - let match_fields = quote!( - #variant_with_fields_ident => (#(#field_idents,)*), - _ => unreachable!(), - ); - let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { - #match_fields - };); - let match_fields = quote!(let (#(#field_idents,)*) = match &self.0 { - #match_fields - };); - token_stream.extend(TokenStream::from(quote! { - #[repr(transparent)] - pub struct #wrapper_ident(enum_name); - impl #bevy_reflect_path::Reflect for #wrapper_ident { - fn type_name(&self) -> &str { - #wrapper_name - } - - fn any(&self) -> &dyn std::any::Any { - self.0.any() - } - - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self.0.any_mut() - } - - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - self.0.apply(value); - } - - fn set(&mut self, value: Box) -> Result<(), Box> { - self.0.set(value) - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::Struct(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::Struct(self) - } - - fn clone_value(&self) -> Box { - self.0.clone_value() - } - - fn reflect_hash(&self) -> Option { - self.0.reflect_hash() - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - self.0.reflect_partial_eq(value) - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - self.0.serializable() - } + }) +} - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } +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, + enum_apply: Vec, +} - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - } - impl #bevy_reflect_path::Struct for #wrapper_ident { - fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { - #match_fields - match name { - #(#field_names => Some(#field_idents),)* - _ => None, - } - } +fn filter_active(field: &StructField) -> bool { + !field.attrs.ignore +} - fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - #match_fields_mut - match name { - #(#field_names => Some(#field_idents),)* - _ => None, - } - } +fn underscores(count: usize) -> proc_macro2::TokenStream { + let mut output = quote! {}; + for _ in 0..count { + output = quote! { + #output _, + } + } + output +} - fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - #match_fields - match index { - #(#field_indices => Some(#field_idents),)* - _ => None, +fn generate_impls( + reflect_enum: &ReflectEnum, + ref_index: &Ident, + ref_name: &Ident, + ref_value: &Ident, +) -> EnumImpls { + let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); + + let mut variant_info: Vec = Vec::new(); + let mut enum_field: Vec = Vec::new(); + let mut enum_field_at: Vec = Vec::new(); + let mut enum_index_of: Vec = Vec::new(); + let mut enum_name_at: Vec = Vec::new(); + let mut enum_field_len: Vec = Vec::new(); + let mut enum_variant_name: Vec = Vec::new(); + let mut enum_variant_type: Vec = Vec::new(); + let mut enum_apply: Vec = 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); + + // TODO: Filter by active fields + match &variant.fields { + EnumVariantFields::Unit => { + variant_info.push(quote! { + #bevy_reflect_path::VariantInfo::Unit( + #bevy_reflect_path::UnitVariantInfo::new(#name) + ) + }); + enum_variant_name.push(quote! { + #unit => #name + }); + enum_apply.push(quote! { + #name => { + *self = #unit; } - } - - fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - #match_fields_mut - match index { - #(#field_indices => Some(#field_idents),)* - _ => None, + }); + } + EnumVariantFields::Unnamed(fields) => { + let mut field_info = Vec::new(); + let mut variant_apply = Vec::new(); + for (field_idx, field) in fields.iter().enumerate() { + let empties = underscores(field_idx); + enum_field_at.push(quote! { + #unit( #empties value, .. ) if #ref_index == #field_idx => Some(value) + }); + + let field_ty = &field.data.ty; + field_info.push(quote! { + #bevy_reflect_path::UnnamedField::new::<#field_ty>(#field_idx) + }); + + let expect_field = format!("field at index `{}` should exist", field_idx); + let expect_type = format!( + "field at index `{}` should be of type `{}`", + field_idx, + field_ty.to_token_stream().to_string() + ); + variant_apply.push(quote! { + #ref_value + .field_at(#field_idx) + .expect(#expect_field) + .clone_value() + .take::<#field_ty>() + .expect(#expect_type) + }); + } + + let field_len = fields.len(); + 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::Tuple + }); + enum_apply.push(quote! { + #name => { + *self = #unit( #(#variant_apply),* ); } - } - fn name_at(&self, index: usize) -> Option<&str> { - match index { - #(#field_indices => Some(#field_names),)* - _ => None, + }); + variant_info.push(quote! { + #bevy_reflect_path::VariantInfo::Tuple( + #bevy_reflect_path::TupleVariantInfo::new(#name, &[ + #(#field_info),* + ]) + ) + }); + } + EnumVariantFields::Named(fields) => { + let mut field_info = Vec::new(); + let mut variant_apply = Vec::new(); + for (field_idx, field) in fields.iter().enumerate() { + 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 == #field_idx => Some(#field_ident) + }); + enum_index_of.push(quote! { + #unit{ #field_ident, .. } if #ref_name == #field_name => Some(#field_idx) + }); + enum_name_at.push(quote! { + #unit{ #field_ident, .. } if #ref_index == #field_idx => Some(#field_name) + }); + + let field_ty = &field.data.ty; + field_info.push(quote! { + #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) + }); + + let expect_field = format!("field with name `{}` should exist", field_name); + let expect_type = format!( + "field with name `{}` should be of type `{}`", + field_name, + field_ty.to_token_stream().to_string() + ); + variant_apply.push(quote! { + #field_ident: #ref_value + .field(#field_name) + .expect(#expect_field) + .clone_value() + .take::<#field_ty>() + .expect(#expect_type) + }); + } + + let field_len = fields.len(); + 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::Struct + }); + enum_apply.push(quote! { + #name => { + *self = #unit{ #(#variant_apply),* }; } - } - - fn field_len(&self) -> usize { - #fields_len - } - - fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { - #bevy_reflect_path::FieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { - #match_fields - let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(#field_names, #field_idents.clone_value());)* - dynamic - } + }); + variant_info.push(quote! { + #bevy_reflect_path::VariantInfo::Struct( + #bevy_reflect_path::StructVariantInfo::new(#name, &[ + #(#field_info),* + ]) + ) + }); } - })); - } - for (wrapper_ident, wrapper_name, _variant_index, variant_with_fields_ident, fields) in - tuple_wrappers - { - let mut field_names = Vec::new(); - let mut field_idents = Vec::new(); - let mut field_indices = Vec::new(); - for (index, _field) in fields.unnamed.iter().enumerate() { - field_names.push(tuple_field_name(index)); - field_idents.push(tuple_field_ident(index)); - field_indices.push(index); } - let fields_len = field_indices.len(); - let match_fields = quote!( - #variant_with_fields_ident => (#(#field_idents,)*), - _ => unreachable!(), - ); - let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { - #match_fields - };); - let match_fields = quote!(let (#(#field_idents,)*) = match &self.0 { - #match_fields - };); - token_stream.extend(TokenStream::from(quote! { - #[repr(transparent)] - pub struct #wrapper_ident(enum_name); - impl #bevy_reflect_path::Reflect for #wrapper_ident { - fn type_name(&self) -> &str { - #wrapper_name - } - - fn into_any(self: Box) -> Box { - self - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - self.0.apply(value); - } - - fn set(&mut self, value: Box) -> Result<(), Box> { - self.0.set(value) - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::Tuple(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::Tuple(self) - } - - fn clone_value(&self) -> Box { - self.0.clone_value() - } - - fn reflect_hash(&self) -> Option { - self.0.reflect_hash() - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - self.0.reflect_partial_eq(value) - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - self.0.serializable() - } - - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - } - impl #bevy_reflect_path::Tuple for #wrapper_ident { - fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - #match_fields - match index { - #(#field_indices => Some(#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - #match_fields_mut - match index { - #(#field_indices => Some(#field_idents),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #fields_len - } - - fn iter_fields(&self) -> #bevy_reflect_path::TupleFieldIter { - #bevy_reflect_path::TupleFieldIter::new(self) - } + } - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTuple { - #match_fields - let mut dynamic = #bevy_reflect_path::DynamicTuple::default(); - #(dynamic.insert_boxed(#field_idents.clone_value());)* - dynamic - } - } - })); + EnumImpls { + variant_info, + enum_field, + enum_field_at, + enum_index_of, + enum_name_at, + enum_field_len, + enum_variant_name, + enum_apply, + enum_variant_type, } - token_stream } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index e72b2c0e823a3..9c65984011f44 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -48,8 +48,8 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { ReflectDerive::Struct(struct_data) => impls::impl_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), - _ => todo!(), } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index b20e098b88a21..390b07abdbed6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -1,5 +1,5 @@ use crate::container_attributes::ReflectTraits; -use crate::{utility, ReflectMeta}; +use crate::ReflectMeta; use proc_macro2::Ident; use syn::parse::{Parse, ParseStream}; use syn::token::{Paren, Where}; diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 9e138d05f3a2e..d4d4d317f9b35 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -209,33 +209,56 @@ mod tests { } #[test] + #[allow(unused_variables)] fn reflect_enum() { - // TODO: Uncomment when derive Reflect is re-enabled for enums - // #[derive(Reflect)] - // enum Foo { - // A, - // B(usize), - // C { value: f32 }, - // } + #[derive(Reflect, Debug, PartialEq)] + enum Foo { + A, + B(usize), + C { value: f32 }, + } - // Option (Tuple) - let mut value = Some(123usize); - let reflected_value = &mut value; + // === Foo === // + let mut foo = Foo::B(123); + assert_eq!("B", foo.variant_name()); + assert_eq!(1, foo.field_len()); + + if foo.is_variant(VariantType::Tuple) { + foo.field_at_mut(0) + .and_then(|field| field.downcast_mut::()) + .map(|field| *field = 321); + } else { + panic!("expected `VariantType::Tuple`") + } + + assert_eq!(Foo::B(321), foo); - assert!(reflected_value + let mut new_value = DynamicEnum::from(Foo::C { value: 1.23 }); + foo.apply(&new_value); + assert_eq!(Foo::C { value: 1.23 }, foo); + + new_value.set_variant("A", DynamicVariant::Unit); + foo.apply(&new_value); + assert_eq!(Foo::A, foo); + + // === Option === // + let mut value = Some(123usize); + assert!(value .reflect_partial_eq(&Some(123usize)) .unwrap_or_default()); - assert!(!reflected_value + assert!(!value .reflect_partial_eq(&Some(321usize)) .unwrap_or_default()); - assert_eq!("Some", reflected_value.variant_name()); + assert_eq!("Some", value.variant_name()); - if reflected_value.is_variant(VariantType::Tuple) { - reflected_value + if value.is_variant(VariantType::Tuple) { + value .field_at_mut(0) .and_then(|field| field.downcast_mut::()) .map(|field| *field = 321); + } else { + panic!("expected `VariantType::Tuple`") } assert_eq!(Some(321), value); From e3adb47f9baac19f28e4d0876e8845903c021794 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 15:10:50 -0700 Subject: [PATCH 26/75] Add variant_path method --- crates/bevy_reflect/src/enums/enum_trait.rs | 16 ++++++++++++++++ crates/bevy_reflect/src/lib.rs | 1 + 2 files changed, 17 insertions(+) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 5196635aef872..8d43408e3004c 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -39,6 +39,10 @@ pub trait Enum: Reflect { 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. @@ -92,6 +96,18 @@ impl EnumInfo { 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.id().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() diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index d4d4d317f9b35..bdfd2ee945e27 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -251,6 +251,7 @@ mod tests { .unwrap_or_default()); assert_eq!("Some", value.variant_name()); + assert_eq!("core::option::Option::Some", value.variant_path()); if value.is_variant(VariantType::Tuple) { value From b71118c40a2ce5686b3ca3bf264623fbff02fbb2 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 16:43:19 -0700 Subject: [PATCH 27/75] Enforce proper field access for struct/tuple variants --- .../bevy_reflect_derive/src/impls/enums.rs | 16 +++++++++++++--- crates/bevy_reflect/src/enums/enum_trait.rs | 9 ++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 71eb1adb6d372..c21eeb7bfe85b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -188,9 +188,19 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { 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 - for (index, field) in #ref_value.iter_fields().enumerate() { - let name = #ref_value.name_at(index).unwrap(); - #bevy_reflect_path::Enum::field_mut(self, name).map(|v| v.apply(field)); + match #ref_value.variant_type() { + #bevy_reflect_path::VariantType::Struct => { + for (index, field) in #ref_value.iter_fields().enumerate() { + let name = #ref_value.name_at(index).unwrap(); + #bevy_reflect_path::Enum::field_mut(self, name).map(|v| v.apply(field)); + } + } + #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)); + } + } + _ => {} } } else { // New variant -> perform a switch diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 8d43408e3004c..820beb46ba050 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -155,7 +155,14 @@ impl<'a> Iterator for VariantFieldIter<'a> { type Item = &'a dyn Reflect; fn next(&mut self) -> Option { - let value = self.container.field_at(self.index); + let value = match self.container.variant_type() { + VariantType::Unit => None, + VariantType::Tuple => self.container.field_at(self.index), + VariantType::Struct => { + let name = self.container.name_at(self.index)?; + self.container.field(name) + } + }; self.index += 1; value } From 7a3236856d9e5c4dcb073c4fa40d46676602a4bc Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 16:52:55 -0700 Subject: [PATCH 28/75] Add doc comment for Enum trait --- crates/bevy_reflect/src/enums/enum_trait.rs | 76 +++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 820beb46ba050..09e9fa0edfd00 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -4,6 +4,82 @@ 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, unlike their struct counterparts, +/// are a lot more complex. 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, 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. /// From 3d540670d5f739b0f058aef70e78adeda63cc5f8 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 17:37:03 -0700 Subject: [PATCH 29/75] Fixed unit variants not returning VariantType --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index c21eeb7bfe85b..4ca38f4c51f8d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -287,6 +287,9 @@ fn generate_impls( enum_variant_name.push(quote! { #unit => #name }); + enum_variant_type.push(quote! { + #unit => #bevy_reflect_path::VariantType::Unit + }); enum_apply.push(quote! { #name => { *self = #unit; From c1e5682b8f29613d3519d5e498d1f2bb14c7b64a Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 17:42:42 -0700 Subject: [PATCH 30/75] Fixed panic in Reflect::apply for derived enums --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 4ca38f4c51f8d..de3976ffc93ae 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -206,9 +206,11 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { // New variant -> perform a switch match #ref_value.variant_name() { #(#enum_apply,)* - _ => panic!("Enum is not a {}.", std::any::type_name::()), + x => panic!("Variant named `{}` does not exist on enum `{}`", x, std::any::type_name::()), } } + } else { + panic!("`{}` is not an enum", #ref_value.type_name()); } } From 1ba467d1481a56f6b6c73472732bb6ec7201ffb5 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 17:50:33 -0700 Subject: [PATCH 31/75] Refactor tests --- crates/bevy_reflect/src/enums/dynamic_enum.rs | 22 +- crates/bevy_reflect/src/enums/enum_trait.rs | 2 +- crates/bevy_reflect/src/enums/mod.rs | 250 ++++++++++++++++++ crates/bevy_reflect/src/impls/std.rs | 63 ++++- crates/bevy_reflect/src/lib.rs | 152 ----------- 5 files changed, 333 insertions(+), 156 deletions(-) diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 152f59ba8b1a7..0a25a8068816e 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -28,6 +28,24 @@ impl Default for DynamicVariant { } } +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. @@ -88,9 +106,9 @@ impl DynamicEnum { } /// Set the current enum variant represented by this struct. - pub fn set_variant>(&mut self, name: I, variant: DynamicVariant) { + pub fn set_variant, V: Into>(&mut self, name: I, variant: V) { self.variant_name = name.into(); - self.variant = variant; + self.variant = variant.into(); } /// Create a [`DynamicEnum`] from an existing one. diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 09e9fa0edfd00..b22513a14f82a 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -176,7 +176,7 @@ impl EnumInfo { /// /// This does _not_ check if the given variant exists. pub fn variant_path(&self, name: &str) -> String { - format!("{}::{}", self.id().type_name(), name) + format!("{}::{}", self.type_name(), name) } /// Checks if a variant with the given name exists within this enum. diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 43fc164dffd15..f170dbe37214c 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -7,3 +7,253 @@ pub use dynamic_enum::*; pub use enum_trait::*; pub use helpers::*; pub use variants::*; + +#[cfg(test)] +#[allow(unused_variables)] +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_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 = MyEnum::A; + assert_eq!(0, value.field_len()); + let mut iter = value.iter_fields(); + assert!(iter.next().is_none()); + + // === Tuple === // + let value = MyEnum::B(123, 321); + assert_eq!(2, value.field_len()); + let mut iter = value.iter_fields(); + assert!(iter + .next() + .and_then(|field| field.reflect_partial_eq(&123_usize)) + .unwrap_or_default()); + assert!(iter + .next() + .and_then(|field| field.reflect_partial_eq(&321_i32)) + .unwrap_or_default()); + + // === Struct === // + let value = 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.reflect_partial_eq(&1.23_f32)) + .unwrap_or_default()); + assert!(iter + .next() + .and_then(|field| field.reflect_partial_eq(&true)) + .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); + } +} diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index d722ca8c11614..57bdbf1abed7c 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -768,7 +768,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}; @@ -849,4 +851,63 @@ mod tests { let b: &dyn Reflect = &Some(123); 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) { + value + .field_at_mut(0) + .and_then(|field| field.downcast_mut::()) + .map(|field| *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`"); + } + } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index bdfd2ee945e27..17d6519f86812 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -208,78 +208,6 @@ mod tests { assert_eq!(4, *iter.next().unwrap().downcast_ref::().unwrap()); } - #[test] - #[allow(unused_variables)] - fn reflect_enum() { - #[derive(Reflect, Debug, PartialEq)] - enum Foo { - A, - B(usize), - C { value: f32 }, - } - - // === Foo === // - let mut foo = Foo::B(123); - assert_eq!("B", foo.variant_name()); - assert_eq!(1, foo.field_len()); - - if foo.is_variant(VariantType::Tuple) { - foo.field_at_mut(0) - .and_then(|field| field.downcast_mut::()) - .map(|field| *field = 321); - } else { - panic!("expected `VariantType::Tuple`") - } - - assert_eq!(Foo::B(321), foo); - - let mut new_value = DynamicEnum::from(Foo::C { value: 1.23 }); - foo.apply(&new_value); - assert_eq!(Foo::C { value: 1.23 }, foo); - - new_value.set_variant("A", DynamicVariant::Unit); - foo.apply(&new_value); - assert_eq!(Foo::A, foo); - - // === Option === // - 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) { - value - .field_at_mut(0) - .and_then(|field| field.downcast_mut::()) - .map(|field| *field = 321); - } else { - panic!("expected `VariantType::Tuple`") - } - - assert_eq!(Some(321), value); - } - - #[test] - fn dynamic_enum() { - // Some(usize) -> None - let mut value = Some(123usize); - let mut dyn_enum = DynamicEnum::from::>(None); - value.apply(&dyn_enum); - assert_eq!(None, value); - - // None -> Some(usize) - let data = (321usize,).clone_dynamic(); - dyn_enum.set_variant("Some".to_string(), DynamicVariant::Tuple(data)); - value.apply(&dyn_enum); - assert_eq!(Some(321), value); - } - #[test] #[should_panic(expected = "the given key does not support hashing")] fn reflect_map_no_hash() { @@ -749,86 +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::()); - - // TODO: Uncomment this section when deriving Reflect for enum is re-enabled - // Enum - // #[derive(Reflect)] - // enum MyEnum { - // A, - // B(usize, isize), - // C { foo: String }, - // } - // - // let info = MyEnum::type_info(); - // if let TypeInfo::Enum(info) = info { - // assert!(info.id().is::()); - // assert_eq!(std::any::type_name::(), info.id().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().id().is::()); - // assert!(variant.field_at(1).unwrap().id().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().id().is::()); - // assert!(variant.field("foo").unwrap().id().is::()); - // } else { - // panic!("Expected `VariantInfo::Struct`"); - // } - // } else { - // panic!("Expected `TypeInfo::Enum`"); - // } - - // Option (Enum) - 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`"); - } - // Tuple type MyTuple = (u32, f32, String); From 32ec8b3d61f7342ac405241f90d6ce101ba382b1 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 18:14:17 -0700 Subject: [PATCH 32/75] Added ability to ignore fields --- .../bevy_reflect_derive/src/impls/enums.rs | 34 +++++++++++--- crates/bevy_reflect/src/enums/mod.rs | 46 +++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index de3976ffc93ae..3f8704a1095a1 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -241,7 +241,7 @@ struct EnumImpls { enum_apply: Vec, } -fn filter_active(field: &StructField) -> bool { +fn filter_active(field: &&StructField) -> bool { !field.attrs.ignore } @@ -278,7 +278,6 @@ fn generate_impls( let name = ident.to_string(); let unit = reflect_enum.get_unit(ident); - // TODO: Filter by active fields match &variant.fields { EnumVariantFields::Unit => { variant_info.push(quote! { @@ -301,7 +300,16 @@ fn generate_impls( EnumVariantFields::Unnamed(fields) => { let mut field_info = Vec::new(); let mut variant_apply = Vec::new(); - for (field_idx, field) in fields.iter().enumerate() { + let mut field_idx: usize = 0; + for field in fields.iter().filter(filter_active) { + if field.attrs.ignore { + // Ignored field -> use default value + variant_apply.push(quote! { + Default::default() + }); + continue; + } + let empties = underscores(field_idx); enum_field_at.push(quote! { #unit( #empties value, .. ) if #ref_index == #field_idx => Some(value) @@ -326,9 +334,11 @@ fn generate_impls( .take::<#field_ty>() .expect(#expect_type) }); + + field_idx += 1; } - let field_len = fields.len(); + let field_len = field_idx; enum_field_len.push(quote! { #unit(..) => #field_len }); @@ -354,8 +364,18 @@ fn generate_impls( EnumVariantFields::Named(fields) => { let mut field_info = Vec::new(); let mut variant_apply = Vec::new(); - for (field_idx, field) in fields.iter().enumerate() { + let mut field_idx: usize = 0; + for field in fields.iter() { let field_ident = field.data.ident.as_ref().unwrap(); + + if field.attrs.ignore { + // Ignored field -> use default value + variant_apply.push(quote! { + #field_ident: Default::default() + }); + continue; + } + let field_name = field_ident.to_string(); enum_field.push(quote! { #unit{ #field_ident, .. } if #ref_name == #field_name => Some(#field_ident) @@ -389,9 +409,11 @@ fn generate_impls( .take::<#field_ty>() .expect(#expect_type) }); + + field_idx += 1; } - let field_len = fields.len(); + let field_len = field_idx; enum_field_len.push(quote! { #unit{..} => #field_len }); diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index f170dbe37214c..b9b5f6976ebde 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -256,4 +256,50 @@ mod tests { 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().id().is::()); + } else { + panic!("expected `VariantInfo::Struct`"); + } + } else { + panic!("expected `TypeInfo::Enum`"); + } + } } From 84a2bab8507395fde8d2eae815bde5c8676e3763 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 18:15:19 -0700 Subject: [PATCH 33/75] Silence warning --- crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs | 1 + 1 file changed, 1 insertion(+) 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 4f95c86003152..d80cfc4244b89 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -94,6 +94,7 @@ pub(crate) struct EnumVariant<'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, } From 6464f155de6cc644e4bf1547e2d16880d6924955 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 18:30:16 -0700 Subject: [PATCH 34/75] Cleanup --- .../bevy_reflect_derive/src/impls/enums.rs | 21 +++------------- .../bevy_reflect_derive/src/utility.rs | 25 +++++++++++++++++++ crates/bevy_reflect/src/enums/mod.rs | 14 ++++++++--- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 3f8704a1095a1..ef8e246a84009 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,5 +1,6 @@ -use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField}; +use crate::derive_data::{EnumVariantFields, ReflectEnum}; use crate::impls::impl_typed; +use crate::utility; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; use quote::{quote, ToTokens}; @@ -241,20 +242,6 @@ struct EnumImpls { enum_apply: Vec, } -fn filter_active(field: &&StructField) -> bool { - !field.attrs.ignore -} - -fn underscores(count: usize) -> proc_macro2::TokenStream { - let mut output = quote! {}; - for _ in 0..count { - output = quote! { - #output _, - } - } - output -} - fn generate_impls( reflect_enum: &ReflectEnum, ref_index: &Ident, @@ -301,7 +288,7 @@ fn generate_impls( let mut field_info = Vec::new(); let mut variant_apply = Vec::new(); let mut field_idx: usize = 0; - for field in fields.iter().filter(filter_active) { + for field in fields.iter() { if field.attrs.ignore { // Ignored field -> use default value variant_apply.push(quote! { @@ -310,7 +297,7 @@ fn generate_impls( continue; } - let empties = underscores(field_idx); + let empties = utility::underscores(field_idx); enum_field_at.push(quote! { #unit( #empties value, .. ) if #ref_index == #field_idx => Some(value) }); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 0424a9b60b182..84e46d015d95f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -2,6 +2,7 @@ use bevy_macro_utils::BevyManifest; use proc_macro2::{Ident, Span}; +use quote::quote; use syn::Path; /// Returns the correct path for `bevy_reflect`. @@ -22,6 +23,30 @@ pub(crate) fn get_reflect_ident(name: &str) -> Ident { Ident::new(&reflected, Span::call_site()) } +/// Returns a token stream of comma-separated underscores for the given count. +/// +/// This is useful for creating tuple field accessors: +/// +/// ```ignore +/// let empties = underscores(2); +/// quote! { +/// let (#empties value, ..) = (10, 20, 30); +/// assert_eq!(30, value); +/// } +/// ``` +/// +/// > Note: This automatically handles the trailing comma. +/// +pub(crate) fn underscores(count: usize) -> proc_macro2::TokenStream { + let mut output = proc_macro2::TokenStream::new(); + for _ in 0..count { + output = quote! { + #output _, + } + } + output +} + /// Helper struct used to process an iterator of `Result, syn::Error>`, /// combining errors into one along the way. pub(crate) struct ResultSifter { diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index b9b5f6976ebde..bedb6d672409b 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -269,7 +269,11 @@ mod tests { } if let TypeInfo::Enum(info) = TestEnum::type_info() { - assert_eq!(2, info.variant_len(), "expected one of the variants to be ignored"); + 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 { @@ -293,8 +297,12 @@ mod tests { 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().id().is::()); + 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`"); } From 1360b5be8cba115f9518221a223a498c3a45bd52 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 22:17:42 -0700 Subject: [PATCH 35/75] Added default hash function --- .../bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index ef8e246a84009..fe5b3134ae59b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -28,7 +28,14 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let hash_fn = reflect_enum .meta() .traits() - .get_hash_impl(bevy_reflect_path); + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_hash(&self) -> Option { + #bevy_reflect_path::enum_hash(self) + } + } + }); let partial_eq_fn = reflect_enum .meta() .traits() From 4550e99c444152438190fa4424e018a59ac74f8b Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 22:19:00 -0700 Subject: [PATCH 36/75] Genericized constructor --- crates/bevy_reflect/src/enums/dynamic_enum.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 0a25a8068816e..9e486165e7008 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -87,11 +87,11 @@ impl DynamicEnum { /// * `variant_name`: The name of the variant to set /// * `variant`: The variant data /// - pub fn new>(name: I, variant_name: I, variant: DynamicVariant) -> Self { + pub fn new, V: Into>(name: I, variant_name: I, variant: V) -> Self { Self { name: name.into(), variant_name: variant_name.into(), - variant, + variant: variant.into(), } } From 99391218ef37dec022859c99f70212aaf0b5ef5c Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 23:23:19 -0700 Subject: [PATCH 37/75] Use FromReflect to generate new variants --- .../bevy_reflect_derive/src/impls/enums.rs | 22 +++++----- crates/bevy_reflect/src/enums/mod.rs | 44 +++++++++++++++++++ 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index fe5b3134ae59b..4bf02196b9e19 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -321,12 +321,12 @@ fn generate_impls( field_ty.to_token_stream().to_string() ); variant_apply.push(quote! { - #ref_value - .field_at(#field_idx) - .expect(#expect_field) - .clone_value() - .take::<#field_ty>() - .expect(#expect_type) + #bevy_reflect_path::FromReflect::from_reflect( + #ref_value + .field_at(#field_idx) + .expect(#expect_field) + ) + .expect(#expect_type) }); field_idx += 1; @@ -396,11 +396,11 @@ fn generate_impls( field_ty.to_token_stream().to_string() ); variant_apply.push(quote! { - #field_ident: #ref_value - .field(#field_name) - .expect(#expect_field) - .clone_value() - .take::<#field_ty>() + #field_ident: #bevy_reflect_path::FromReflect::from_reflect( + #ref_value + .field(#field_name) + .expect(#expect_field) + ) .expect(#expect_type) }); diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index bedb6d672409b..176404e2a52c5 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -310,4 +310,48 @@ mod tests { panic!("expected `TypeInfo::Enum`"); } } + + #[test] + fn enum_should_respect_complex_fields() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B(TestStruct), + C { value: T }, + } + + #[derive(Reflect, FromReflect, Debug, PartialEq)] + struct TestStruct(usize); + + 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; + + // === Struct Value === // + 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); + + // === Generic Value === // + 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); + } } From 79a9a74d388341ef13702bcc3fec697e76163eca Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 15 May 2022 23:46:32 -0700 Subject: [PATCH 38/75] Split tests --- crates/bevy_reflect/src/enums/mod.rs | 66 +++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 176404e2a52c5..07ee26e1c7a62 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -312,20 +312,17 @@ mod tests { } #[test] - fn enum_should_respect_complex_fields() { + fn enum_should_allow_generics() { #[derive(Reflect, Debug, PartialEq)] enum TestEnum { A, - B(TestStruct), + B(T), C { value: T }, } - #[derive(Reflect, FromReflect, Debug, PartialEq)] - struct TestStruct(usize); - if let TypeInfo::Enum(info) = TestEnum::::type_info() { if let VariantInfo::Tuple(variant) = info.variant("B").unwrap() { - assert!(variant.field_at(0).unwrap().is::()); + assert!(variant.field_at(0).unwrap().is::()); } else { panic!("expected `VariantInfo::Struct`"); } @@ -340,18 +337,67 @@ mod tests { let mut value = TestEnum::::A; - // === Struct Value === // + // === Tuple === // let mut data = DynamicTuple::default(); - data.insert(TestStruct(123)); + data.insert(1.23_f32); let dyn_enum = DynamicEnum::new(std::any::type_name::>(), "B", data); value.apply(&dyn_enum); - assert_eq!(TestEnum::B(TestStruct(123)), value); + assert_eq!(TestEnum::B(1.23), value); - // === Generic 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); + + 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(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 + ); + } } From dbd3d1550138075a80ea253dbeb29b52d81f6921 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 16 May 2022 00:49:52 -0700 Subject: [PATCH 39/75] Generate FromReflect for enums --- .../bevy_reflect_derive/src/enum_utility.rs | 127 ++++++++++++++++++ .../bevy_reflect_derive/src/from_reflect.rs | 31 +++++ .../bevy_reflect_derive/src/impls/enums.rs | 84 +++--------- .../bevy_reflect_derive/src/lib.rs | 3 +- crates/bevy_reflect/src/enums/mod.rs | 53 ++++++++ crates/bevy_reflect/src/impls/std.rs | 31 ++++- 6 files changed, 258 insertions(+), 71 deletions(-) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs 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..d2b5328f2afc0 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -0,0 +1,127 @@ +use crate::derive_data::{EnumVariantFields, ReflectEnum}; +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 mut variant_names: Vec = Vec::new(); + let mut variant_constructors: Vec = 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); + + match &variant.fields { + EnumVariantFields::Unit => { + variant_constructors.push(quote! { + #unit + }); + } + EnumVariantFields::Unnamed(fields) => { + let mut variant_apply = Vec::new(); + let mut field_idx: usize = 0; + for field in fields.iter() { + if field.attrs.ignore { + // Ignored field -> use default value + variant_apply.push(quote! { + Default::default() + }); + continue; + } + + let field_ty = &field.data.ty; + let expect_field = format!("field at index `{}` should exist", field_idx); + let expect_type = format!( + "field at index `{}` should be of type `{}`", + field_idx, + field_ty.to_token_stream().to_string() + ); + + let unwrapper = if can_panic { + quote!(.expect(#expect_type)) + } else { + quote!(?) + }; + + variant_apply.push(quote! { + #bevy_reflect_path::FromReflect::from_reflect( + #ref_value + .field_at(#field_idx) + .expect(#expect_field) + ) + #unwrapper + }); + + field_idx += 1; + } + + variant_constructors.push(quote! { + #unit( #(#variant_apply),* ) + }); + } + EnumVariantFields::Named(fields) => { + let mut variant_apply = Vec::new(); + for field in fields.iter() { + let field_ident = field.data.ident.as_ref().unwrap(); + + if field.attrs.ignore { + // Ignored field -> use default value + variant_apply.push(quote! { + #field_ident: Default::default() + }); + continue; + } + + let field_name = field_ident.to_string(); + let field_ty = &field.data.ty; + let expect_field = format!("field with name `{}` should exist", field_name); + let expect_type = format!( + "field with name `{}` should be of type `{}`", + field_name, + field_ty.to_token_stream().to_string() + ); + + let unwrapper = if can_panic { + quote!(.expect(#expect_type)) + } else { + quote!(?) + }; + + variant_apply.push(quote! { + #field_ident: #bevy_reflect_path::FromReflect::from_reflect( + #ref_value + .field(#field_name) + .expect(#expect_field) + ) + #unwrapper + }); + } + + variant_constructors.push(quote! { + #unit{ #(#variant_apply),* } + }); + } + } + + 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 19c0dcfba3b1e..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,4 +1,6 @@ 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::{ReflectMeta, ReflectStruct}; use proc_macro::TokenStream; @@ -30,6 +32,35 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { }) } +/// 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); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 4bf02196b9e19..445b997bef319 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,9 +1,10 @@ use crate::derive_data::{EnumVariantFields, ReflectEnum}; +use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::impls::impl_typed; use crate::utility; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; -use quote::{quote, ToTokens}; +use quote::quote; pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); @@ -22,8 +23,12 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { enum_field_len, enum_variant_name, enum_variant_type, - enum_apply, - } = generate_impls(reflect_enum, &ref_index, &ref_name, &ref_value); + } = 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() @@ -213,8 +218,10 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } else { // New variant -> perform a switch match #ref_value.variant_name() { - #(#enum_apply,)* - x => panic!("Variant named `{}` does not exist on enum `{}`", x, std::any::type_name::()), + #(#variant_names => { + *self = #variant_constructors + })* + name => panic!("variant with name `{}` does not exist on enum `{}`", name, std::any::type_name::()), } } } else { @@ -246,15 +253,9 @@ struct EnumImpls { enum_field_len: Vec, enum_variant_name: Vec, enum_variant_type: Vec, - enum_apply: Vec, } -fn generate_impls( - reflect_enum: &ReflectEnum, - ref_index: &Ident, - ref_name: &Ident, - ref_value: &Ident, -) -> EnumImpls { +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 = Vec::new(); @@ -265,7 +266,6 @@ fn generate_impls( let mut enum_field_len: Vec = Vec::new(); let mut enum_variant_name: Vec = Vec::new(); let mut enum_variant_type: Vec = Vec::new(); - let mut enum_apply: Vec = Vec::new(); for variant in reflect_enum.active_variants() { let ident = &variant.data.ident; @@ -285,22 +285,13 @@ fn generate_impls( enum_variant_type.push(quote! { #unit => #bevy_reflect_path::VariantType::Unit }); - enum_apply.push(quote! { - #name => { - *self = #unit; - } - }); } EnumVariantFields::Unnamed(fields) => { let mut field_info = Vec::new(); - let mut variant_apply = Vec::new(); let mut field_idx: usize = 0; for field in fields.iter() { if field.attrs.ignore { - // Ignored field -> use default value - variant_apply.push(quote! { - Default::default() - }); + // Ignored field continue; } @@ -314,21 +305,6 @@ fn generate_impls( #bevy_reflect_path::UnnamedField::new::<#field_ty>(#field_idx) }); - let expect_field = format!("field at index `{}` should exist", field_idx); - let expect_type = format!( - "field at index `{}` should be of type `{}`", - field_idx, - field_ty.to_token_stream().to_string() - ); - variant_apply.push(quote! { - #bevy_reflect_path::FromReflect::from_reflect( - #ref_value - .field_at(#field_idx) - .expect(#expect_field) - ) - .expect(#expect_type) - }); - field_idx += 1; } @@ -342,11 +318,6 @@ fn generate_impls( enum_variant_type.push(quote! { #unit(..) => #bevy_reflect_path::VariantType::Tuple }); - enum_apply.push(quote! { - #name => { - *self = #unit( #(#variant_apply),* ); - } - }); variant_info.push(quote! { #bevy_reflect_path::VariantInfo::Tuple( #bevy_reflect_path::TupleVariantInfo::new(#name, &[ @@ -357,16 +328,12 @@ fn generate_impls( } EnumVariantFields::Named(fields) => { let mut field_info = Vec::new(); - let mut variant_apply = Vec::new(); let mut field_idx: usize = 0; for field in fields.iter() { let field_ident = field.data.ident.as_ref().unwrap(); if field.attrs.ignore { - // Ignored field -> use default value - variant_apply.push(quote! { - #field_ident: Default::default() - }); + // Ignored field continue; } @@ -389,21 +356,6 @@ fn generate_impls( #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) }); - let expect_field = format!("field with name `{}` should exist", field_name); - let expect_type = format!( - "field with name `{}` should be of type `{}`", - field_name, - field_ty.to_token_stream().to_string() - ); - variant_apply.push(quote! { - #field_ident: #bevy_reflect_path::FromReflect::from_reflect( - #ref_value - .field(#field_name) - .expect(#expect_field) - ) - .expect(#expect_type) - }); - field_idx += 1; } @@ -417,11 +369,6 @@ fn generate_impls( enum_variant_type.push(quote! { #unit{..} => #bevy_reflect_path::VariantType::Struct }); - enum_apply.push(quote! { - #name => { - *self = #unit{ #(#variant_apply),* }; - } - }); variant_info.push(quote! { #bevy_reflect_path::VariantInfo::Struct( #bevy_reflect_path::StructVariantInfo::new(#name, &[ @@ -441,7 +388,6 @@ fn generate_impls( enum_name_at, enum_field_len, enum_variant_name, - enum_apply, enum_variant_type, } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 9c65984011f44..7b90f952cee18 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; @@ -73,8 +74,8 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream { ReflectDerive::Struct(struct_data) => from_reflect::impl_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), - _ => todo!(), } } diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 07ee26e1c7a62..3b4c67c77207e 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -400,4 +400,57 @@ mod tests { 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 }, + } + + 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(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 + ); + } } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 57bdbf1abed7c..ef056ec52a9f7 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -57,7 +57,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); @@ -724,6 +723,36 @@ impl Reflect for Option { } } +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(); From 5d721703f643d4e600a23c2d3d517320f0000206 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 16 May 2022 00:51:35 -0700 Subject: [PATCH 40/75] Remove redundant tests --- crates/bevy_reflect/src/enums/mod.rs | 30 ---------------------------- 1 file changed, 30 deletions(-) diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 3b4c67c77207e..8aa8a3a977f96 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -364,21 +364,6 @@ mod tests { #[derive(Reflect, FromReflect, Debug, PartialEq)] struct TestStruct(usize); - 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 === // @@ -417,21 +402,6 @@ mod tests { C { value: f32 }, } - 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 === // From feb5ed49303fcc2d2efdf35bc1818eafa7ac0ac6 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 17 May 2022 23:04:37 -0700 Subject: [PATCH 41/75] Add VariantField This grants easy access to the field's name, if needed --- .../bevy_reflect_derive/src/derive_data.rs | 2 +- .../bevy_reflect_derive/src/impls/enums.rs | 8 +++--- crates/bevy_reflect/src/enums/dynamic_enum.rs | 20 +++++++------ crates/bevy_reflect/src/enums/enum_trait.rs | 28 +++++++++++++++++-- crates/bevy_reflect/src/enums/helpers.rs | 12 ++++---- crates/bevy_reflect/src/enums/mod.rs | 20 ++++++++----- crates/bevy_reflect/src/impls/std.rs | 2 +- 7 files changed, 62 insertions(+), 30 deletions(-) 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 d80cfc4244b89..dfbedecafc1fa 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,6 +1,6 @@ -use quote::quote; use crate::container_attributes::ReflectTraits; use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; +use quote::quote; use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; use syn::punctuated::Punctuated; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 445b997bef319..29836221b0449 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -203,14 +203,14 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { // Same variant -> just update fields match #ref_value.variant_type() { #bevy_reflect_path::VariantType::Struct => { - for (index, field) in #ref_value.iter_fields().enumerate() { - let name = #ref_value.name_at(index).unwrap(); - #bevy_reflect_path::Enum::field_mut(self, name).map(|v| v.apply(field)); + 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)); + #bevy_reflect_path::Enum::field_at_mut(self, index).map(|v| v.apply(field.value())); } } _ => {} diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 9e486165e7008..1b697995dbffc 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -87,7 +87,11 @@ impl DynamicEnum { /// * `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 { + pub fn new, V: Into>( + name: I, + variant_name: I, + variant: V, + ) -> Self { Self { name: name.into(), variant_name: variant_name.into(), @@ -131,7 +135,7 @@ impl DynamicEnum { VariantType::Tuple => { let mut data = DynamicTuple::default(); for field in value.iter_fields() { - data.insert_boxed(field.clone_value()); + data.insert_boxed(field.value().clone_value()); } DynamicEnum::new( value.type_name(), @@ -141,9 +145,9 @@ impl DynamicEnum { } VariantType::Struct => { let mut data = DynamicStruct::default(); - for (index, field) in value.iter_fields().enumerate() { - let name = value.name_at(index).unwrap(); - data.insert_boxed(name, field.clone_value()); + for field in value.iter_fields() { + let name = field.name().unwrap(); + data.insert_boxed(name, field.value().clone_value()); } DynamicEnum::new( value.type_name(), @@ -276,10 +280,10 @@ impl Reflect for DynamicEnum { #[inline] fn apply(&mut self, value: &dyn Reflect) { if let ReflectRef::Enum(enum_value) = value.reflect_ref() { - for (i, value) in enum_value.iter_fields().enumerate() { - let name = enum_value.name_at(i).unwrap(); + for field in enum_value.iter_fields() { + let name = field.name().unwrap(); if let Some(v) = self.field_mut(name) { - v.apply(value); + v.apply(field.value()); } } } else { diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index b22513a14f82a..1160222fad2d2 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -228,15 +228,15 @@ impl<'a> VariantFieldIter<'a> { } impl<'a> Iterator for VariantFieldIter<'a> { - type Item = &'a dyn Reflect; + type Item = VariantField<'a>; fn next(&mut self) -> Option { let value = match self.container.variant_type() { VariantType::Unit => None, - VariantType::Tuple => self.container.field_at(self.index), + VariantType::Tuple => Some(VariantField::Tuple(self.container.field_at(self.index)?)), VariantType::Struct => { let name = self.container.name_at(self.index)?; - self.container.field(name) + Some(VariantField::Struct(name, self.container.field(name)?)) } }; self.index += 1; @@ -250,3 +250,25 @@ impl<'a> Iterator for VariantFieldIter<'a> { } 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::Tuple(value) => *value, + Self::Struct(.., value) => *value, + } + } +} diff --git a/crates/bevy_reflect/src/enums/helpers.rs b/crates/bevy_reflect/src/enums/helpers.rs index 7e007521c5d55..03c36a5822367 100644 --- a/crates/bevy_reflect/src/enums/helpers.rs +++ b/crates/bevy_reflect/src/enums/helpers.rs @@ -9,7 +9,7 @@ pub fn enum_hash(value: &TEnum) -> Option { value.variant_name().hash(&mut hasher); value.variant_type().hash(&mut hasher); for field in value.iter_fields() { - hasher.write_u64(field.reflect_hash()?); + hasher.write_u64(field.value().reflect_hash()?); } Some(hasher.finish()) } @@ -54,10 +54,10 @@ pub fn enum_partial_eq(a: &TEnum, b: &dyn Reflect) -> Option match a.variant_type() { VariantType::Struct => { // Same struct fields? - for (i, value) in a.iter_fields().enumerate() { - let field_name = a.name_at(i).unwrap(); + 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(value) { + if let Some(false) | None = field_value.reflect_partial_eq(field.value()) { // Fields failed comparison return Some(false); } @@ -70,9 +70,9 @@ pub fn enum_partial_eq(a: &TEnum, b: &dyn Reflect) -> Option } VariantType::Tuple => { // Same tuple fields? - for (i, value) in a.iter_fields().enumerate() { + 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(value) { + if let Some(false) | None = field_value.reflect_partial_eq(field.value()) { // Fields failed comparison return Some(false); } diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 8aa8a3a977f96..caeff1abf0868 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -168,26 +168,26 @@ mod tests { #[test] fn enum_should_iterate_fields() { // === Unit === // - let value = MyEnum::A; + 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 = MyEnum::B(123, 321); + 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.reflect_partial_eq(&123_usize)) + .and_then(|field| field.value().reflect_partial_eq(&123_usize)) .unwrap_or_default()); assert!(iter .next() - .and_then(|field| field.reflect_partial_eq(&321_i32)) + .and_then(|field| field.value().reflect_partial_eq(&321_i32)) .unwrap_or_default()); // === Struct === // - let value = MyEnum::C { + let value: &dyn Enum = &MyEnum::C { foo: 1.23, bar: true, }; @@ -195,11 +195,17 @@ mod tests { let mut iter = value.iter_fields(); assert!(iter .next() - .and_then(|field| field.reflect_partial_eq(&1.23_f32)) + .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.reflect_partial_eq(&true)) + .and_then(|field| field + .value() + .reflect_partial_eq(&true) + .and(field.name().map(|name| name == "bar"))) .unwrap_or_default()); } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index ef056ec52a9f7..7441f39f14c69 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -668,7 +668,7 @@ impl Reflect for Option { // Same variant -> just update fields for (index, field) in value.iter_fields().enumerate() { let name = value.name_at(index).unwrap(); - self.field_mut(name).map(|v| v.apply(field)); + self.field_mut(name).map(|v| v.apply(field.value())); } } else { // New variant -> perform a switch From eb861a80b64eb328dc09213860050f1ada0d89fa Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 29 May 2022 15:18:33 -0700 Subject: [PATCH 42/75] Remove unused imports --- crates/bevy_reflect/src/enums/enum_trait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 1160222fad2d2..2a57f1bdb20c3 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,4 +1,4 @@ -use crate::{DynamicEnum, Reflect, ReflectRef, Struct, Tuple, VariantInfo, VariantType}; +use crate::{DynamicEnum, Reflect, VariantInfo, VariantType}; use bevy_utils::HashMap; use std::any::{Any, TypeId}; use std::borrow::Cow; From ef0ef4cff734202fb8f9704161a1f03653344a8e Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 29 May 2022 15:23:56 -0700 Subject: [PATCH 43/75] Fix some clippy errors --- .../bevy_reflect_derive/src/enum_utility.rs | 4 ++-- .../bevy_reflect_derive/src/impls/enums.rs | 2 +- crates/bevy_reflect/src/impls/std.rs | 10 +++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs index d2b5328f2afc0..64ee3bbdc8396 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -48,7 +48,7 @@ pub(crate) fn get_variant_constructors( let expect_type = format!( "field at index `{}` should be of type `{}`", field_idx, - field_ty.to_token_stream().to_string() + field_ty.to_token_stream() ); let unwrapper = if can_panic { @@ -92,7 +92,7 @@ pub(crate) fn get_variant_constructors( let expect_type = format!( "field with name `{}` should be of type `{}`", field_name, - field_ty.to_token_stream().to_string() + field_ty.to_token_stream() ); let unwrapper = if can_panic { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 29836221b0449..7646f4569b4e6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -117,7 +117,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } fn iter_fields(&self) -> #bevy_reflect_path::VariantFieldIter { - VariantFieldIter::new(self) + #bevy_reflect_path::VariantFieldIter::new(self) } #[inline] diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 7441f39f14c69..1a5bf56e2cb60 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -668,7 +668,9 @@ impl Reflect for Option { // Same variant -> just update fields for (index, field) in value.iter_fields().enumerate() { let name = value.name_at(index).unwrap(); - self.field_mut(name).map(|v| v.apply(field.value())); + if let Some(v) = self.field_mut(name) { + v.apply(field.value()); + } } } else { // New variant -> perform a switch @@ -896,10 +898,12 @@ mod tests { assert_eq!("core::option::Option::Some", value.variant_path()); if value.is_variant(VariantType::Tuple) { - value + if let Some(field) = value .field_at_mut(0) .and_then(|field| field.downcast_mut::()) - .map(|field| *field = 321); + { + *field = 321 + } } else { panic!("expected `VariantType::Tuple`") } From 703592662b3fa1a8b0e65b67c61f4f2dee813760 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 29 May 2022 15:38:15 -0700 Subject: [PATCH 44/75] Fix unused variable warning on reflected enums --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 4 ++-- crates/bevy_reflect/src/enums/mod.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 7646f4569b4e6..fdb329234a181 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -345,10 +345,10 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden #unit{ #field_ident, .. } if #ref_index == #field_idx => Some(#field_ident) }); enum_index_of.push(quote! { - #unit{ #field_ident, .. } if #ref_name == #field_name => Some(#field_idx) + #unit{ .. } if #ref_name == #field_name => Some(#field_idx) }); enum_name_at.push(quote! { - #unit{ #field_ident, .. } if #ref_index == #field_idx => Some(#field_name) + #unit{ .. } if #ref_index == #field_idx => Some(#field_name) }); let field_ty = &field.data.ty; diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index caeff1abf0868..1c9cc561fbcdf 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -9,7 +9,6 @@ pub use helpers::*; pub use variants::*; #[cfg(test)] -#[allow(unused_variables)] mod tests { use crate as bevy_reflect; use crate::*; From 2d7dce1ed5c1bb5b944a2b532f4206151abd98c6 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 29 May 2022 15:38:45 -0700 Subject: [PATCH 45/75] Implement Reflect for WindowId --- crates/bevy_render/src/camera/camera.rs | 3 ++- crates/bevy_render/src/camera/projection.rs | 11 ++++++----- crates/bevy_window/Cargo.toml | 1 + crates/bevy_window/src/window.rs | 4 +++- 4 files changed, 12 insertions(+), 7 deletions(-) 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); From 489e26de32e48cfb72aa12537a7dfd8d38177bfd Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 29 May 2022 15:54:11 -0700 Subject: [PATCH 46/75] Fix doc link --- crates/bevy_reflect/bevy_reflect_derive/src/utility.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 84e46d015d95f..3e2a88e03caa3 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -79,8 +79,6 @@ impl ResultSifter { } /// Associated method that provides a convenient implementation for [`Iterator::fold`]. - /// - /// [`Iterator::fold`]: core::iter::traits::iterator::Iterator::fold pub fn fold(mut sifter: Self, result: Result) -> Self { sifter.sift(result); sifter From 512e1a2354b151ef5bae505c2483ef73e6158d42 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 31 May 2022 22:34:56 -0700 Subject: [PATCH 47/75] Add enum_debug helper --- crates/bevy_reflect/src/enums/dynamic_enum.rs | 12 +++++- crates/bevy_reflect/src/enums/helpers.rs | 43 +++++++++++++++++++ crates/bevy_reflect/src/lib.rs | 23 ++++++++-- crates/bevy_reflect/src/reflect.rs | 3 +- 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 1b697995dbffc..1d753d1aacca8 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -1,9 +1,10 @@ use crate::utility::NonGenericTypeInfoCell; use crate::{ - enum_hash, enum_partial_eq, DynamicInfo, DynamicStruct, DynamicTuple, Enum, Reflect, - ReflectMut, ReflectRef, Struct, Tuple, TypeInfo, Typed, VariantFieldIter, VariantType, + 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 { @@ -321,6 +322,13 @@ impl Reflect for DynamicEnum { 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 { diff --git a/crates/bevy_reflect/src/enums/helpers.rs b/crates/bevy_reflect/src/enums/helpers.rs index 03c36a5822367..f297d655fd021 100644 --- a/crates/bevy_reflect/src/enums/helpers.rs +++ b/crates/bevy_reflect/src/enums/helpers.rs @@ -1,4 +1,5 @@ use crate::{Enum, Reflect, ReflectRef, VariantType}; +use std::fmt::Debug; use std::hash::{Hash, Hasher}; /// Returns the `u64` hash of the given [enum](Enum). @@ -86,3 +87,45 @@ pub fn enum_partial_eq(a: &TEnum, b: &dyn Reflect) -> Option _ => Some(false), } } + +/// 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: +/// +/// // MyEnum { +/// // foo: 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/lib.rs b/crates/bevy_reflect/src/lib.rs index 17d6519f86812..f6dbc37ce67f3 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -827,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, @@ -839,6 +841,13 @@ mod tests { foo: String, } + #[derive(Reflect)] + enum SomeEnum { + A, + B(usize), + C { value: i32 }, + } + #[derive(Reflect)] struct SomeTupleStruct(String); @@ -863,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, }; @@ -891,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 0176422fc1c26..3b3b7c274445a 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -1,5 +1,5 @@ use crate::{ - array_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, + 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, }; @@ -173,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()), } } From 79ed4d85b8b4362d6dabaecdc17e0862583d5d08 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 31 May 2022 22:37:37 -0700 Subject: [PATCH 48/75] Fix doc comment --- crates/bevy_reflect/src/enums/helpers.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_reflect/src/enums/helpers.rs b/crates/bevy_reflect/src/enums/helpers.rs index f297d655fd021..119305d550848 100644 --- a/crates/bevy_reflect/src/enums/helpers.rs +++ b/crates/bevy_reflect/src/enums/helpers.rs @@ -105,9 +105,9 @@ pub fn enum_partial_eq(a: &TEnum, b: &dyn Reflect) -> Option /// /// // Output: /// -/// // MyEnum { -/// // foo: 123, -/// // } +/// // B ( +/// // 123, +/// // ) /// ``` #[inline] pub fn enum_debug(dyn_enum: &dyn Enum, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { From 4928c1ca802810a8eb6b6c5b44d3caa60e143200 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 31 May 2022 22:49:43 -0700 Subject: [PATCH 49/75] Add debug impl to enum derive --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index fdb329234a181..6967aea73bf9c 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -41,6 +41,11 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } }); + let debug_fn = reflect_enum + .meta() + .traits() + .get_debug_impl(); + let partial_eq_fn = reflect_enum .meta() .traits() @@ -240,6 +245,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { #hash_fn #partial_eq_fn + + #debug_fn } }) } From 812f93f838ad2c83c5b7e7e79bcd58160152c9e8 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 31 May 2022 23:02:56 -0700 Subject: [PATCH 50/75] Create default Enum impls --- .../bevy_reflect_derive/src/impls/enums.rs | 75 +---------------- crates/bevy_reflect/src/enums/enum_trait.rs | 84 +++++++++++++++---- crates/bevy_reflect/src/enums/variants.rs | 7 ++ crates/bevy_reflect/src/impls/std.rs | 36 ++++---- 4 files changed, 96 insertions(+), 106 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 6967aea73bf9c..7f2d3ad5ba578 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -18,11 +18,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { 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 { @@ -41,11 +37,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } }); - let debug_fn = reflect_enum - .meta() - .traits() - .get_debug_impl(); - + let debug_fn = reflect_enum.meta().traits().get_debug_impl(); let partial_eq_fn = reflect_enum .meta() .traits() @@ -107,32 +99,10 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } - 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 { @@ -141,14 +111,6 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } - #[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) } @@ -255,11 +217,7 @@ 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 { @@ -268,11 +226,7 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden let mut variant_info: Vec = Vec::new(); let mut enum_field: Vec = Vec::new(); let mut enum_field_at: Vec = Vec::new(); - let mut enum_index_of: Vec = Vec::new(); - let mut enum_name_at: Vec = Vec::new(); - let mut enum_field_len: Vec = Vec::new(); let mut enum_variant_name: Vec = Vec::new(); - let mut enum_variant_type: Vec = Vec::new(); for variant in reflect_enum.active_variants() { let ident = &variant.data.ident; @@ -289,9 +243,6 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden enum_variant_name.push(quote! { #unit => #name }); - enum_variant_type.push(quote! { - #unit => #bevy_reflect_path::VariantType::Unit - }); } EnumVariantFields::Unnamed(fields) => { let mut field_info = Vec::new(); @@ -315,16 +266,9 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden field_idx += 1; } - let field_len = field_idx; - 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::Tuple - }); variant_info.push(quote! { #bevy_reflect_path::VariantInfo::Tuple( #bevy_reflect_path::TupleVariantInfo::new(#name, &[ @@ -351,12 +295,6 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden enum_field_at.push(quote! { #unit{ #field_ident, .. } if #ref_index == #field_idx => Some(#field_ident) }); - enum_index_of.push(quote! { - #unit{ .. } if #ref_name == #field_name => Some(#field_idx) - }); - enum_name_at.push(quote! { - #unit{ .. } if #ref_index == #field_idx => Some(#field_name) - }); let field_ty = &field.data.ty; field_info.push(quote! { @@ -366,16 +304,9 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden field_idx += 1; } - let field_len = field_idx; - 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::Struct - }); variant_info.push(quote! { #bevy_reflect_path::VariantInfo::Struct( #bevy_reflect_path::StructVariantInfo::new(#name, &[ @@ -391,10 +322,6 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden 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/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 2a57f1bdb20c3..2f1ab8a638e02 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,7 +1,7 @@ -use crate::{DynamicEnum, Reflect, VariantInfo, VariantType}; +use crate::{DynamicEnum, Reflect, TypeInfo, VariantInfo, VariantType}; use bevy_utils::HashMap; use std::any::{Any, TypeId}; -use std::borrow::Cow; +use std::borrow::{Borrow, Cow}; use std::slice::Iter; /// A trait representing a [reflected] enum. @@ -93,24 +93,61 @@ pub trait Enum: Reflect { 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; + /// The type of the current variant. + fn variant_type(&self) -> VariantType { + if let TypeInfo::Enum(info) = self.get_type_info() { + if let Some(variant) = info.variant(self.variant_name()) { + variant.variant_type() + } else { + panic!("invalid variant: `{}`", self.variant_name()); + } + } else { + panic!( + "`{:?}` is not `TypeInfo::Enum`", + std::any::type_name::() + ); + } + } + /// 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> { + if let TypeInfo::Enum(info) = self.get_type_info() { + match info.variant(self.variant_name())? { + VariantInfo::Struct(variant) => { + variant.field_at(index).map(|field| field.name().borrow()) + } + _ => None, + } + } else { + panic!( + "`{:?}` is not `TypeInfo::Enum`", + std::any::type_name::() + ); + } + } + /// 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 { + if let TypeInfo::Enum(info) = self.get_type_info() { + match info.variant(self.variant_name())? { + VariantInfo::Struct(variant) => variant.index_of(name), + _ => None, + } + } else { + panic!( + "`{:?}` is not `TypeInfo::Enum`", + std::any::type_name::() + ); + } + } /// 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 @@ -119,6 +156,25 @@ pub trait Enum: Reflect { fn variant_path(&self) -> String { format!("{}::{}", self.type_name(), self.variant_name()) } + /// Returns the number of fields in the current variant. + fn field_len(&self) -> usize { + if let TypeInfo::Enum(info) = self.get_type_info() { + if let Some(variant) = info.variant(self.variant_name()) { + match variant { + VariantInfo::Unit(..) => 0, + VariantInfo::Tuple(variant_info) => variant_info.field_len(), + VariantInfo::Struct(variant_info) => variant_info.field_len(), + } + } else { + panic!("invalid variant: `{}`", self.variant_name()); + } + } else { + panic!( + "`{:?}` is not `TypeInfo::Enum`", + std::any::type_name::() + ); + } + } } /// A container for compile-time enum info. diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs index b1a9740d21c75..c17dd4db50db9 100644 --- a/crates/bevy_reflect/src/enums/variants.rs +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -73,6 +73,13 @@ impl VariantInfo { Self::Unit(info) => info.name(), } } + pub fn variant_type(&self) -> VariantType { + match self { + Self::Struct(..) => VariantType::Struct, + Self::Tuple(..) => VariantType::Tuple, + Self::Unit(..) => VariantType::Unit, + } + } } /// Type info for struct variants. diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 1a5bf56e2cb60..2670fd9f14cf4 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -586,26 +586,10 @@ impl Enum for Option { } } - 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 { @@ -614,6 +598,10 @@ impl Enum for Option { } } + fn clone_dynamic(&self) -> DynamicEnum { + DynamicEnum::from_ref::(self) + } + #[inline] fn variant_type(&self) -> VariantType { match self { @@ -622,8 +610,20 @@ impl Enum for Option { } } - fn clone_dynamic(&self) -> DynamicEnum { - DynamicEnum::from_ref::(self) + fn name_at(&self, _index: usize) -> Option<&str> { + None + } + + fn index_of(&self, _name: &str) -> Option { + None + } + + #[inline] + fn field_len(&self) -> usize { + match self { + Some(..) => 1, + None => 0, + } } } From dfbad2c8284e2fe56fd0e8acdac93cb4d2c3469b Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 31 May 2022 23:26:13 -0700 Subject: [PATCH 51/75] Fix clippy --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 7b90f952cee18..b9ff22da38d59 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -71,8 +71,9 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream { }; match derive_data { - ReflectDerive::Struct(struct_data) => from_reflect::impl_struct(&struct_data), - ReflectDerive::UnitStruct(struct_data) => from_reflect::impl_struct(&struct_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), From 1f18eafe3527f392ecb12c1b7a27ca953ab17757 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 1 Jun 2022 00:07:36 -0700 Subject: [PATCH 52/75] Fix more clippy --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index b9ff22da38d59..7c8c25acced0d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -46,8 +46,9 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { }; match derive_data { - ReflectDerive::Struct(struct_data) => impls::impl_struct(&struct_data), - ReflectDerive::UnitStruct(struct_data) => impls::impl_struct(&struct_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), From ba401b1b64207ed423130f7ee0588b29a42ea733 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 1 Jun 2022 00:17:55 -0700 Subject: [PATCH 53/75] More clippy fixes --- crates/bevy_reflect/src/enums/enum_trait.rs | 3 +-- crates/bevy_reflect/src/impls/std.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 2f1ab8a638e02..207b2418aa93c 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -323,8 +323,7 @@ impl<'a> VariantField<'a> { pub fn value(&self) -> &'a dyn Reflect { match self { - Self::Tuple(value) => *value, - Self::Struct(.., value) => *value, + Self::Struct(.., value) | Self::Tuple(value) => *value, } } } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 2670fd9f14cf4..8c513fb05c897 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -902,10 +902,10 @@ mod tests { .field_at_mut(0) .and_then(|field| field.downcast_mut::()) { - *field = 321 + *field = 321; } } else { - panic!("expected `VariantType::Tuple`") + panic!("expected `VariantType::Tuple`"); } assert_eq!(Some(321), value); From 00f13395e7ba4707b8cc5b6b177fa4005550edbb Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 4 Jun 2022 14:30:20 -0700 Subject: [PATCH 54/75] Add enum serialization --- crates/bevy_reflect/src/serde/mod.rs | 2 + crates/bevy_reflect/src/serde/ser.rs | 229 ++++++++++++++++++++++++++- 2 files changed, 225 insertions(+), 6 deletions(-) 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 73edf2f13e426..e9962b5ea51c7 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,14 +85,16 @@ 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, } .serialize(serializer), - ReflectRef::Enum(_value) => { - todo!() - } } } } @@ -201,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, @@ -369,3 +483,106 @@ 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; + + 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::(); + + // === Unit Variant === // + let value = MyEnum::Unit; + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, Default::default()).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, Default::default()).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, Default::default()).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, Default::default()).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); + } +} From dea4e0d5f972683428b615b94f805a51c72adfd6 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 4 Jun 2022 15:13:19 -0700 Subject: [PATCH 55/75] Fix unit variant partial_eq and added tests --- crates/bevy_reflect/src/enums/helpers.rs | 2 +- crates/bevy_reflect/src/enums/mod.rs | 71 ++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/enums/helpers.rs b/crates/bevy_reflect/src/enums/helpers.rs index 119305d550848..d7fe169cb4ecc 100644 --- a/crates/bevy_reflect/src/enums/helpers.rs +++ b/crates/bevy_reflect/src/enums/helpers.rs @@ -84,7 +84,7 @@ pub fn enum_partial_eq(a: &TEnum, b: &dyn Reflect) -> Option } Some(true) } - _ => Some(false), + _ => Some(true), } } diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 1c9cc561fbcdf..a32f134ae7c08 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -428,4 +428,75 @@ mod tests { value ); } + + #[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}}"); + } } From 9ae49e3599ffee0a1e83d9d391e69788615ffe99 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 4 Jun 2022 15:17:02 -0700 Subject: [PATCH 56/75] Add enum deserialization --- crates/bevy_reflect/src/serde/de.rs | 200 +++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 3 deletions(-) diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index 950208fbea8e3..132482ef76d2f 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_ref().map(|x| x.as_str()) { + 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_ref().map(|x| x.as_str()) { + 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()); + } +} From 9faa42012ae87eebf9801dfd7e4dcc8058d4a567 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 4 Jun 2022 15:18:03 -0700 Subject: [PATCH 57/75] Cleanup --- crates/bevy_reflect/src/enums/helpers.rs | 10 --- crates/bevy_reflect/src/enums/mod.rs | 82 ++++++++++++++---------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/crates/bevy_reflect/src/enums/helpers.rs b/crates/bevy_reflect/src/enums/helpers.rs index d7fe169cb4ecc..c14913d19dede 100644 --- a/crates/bevy_reflect/src/enums/helpers.rs +++ b/crates/bevy_reflect/src/enums/helpers.rs @@ -15,16 +15,6 @@ pub fn enum_hash(value: &TEnum) -> Option { Some(hasher.finish()) } -// TODO: Add serializable. How do we handle enums? -// pub fn enum_serialize(value: &TEnum, serializer: S) -> Result -// where -// TEnum: Enum + ?Sized, -// S: serde::Serializer, -// { -// -// -// } - /// Compares an [`Enum`] with a [`Reflect`] value. /// /// Returns true if and only if all of the following are true: diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index a32f134ae7c08..3364478d5c8aa 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -445,58 +445,72 @@ mod tests { 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"); + 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"); + 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)"); + 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)"); + 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)"); + 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)"); + 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: 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::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::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}}"); + 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}}" + ); } } From 102a9bb167a8f6954f7dc303a4614c3cba517c93 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 11 Jun 2022 14:22:51 -0700 Subject: [PATCH 58/75] Revert "Create default Enum impls" This reverts commit 91409a061106a5ba64ce579c09e6fce4af710197. This was done in response to the poor performance of using TypeInfo directly: https://github.com/bevyengine/bevy/pull/4042#discussion_r892984360 --- .../bevy_reflect_derive/src/impls/enums.rs | 74 +++++++++++++++- crates/bevy_reflect/src/enums/enum_trait.rs | 84 ++++--------------- crates/bevy_reflect/src/enums/variants.rs | 7 -- crates/bevy_reflect/src/impls/std.rs | 36 ++++---- 4 files changed, 105 insertions(+), 96 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 7f2d3ad5ba578..f677aa411bef4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -18,7 +18,11 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { 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 { @@ -37,7 +41,10 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } }); - let debug_fn = reflect_enum.meta().traits().get_debug_impl(); + let debug_fn = reflect_enum + .meta() + .traits() + .get_debug_impl(); let partial_eq_fn = reflect_enum .meta() .traits() @@ -99,10 +106,32 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } + 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 { @@ -111,6 +140,14 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } + #[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) } @@ -217,7 +254,11 @@ 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 { @@ -226,7 +267,11 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden let mut variant_info: Vec = Vec::new(); let mut enum_field: Vec = Vec::new(); let mut enum_field_at: Vec = Vec::new(); + let mut enum_index_of: Vec = Vec::new(); + let mut enum_name_at: Vec = Vec::new(); + let mut enum_field_len: Vec = Vec::new(); let mut enum_variant_name: Vec = Vec::new(); + let mut enum_variant_type: Vec = Vec::new(); for variant in reflect_enum.active_variants() { let ident = &variant.data.ident; @@ -243,6 +288,9 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden enum_variant_name.push(quote! { #unit => #name }); + enum_variant_type.push(quote! { + #unit => #bevy_reflect_path::VariantType::Unit + }); } EnumVariantFields::Unnamed(fields) => { let mut field_info = Vec::new(); @@ -266,9 +314,16 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden field_idx += 1; } + let field_len = field_idx; + 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::Tuple + }); variant_info.push(quote! { #bevy_reflect_path::VariantInfo::Tuple( #bevy_reflect_path::TupleVariantInfo::new(#name, &[ @@ -295,6 +350,12 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden enum_field_at.push(quote! { #unit{ #field_ident, .. } if #ref_index == #field_idx => Some(#field_ident) }); + enum_index_of.push(quote! { + #unit{ .. } if #ref_name == #field_name => Some(#field_idx) + }); + enum_name_at.push(quote! { + #unit{ .. } if #ref_index == #field_idx => Some(#field_name) + }); let field_ty = &field.data.ty; field_info.push(quote! { @@ -304,9 +365,16 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden field_idx += 1; } + let field_len = field_idx; + 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::Struct + }); variant_info.push(quote! { #bevy_reflect_path::VariantInfo::Struct( #bevy_reflect_path::StructVariantInfo::new(#name, &[ @@ -322,6 +390,10 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden 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/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 207b2418aa93c..a0e0e95f1e2b7 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -1,7 +1,7 @@ -use crate::{DynamicEnum, Reflect, TypeInfo, VariantInfo, VariantType}; +use crate::{DynamicEnum, Reflect, VariantInfo, VariantType}; use bevy_utils::HashMap; use std::any::{Any, TypeId}; -use std::borrow::{Borrow, Cow}; +use std::borrow::Cow; use std::slice::Iter; /// A trait representing a [reflected] enum. @@ -93,61 +93,24 @@ pub trait Enum: Reflect { 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; - /// The type of the current variant. - fn variant_type(&self) -> VariantType { - if let TypeInfo::Enum(info) = self.get_type_info() { - if let Some(variant) = info.variant(self.variant_name()) { - variant.variant_type() - } else { - panic!("invalid variant: `{}`", self.variant_name()); - } - } else { - panic!( - "`{:?}` is not `TypeInfo::Enum`", - std::any::type_name::() - ); - } - } - /// 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> { - if let TypeInfo::Enum(info) = self.get_type_info() { - match info.variant(self.variant_name())? { - VariantInfo::Struct(variant) => { - variant.field_at(index).map(|field| field.name().borrow()) - } - _ => None, - } - } else { - panic!( - "`{:?}` is not `TypeInfo::Enum`", - std::any::type_name::() - ); - } - } - /// 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 { - if let TypeInfo::Enum(info) = self.get_type_info() { - match info.variant(self.variant_name())? { - VariantInfo::Struct(variant) => variant.index_of(name), - _ => None, - } - } else { - panic!( - "`{:?}` is not `TypeInfo::Enum`", - std::any::type_name::() - ); - } - } /// 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 @@ -156,25 +119,6 @@ pub trait Enum: Reflect { fn variant_path(&self) -> String { format!("{}::{}", self.type_name(), self.variant_name()) } - /// Returns the number of fields in the current variant. - fn field_len(&self) -> usize { - if let TypeInfo::Enum(info) = self.get_type_info() { - if let Some(variant) = info.variant(self.variant_name()) { - match variant { - VariantInfo::Unit(..) => 0, - VariantInfo::Tuple(variant_info) => variant_info.field_len(), - VariantInfo::Struct(variant_info) => variant_info.field_len(), - } - } else { - panic!("invalid variant: `{}`", self.variant_name()); - } - } else { - panic!( - "`{:?}` is not `TypeInfo::Enum`", - std::any::type_name::() - ); - } - } } /// A container for compile-time enum info. diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs index c17dd4db50db9..b1a9740d21c75 100644 --- a/crates/bevy_reflect/src/enums/variants.rs +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -73,13 +73,6 @@ impl VariantInfo { Self::Unit(info) => info.name(), } } - pub fn variant_type(&self) -> VariantType { - match self { - Self::Struct(..) => VariantType::Struct, - Self::Tuple(..) => VariantType::Tuple, - Self::Unit(..) => VariantType::Unit, - } - } } /// Type info for struct variants. diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 8c513fb05c897..079cb89ab0660 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -586,10 +586,26 @@ impl Enum for Option { } } + 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 { @@ -598,10 +614,6 @@ impl Enum for Option { } } - fn clone_dynamic(&self) -> DynamicEnum { - DynamicEnum::from_ref::(self) - } - #[inline] fn variant_type(&self) -> VariantType { match self { @@ -610,20 +622,8 @@ impl Enum for Option { } } - fn name_at(&self, _index: usize) -> Option<&str> { - None - } - - fn index_of(&self, _name: &str) -> Option { - None - } - - #[inline] - fn field_len(&self) -> usize { - match self { - Some(..) => 1, - None => 0, - } + fn clone_dynamic(&self) -> DynamicEnum { + DynamicEnum::from_ref::(self) } } From 8b2163fefd6dce99050bbdfb16ed8cda1322124d Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 11 Jun 2022 14:38:15 -0700 Subject: [PATCH 59/75] Fix up example --- examples/reflection/reflection_types.rs | 32 +++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index 12ec630738502..a3f435e92fb5a 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 { foo: f32, bar: bool }, +} + /// 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, } @@ -83,7 +91,7 @@ fn setup() { // arity 12 or less. ReflectRef::Tuple(_) => {} // `Enum` is a trait automatically implemented for enums that derive Reflect. This trait allows you - // to interact list possible variants and interact with the currently active one + // 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 From cf4f50683b23e451e16bc0ac66e2b126774b92b3 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 11 Jun 2022 14:56:54 -0700 Subject: [PATCH 60/75] Formatting --- crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index f677aa411bef4..76cd243957f65 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -41,10 +41,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } }); - let debug_fn = reflect_enum - .meta() - .traits() - .get_debug_impl(); + let debug_fn = reflect_enum.meta().traits().get_debug_impl(); let partial_eq_fn = reflect_enum .meta() .traits() From 1d9c6467cf31d9ba99282e29718d9f62fd817cc4 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 11 Jun 2022 15:02:49 -0700 Subject: [PATCH 61/75] Fix clippy --- crates/bevy_reflect/src/serde/de.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index 132482ef76d2f..ad24185de15f3 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -551,7 +551,7 @@ impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> { V: MapAccess<'de>, { let key = map.next_key::()?; - match key.as_ref().map(|x| x.as_str()) { + match key.as_deref() { Some(type_fields::VARIANT) => {} Some(key) => return Err(V::Error::unknown_field(key, &[type_fields::VARIANT])), _ => { @@ -564,7 +564,7 @@ impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> { let mut dynamic_enum = DynamicEnum::default(); let key = map.next_key::()?; - match key.as_ref().map(|x| x.as_str()) { + match key.as_deref() { Some(type_fields::STRUCT) => { let dynamic_struct = map.next_value_seed(StructDeserializer { registry: self.registry, From 435fc45fae5b53e96d4564a9a9465c81e761d52e Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 11 Jun 2022 15:13:37 -0700 Subject: [PATCH 62/75] Change blacklisted name --- examples/reflection/reflection_types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index a3f435e92fb5a..db9dc4b61d995 100644 --- a/examples/reflection/reflection_types.rs +++ b/examples/reflection/reflection_types.rs @@ -37,7 +37,7 @@ pub struct C(usize); pub enum D { A, B(usize), - C { foo: f32, bar: bool }, + C { value: f32 }, } /// Reflect has "built in" support for some common traits like `PartialEq`, `Hash`, and `Serialize`. From a8c184a86c443b9c448cf20b5ae3aeee508f188a Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 11 Jun 2022 15:34:11 -0700 Subject: [PATCH 63/75] Fix serde tests --- crates/bevy_reflect/src/serde/ser.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index e9962b5ea51c7..211c4dd5b289a 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -490,6 +490,7 @@ mod tests { use crate as bevy_reflect; use crate::prelude::*; use crate::TypeRegistry; + use ron::ser::PrettyConfig; fn get_registry() -> TypeRegistry { let mut registry = TypeRegistry::default(); @@ -513,10 +514,12 @@ mod tests { 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, Default::default()).unwrap(); + 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": { @@ -528,7 +531,7 @@ mod tests { // === NewType Variant === // let value = MyEnum::NewType(123); let serializer = ReflectSerializer::new(&value, ®istry); - let output = ron::ser::to_string_pretty(&serializer, Default::default()).unwrap(); + 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": { @@ -546,7 +549,7 @@ mod tests { // === Tuple Variant === // let value = MyEnum::Tuple(1.23, 3.21); let serializer = ReflectSerializer::new(&value, ®istry); - let output = ron::ser::to_string_pretty(&serializer, Default::default()).unwrap(); + 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": { @@ -570,7 +573,7 @@ mod tests { value: String::from("I <3 Enums"), }; let serializer = ReflectSerializer::new(&value, ®istry); - let output = ron::ser::to_string_pretty(&serializer, Default::default()).unwrap(); + 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": { @@ -583,6 +586,6 @@ mod tests { }, }, }"#; - assert_eq!(expected, output); + assert_eq!(expected, output.replace('\r', "")); } } From cd6c55152f5efc2468ad39ef6b21f576c55686a1 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 11 Jun 2022 15:37:33 -0700 Subject: [PATCH 64/75] Remove redundant clone --- crates/bevy_reflect/src/serde/ser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index 211c4dd5b289a..0dd239d6305a7 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -573,7 +573,7 @@ mod tests { value: String::from("I <3 Enums"), }; let serializer = ReflectSerializer::new(&value, ®istry); - let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); + let output = ron::ser::to_string_pretty(&serializer, config).unwrap(); let expected = r#"{ "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", "enum": { From ab3ea82dc2dbb8d31ba4541065b0f2ad89c7efdb Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Thu, 23 Jun 2022 11:24:55 +0200 Subject: [PATCH 65/75] Use field access syntax in enum macros Using struct expression and patterns is allowed in rust for tuple structs. As per references. This allows us to simplify some of the macros, and remove `underscores` from the utility module. - https://play.rust-lang.org/?gist=fd4cc2283fff6a6aade5dbe9427e44db - https://doc.rust-lang.org/reference/expressions/struct-expr.html - https://doc.rust-lang.org/reference/patterns.html#struct-patterns --- .../bevy_reflect_derive/src/derive_data.rs | 30 ++-- .../bevy_reflect_derive/src/enum_utility.rs | 151 +++++++----------- .../bevy_reflect_derive/src/impls/enums.rs | 144 +++++++---------- .../bevy_reflect_derive/src/utility.rs | 25 --- 4 files changed, 122 insertions(+), 228 deletions(-) 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 dfbedecafc1fa..3eb9bed751857 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -187,28 +187,18 @@ impl<'a> ReflectDerive<'a> { .iter() .enumerate() .map(|(index, variant)| -> Result { - let attrs = parse_field_attrs(&variant.attrs)?; let fields = Self::collect_struct_fields(&variant.fields)?; - Ok(match variant.fields { - Fields::Named(..) => EnumVariant { - data: variant, - fields: EnumVariantFields::Named(fields), - attrs, - index, - }, - Fields::Unnamed(..) => EnumVariant { - data: variant, - fields: EnumVariantFields::Unnamed(fields), - attrs, - index, - }, - Fields::Unit => EnumVariant { - data: variant, - fields: EnumVariantFields::Unit, - attrs, - index, - }, + 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( diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs index 64ee3bbdc8396..f7410e675eacf 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -1,6 +1,7 @@ use crate::derive_data::{EnumVariantFields, ReflectEnum}; -use proc_macro2::Ident; +use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; +use syn::Index; /// Contains all data needed to construct all variants within an enum. pub(crate) struct EnumVariantConstructors { @@ -10,6 +11,13 @@ pub(crate) struct EnumVariantConstructors { pub variant_constructors: Vec, } +fn field_indentifier(i: usize, ident: Option<&Ident>) -> TokenStream { + let tuple_accessor = Index::from(i); + match ident { + Some(ident) => quote!(#ident :), + None => quote!(#tuple_accessor :), + } +} /// Gets the constructors for all variants in the given enum. pub(crate) fn get_variant_constructors( reflect_enum: &ReflectEnum, @@ -17,106 +25,63 @@ pub(crate) fn get_variant_constructors( can_panic: bool, ) -> EnumVariantConstructors { let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); - let mut variant_names: Vec = Vec::new(); - let mut variant_constructors: Vec = Vec::new(); + let variant_count = reflect_enum.variants().len(); + let mut variant_names: Vec = Vec::with_capacity(variant_count); + let mut variant_constructors: Vec = Vec::with_capacity(variant_count); for variant in reflect_enum.active_variants() { let ident = &variant.data.ident; let name = ident.to_string(); - let unit = reflect_enum.get_unit(ident); - - match &variant.fields { - EnumVariantFields::Unit => { - variant_constructors.push(quote! { - #unit - }); - } - EnumVariantFields::Unnamed(fields) => { - let mut variant_apply = Vec::new(); - let mut field_idx: usize = 0; - for field in fields.iter() { - if field.attrs.ignore { - // Ignored field -> use default value - variant_apply.push(quote! { - Default::default() - }); - continue; - } - - let field_ty = &field.data.ty; - let expect_field = format!("field at index `{}` should exist", field_idx); + let variant_constructor = reflect_enum.get_unit(ident); + + let fields = match &variant.fields { + EnumVariantFields::Unit => &[], + EnumVariantFields::Named(fields) => fields.as_slice(), + 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 = field_indentifier(declar_index, field.data.ident.as_ref()); + let field_value = if field.attrs.ignore { + quote! { Default::default() } + } else { + let error_repr = match (&field.data.ident, reflect_index) { + (None, 0) => "1st".to_owned(), + (None, 1) => "2nd".to_owned(), + (None, 2) => "3rd".to_owned(), + // Assuming we have less than 21 fields + (None, n) => format!("{}th", n + 1), + (Some(name), _) => format!("`{name}`"), + }; + let unwrapper = if can_panic { let expect_type = format!( - "field at index `{}` should be of type `{}`", - field_idx, - field_ty.to_token_stream() + "the {error_repr} field should be of type `{}`", + field.data.ty.to_token_stream() ); - - let unwrapper = if can_panic { - quote!(.expect(#expect_type)) - } else { - quote!(?) - }; - - variant_apply.push(quote! { - #bevy_reflect_path::FromReflect::from_reflect( - #ref_value - .field_at(#field_idx) - .expect(#expect_field) - ) - #unwrapper - }); - - field_idx += 1; - } - - variant_constructors.push(quote! { - #unit( #(#variant_apply),* ) - }); - } - EnumVariantFields::Named(fields) => { - let mut variant_apply = Vec::new(); - for field in fields.iter() { - let field_ident = field.data.ident.as_ref().unwrap(); - - if field.attrs.ignore { - // Ignored field -> use default value - variant_apply.push(quote! { - #field_ident: Default::default() - }); - continue; + quote!(.expect(#expect_type)) + } else { + quote!(?) + }; + let field_accessor = match &field.data.ident { + Some(ident) => { + let name = ident.to_string(); + quote!(.field(#name)) } - - let field_name = field_ident.to_string(); - let field_ty = &field.data.ty; - let expect_field = format!("field with name `{}` should exist", field_name); - let expect_type = format!( - "field with name `{}` should be of type `{}`", - field_name, - field_ty.to_token_stream() - ); - - let unwrapper = if can_panic { - quote!(.expect(#expect_type)) - } else { - quote!(?) - }; - - variant_apply.push(quote! { - #field_ident: #bevy_reflect_path::FromReflect::from_reflect( - #ref_value - .field(#field_name) - .expect(#expect_field) - ) - #unwrapper - }); + None => quote!(.field_at(#reflect_index)), + }; + reflect_index += 1; + let expect_field = format!("the {error_repr} field was not declared"); + let accessor = quote!(#field_accessor .expect(#expect_field)); + quote! { + #bevy_reflect_path::FromReflect::from_reflect(#ref_value #accessor) + #unwrapper } - - variant_constructors.push(quote! { - #unit{ #(#variant_apply),* } - }); - } - } - + }; + quote! { #field_ident #field_value } + }); + variant_constructors.push(quote! { + #variant_constructor { #( #constructor_fields ),* } + }); variant_names.push(name); } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 76cd243957f65..890d542abf54e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,10 +1,9 @@ use crate::derive_data::{EnumVariantFields, ReflectEnum}; use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::impls::impl_typed; -use crate::utility; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; -use quote::quote; +use quote::{format_ident, quote}; pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); @@ -261,126 +260,91 @@ struct EnumImpls { 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 = Vec::new(); - let mut enum_field: Vec = Vec::new(); - let mut enum_field_at: Vec = Vec::new(); - let mut enum_index_of: Vec = Vec::new(); - let mut enum_name_at: Vec = Vec::new(); - let mut enum_field_len: Vec = Vec::new(); - let mut enum_variant_name: Vec = Vec::new(); - let mut enum_variant_type: Vec = Vec::new(); + 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); - match &variant.fields { - EnumVariantFields::Unit => { - variant_info.push(quote! { - #bevy_reflect_path::VariantInfo::Unit( - #bevy_reflect_path::UnitVariantInfo::new(#name) - ) - }); - enum_variant_name.push(quote! { - #unit => #name - }); - enum_variant_type.push(quote! { - #unit => #bevy_reflect_path::VariantType::Unit - }); - } - EnumVariantFields::Unnamed(fields) => { + // This is equivalent to a |fields: &Vec, to_run: |usize, usize, &StructField| -> TokenStream| + // closure. Issue is that the closure cannot itself accept another closure, because closures cannot vary in + // the concrete type they accept, and each closure has a different concrete type. + macro_rules! for_fields { + ($fields:expr, |$reflect_idx:ident, $declar_field:pat, $field:ident| $body:expr) => {{ let mut field_info = Vec::new(); - let mut field_idx: usize = 0; - for field in fields.iter() { - if field.attrs.ignore { + let mut $reflect_idx: usize = 0; + for ($declar_field, $field) in $fields.iter().enumerate() { + if $field.attrs.ignore { // Ignored field continue; } + field_info.push($body); + $reflect_idx += 1; + } + ($reflect_idx, field_info) + }}; + } + let (variant_type, field_len, field_info) = match &variant.fields { + EnumVariantFields::Unit => ("Unit", 0usize, quote!(#name)), - let empties = utility::underscores(field_idx); + EnumVariantFields::Unnamed(fields) => { + let (field_len, field_info) = for_fields!(fields, |reflect_idx, declar, field| { + let declar_field = syn::Index::from(declar); enum_field_at.push(quote! { - #unit( #empties value, .. ) if #ref_index == #field_idx => Some(value) + #unit { #declar_field : value, .. } if #ref_index == #reflect_idx => Some(value) }); - let field_ty = &field.data.ty; - field_info.push(quote! { - #bevy_reflect_path::UnnamedField::new::<#field_ty>(#field_idx) - }); - - field_idx += 1; - } - - let field_len = field_idx; - 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::Tuple - }); - variant_info.push(quote! { - #bevy_reflect_path::VariantInfo::Tuple( - #bevy_reflect_path::TupleVariantInfo::new(#name, &[ - #(#field_info),* - ]) - ) + quote! { #bevy_reflect_path::UnnamedField::new::<#field_ty>(#reflect_idx) } }); + ("Tuple", field_len, quote!(#name, &[ #(#field_info),* ])) } EnumVariantFields::Named(fields) => { - let mut field_info = Vec::new(); - let mut field_idx: usize = 0; - for field in fields.iter() { + let (field_len, field_info) = for_fields!(fields, |reflect_idx, _, field| { let field_ident = field.data.ident.as_ref().unwrap(); - - if field.attrs.ignore { - // Ignored field - continue; - } - 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 == #field_idx => Some(#field_ident) + #unit{ #field_ident, .. } if #ref_index == #reflect_idx => Some(#field_ident) }); enum_index_of.push(quote! { - #unit{ .. } if #ref_name == #field_name => Some(#field_idx) + #unit{ .. } if #ref_name == #field_name => Some(#reflect_idx) }); enum_name_at.push(quote! { - #unit{ .. } if #ref_index == #field_idx => Some(#field_name) + #unit{ .. } if #ref_index == #reflect_idx => Some(#field_name) }); let field_ty = &field.data.ty; - field_info.push(quote! { - #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) - }); - - field_idx += 1; - } - - let field_len = field_idx; - 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::Struct - }); - variant_info.push(quote! { - #bevy_reflect_path::VariantInfo::Struct( - #bevy_reflect_path::StructVariantInfo::new(#name, &[ - #(#field_info),* - ]) - ) + quote! { #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) } }); + ("Struct", field_len, quote!(#name, &[ #(#field_info),* ])) } - } + }; + let variant = Ident::new(variant_type, Span::call_site()); + let info_type = format_ident!("{}VariantInfo", variant_type); + variant_info.push(quote! { + #bevy_reflect_path::VariantInfo::#variant( + #bevy_reflect_path::#info_type::new(#field_info) + ) + }); + 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 + }); } EnumImpls { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 3e2a88e03caa3..a03541e934e26 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -2,7 +2,6 @@ use bevy_macro_utils::BevyManifest; use proc_macro2::{Ident, Span}; -use quote::quote; use syn::Path; /// Returns the correct path for `bevy_reflect`. @@ -23,30 +22,6 @@ pub(crate) fn get_reflect_ident(name: &str) -> Ident { Ident::new(&reflected, Span::call_site()) } -/// Returns a token stream of comma-separated underscores for the given count. -/// -/// This is useful for creating tuple field accessors: -/// -/// ```ignore -/// let empties = underscores(2); -/// quote! { -/// let (#empties value, ..) = (10, 20, 30); -/// assert_eq!(30, value); -/// } -/// ``` -/// -/// > Note: This automatically handles the trailing comma. -/// -pub(crate) fn underscores(count: usize) -> proc_macro2::TokenStream { - let mut output = proc_macro2::TokenStream::new(); - for _ in 0..count { - output = quote! { - #output _, - } - } - output -} - /// Helper struct used to process an iterator of `Result, syn::Error>`, /// combining errors into one along the way. pub(crate) struct ResultSifter { From 080b039873113b2b7357120f1e1b270c81610358 Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Sun, 26 Jun 2022 11:43:43 +0200 Subject: [PATCH 66/75] Clenaup a bit more the bevy_reflect enum code --- .../bevy_reflect_derive/src/enum_utility.rs | 45 +++++------- .../bevy_reflect_derive/src/impls/enums.rs | 70 +++++++++++-------- .../bevy_reflect_derive/src/utility.rs | 9 ++- 3 files changed, 65 insertions(+), 59 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs index f7410e675eacf..230e0876c6b6c 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -1,7 +1,9 @@ -use crate::derive_data::{EnumVariantFields, ReflectEnum}; -use proc_macro2::{Ident, TokenStream}; +use crate::{ + derive_data::{EnumVariantFields, ReflectEnum}, + utility::field_ident_or_indexed, +}; +use proc_macro2::Ident; use quote::{quote, ToTokens}; -use syn::Index; /// Contains all data needed to construct all variants within an enum. pub(crate) struct EnumVariantConstructors { @@ -11,13 +13,6 @@ pub(crate) struct EnumVariantConstructors { pub variant_constructors: Vec, } -fn field_indentifier(i: usize, ident: Option<&Ident>) -> TokenStream { - let tuple_accessor = Index::from(i); - match ident { - Some(ident) => quote!(#ident :), - None => quote!(#tuple_accessor :), - } -} /// Gets the constructors for all variants in the given enum. pub(crate) fn get_variant_constructors( reflect_enum: &ReflectEnum, @@ -26,8 +21,8 @@ pub(crate) fn get_variant_constructors( ) -> EnumVariantConstructors { let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); let variant_count = reflect_enum.variants().len(); - let mut variant_names: Vec = Vec::with_capacity(variant_count); - let mut variant_constructors: Vec = Vec::with_capacity(variant_count); + 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; @@ -41,24 +36,20 @@ pub(crate) fn get_variant_constructors( }; let mut reflect_index: usize = 0; let constructor_fields = fields.iter().enumerate().map(|(declar_index, field)| { - let field_ident = field_indentifier(declar_index, field.data.ident.as_ref()); + let field_ident = field_ident_or_indexed(declar_index, field.data.ident.as_ref()); let field_value = if field.attrs.ignore { quote! { Default::default() } } else { - let error_repr = match (&field.data.ident, reflect_index) { - (None, 0) => "1st".to_owned(), - (None, 1) => "2nd".to_owned(), - (None, 2) => "3rd".to_owned(), - // Assuming we have less than 21 fields - (None, n) => format!("{}th", n + 1), - (Some(name), _) => format!("`{name}`"), - }; + 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 expect_type = format!( - "the {error_repr} field should be of type `{}`", + let type_err_message = format!( + "the field {error_repr} should be of type `{}`", field.data.ty.to_token_stream() ); - quote!(.expect(#expect_type)) + quote!(.expect(#type_err_message)) } else { quote!(?) }; @@ -70,14 +61,14 @@ pub(crate) fn get_variant_constructors( None => quote!(.field_at(#reflect_index)), }; reflect_index += 1; - let expect_field = format!("the {error_repr} field was not declared"); - let accessor = quote!(#field_accessor .expect(#expect_field)); + 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 } + quote! { #field_ident : #field_value } }); variant_constructors.push(quote! { #variant_constructor { #( #constructor_fields ),* } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 890d542abf54e..ffd136eb4d43b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,9 +1,9 @@ -use crate::derive_data::{EnumVariantFields, ReflectEnum}; +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::{format_ident, quote}; +use quote::quote; pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); @@ -274,29 +274,40 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden let name = ident.to_string(); let unit = reflect_enum.get_unit(ident); - // This is equivalent to a |fields: &Vec, to_run: |usize, usize, &StructField| -> TokenStream| - // closure. Issue is that the closure cannot itself accept another closure, because closures cannot vary in - // the concrete type they accept, and each closure has a different concrete type. - macro_rules! for_fields { - ($fields:expr, |$reflect_idx:ident, $declar_field:pat, $field:ident| $body:expr) => {{ - let mut field_info = Vec::new(); - let mut $reflect_idx: usize = 0; - for ($declar_field, $field) in $fields.iter().enumerate() { - if $field.attrs.ignore { - // Ignored field - continue; - } - field_info.push($body); - $reflect_idx += 1; + 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; } - ($reflect_idx, field_info) - }}; + constructor_argument.push(generate_for_field(reflect_idx, field.index, field)); + reflect_idx += 1; + } + (reflect_idx, constructor_argument) } - let (variant_type, field_len, field_info) = match &variant.fields { - EnumVariantFields::Unit => ("Unit", 0usize, quote!(#name)), + let mut info_type = |variant, info_type, arguments| { + 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) + ) + }); + variant + }; + let (variant, field_len) = match &variant.fields { + EnumVariantFields::Unit => { + let variant = info_type("Unit", "UnitVariantInfo", quote!(#name)); + (variant, 0usize) + } EnumVariantFields::Unnamed(fields) => { - let (field_len, field_info) = for_fields!(fields, |reflect_idx, declar, field| { + 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) @@ -304,10 +315,12 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden let field_ty = &field.data.ty; quote! { #bevy_reflect_path::UnnamedField::new::<#field_ty>(#reflect_idx) } }); - ("Tuple", field_len, quote!(#name, &[ #(#field_info),* ])) + let arguments = quote!(#name, &[ #(#argument),* ]); + let variant = info_type("Tuple", "TupleVariantInfo", arguments); + (variant, field_len) } EnumVariantFields::Named(fields) => { - let (field_len, field_info) = for_fields!(fields, |reflect_idx, _, field| { + 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! { @@ -326,16 +339,11 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden let field_ty = &field.data.ty; quote! { #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) } }); - ("Struct", field_len, quote!(#name, &[ #(#field_info),* ])) + let arguments = quote!(#name, &[ #(#argument),* ]); + let variant = info_type("Struct", "StructVariantInfo", arguments); + (variant, field_len) } }; - let variant = Ident::new(variant_type, Span::call_site()); - let info_type = format_ident!("{}VariantInfo", variant_type); - variant_info.push(quote! { - #bevy_reflect_path::VariantInfo::#variant( - #bevy_reflect_path::#info_type::new(#field_info) - ) - }); enum_field_len.push(quote! { #unit{..} => #field_len }); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index a03541e934e26..9c79f40200fc7 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 { @@ -29,6 +29,13 @@ pub(crate) struct ResultSifter { errors: Option, } +pub(crate) fn field_ident_or_indexed(index: usize, ident: Option<&Ident>) -> Member { + ident.as_ref().map_or_else( + || Member::Unnamed(index.into()), + |&ident| Member::Named(ident.clone()), + ) +} + impl Default for ResultSifter { fn default() -> Self { Self { From 562fb439cc1e66dde897bb6787bec0a3ea9bd446 Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Sun, 26 Jun 2022 19:57:57 +0200 Subject: [PATCH 67/75] Cleanup bevy_reflect a bit more --- .../bevy_reflect_derive/src/enum_utility.rs | 4 +-- .../bevy_reflect_derive/src/impls/enums.rs | 33 ++++++++----------- .../bevy_reflect_derive/src/utility.rs | 27 +++++++++++++-- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs index 230e0876c6b6c..af6037f6c563c 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -1,6 +1,6 @@ use crate::{ derive_data::{EnumVariantFields, ReflectEnum}, - utility::field_ident_or_indexed, + utility::ident_or_index, }; use proc_macro2::Ident; use quote::{quote, ToTokens}; @@ -36,7 +36,7 @@ pub(crate) fn get_variant_constructors( }; let mut reflect_index: usize = 0; let constructor_fields = fields.iter().enumerate().map(|(declar_index, field)| { - let field_ident = field_ident_or_indexed(declar_index, field.data.ident.as_ref()); + let field_ident = ident_or_index(field.data.ident.as_ref(), declar_index); let field_value = if field.attrs.ignore { quote! { Default::default() } } else { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index ffd136eb4d43b..6140f853eee69 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -290,7 +290,7 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden } (reflect_idx, constructor_argument) } - let mut info_type = |variant, info_type, arguments| { + 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! { @@ -298,14 +298,20 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden #bevy_reflect_path::#info_type::new(#arguments) ) }); - variant + 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 + }); }; - let (variant, field_len) = match &variant.fields { + match &variant.fields { EnumVariantFields::Unit => { - let variant = info_type("Unit", "UnitVariantInfo", quote!(#name)); - (variant, 0usize) + 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); @@ -316,8 +322,7 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden quote! { #bevy_reflect_path::UnnamedField::new::<#field_ty>(#reflect_idx) } }); let arguments = quote!(#name, &[ #(#argument),* ]); - let variant = info_type("Tuple", "TupleVariantInfo", arguments); - (variant, field_len) + add_fields_branch("Tuple", "TupleVariantInfo", arguments, field_len); } EnumVariantFields::Named(fields) => { let (field_len, argument) = for_fields(fields, |reflect_idx, _, field| { @@ -340,19 +345,9 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden quote! { #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) } }); let arguments = quote!(#name, &[ #(#argument),* ]); - let variant = info_type("Struct", "StructVariantInfo", arguments); - (variant, field_len) + add_fields_branch("Struct", "StructVariantInfo", arguments, field_len); } }; - 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 - }); } EnumImpls { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 9c79f40200fc7..f8894689a3858 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -29,10 +29,31 @@ pub(crate) struct ResultSifter { errors: Option, } -pub(crate) fn field_ident_or_indexed(index: usize, ident: Option<&Ident>) -> Member { - ident.as_ref().map_or_else( +/// 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()), + |ident| Member::Named(ident.clone()), ) } From 6d34b084df41373514d54addf27c66c314be8bfe Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 26 Jun 2022 19:09:39 -0700 Subject: [PATCH 68/75] Fix DynamicEnum::apply for differing variants --- crates/bevy_reflect/src/enums/dynamic_enum.rs | 44 +++++- crates/bevy_reflect/src/enums/mod.rs | 135 ++++++++++++++++++ 2 files changed, 173 insertions(+), 6 deletions(-) diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 1d753d1aacca8..018e9050a6956 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -280,15 +280,47 @@ impl Reflect for DynamicEnum { #[inline] fn apply(&mut self, value: &dyn Reflect) { - if let ReflectRef::Enum(enum_value) = value.reflect_ref() { - for field in enum_value.iter_fields() { - let name = field.name().unwrap(); - if let Some(v) = self.field_mut(name) { - v.apply(field.value()); + 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(); + Enum::field_mut(self, name).map(|v| v.apply(field.value())); + } + } + VariantType::Tuple => { + for (index, field) in value.iter_fields().enumerate() { + Enum::field_at_mut(self, index).map(|v| 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!("Attempted to apply non-enum type to enum type."); + panic!("`{}` is not an enum", value.type_name()); } } diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs index 3364478d5c8aa..6a1f80fd38cf3 100644 --- a/crates/bevy_reflect/src/enums/mod.rs +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -127,6 +127,27 @@ mod tests { ); } + #[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; @@ -429,6 +450,120 @@ mod tests { ); } + #[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)] From 604c36becd41cfa4830d2049894dc1ee130bbd36 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 26 Jun 2022 19:49:07 -0700 Subject: [PATCH 69/75] Fix clippy errors --- .../bevy_reflect/bevy_reflect_derive/src/enum_utility.rs | 5 +++-- crates/bevy_reflect/src/enums/dynamic_enum.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs index af6037f6c563c..427db8a377e57 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -31,8 +31,9 @@ pub(crate) fn get_variant_constructors( let fields = match &variant.fields { EnumVariantFields::Unit => &[], - EnumVariantFields::Named(fields) => fields.as_slice(), - EnumVariantFields::Unnamed(fields) => fields.as_slice(), + 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)| { diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 018e9050a6956..d0f097e977b1e 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -287,12 +287,16 @@ impl Reflect for DynamicEnum { VariantType::Struct => { for field in value.iter_fields() { let name = field.name().unwrap(); - Enum::field_mut(self, name).map(|v| v.apply(field.value())); + if let Some(v) = Enum::field_mut(self, name) { + v.apply(field.value()); + } } } VariantType::Tuple => { for (index, field) in value.iter_fields().enumerate() { - Enum::field_at_mut(self, index).map(|v| v.apply(field.value())); + if let Some(v) = Enum::field_at_mut(self, index) { + v.apply(field.value()); + } } } _ => {} From 777a618def6698daf875de3be11757363667f291 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 4 Jul 2022 11:49:25 -0700 Subject: [PATCH 70/75] Addressed PR comments --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 13 +++++++++++-- crates/bevy_reflect/src/enums/enum_trait.rs | 7 ++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 7c8c25acced0d..19502098e73f1 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -147,12 +147,21 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { #impl_from_struct }) } - _ => syn::Error::new( + ReflectDerive::TupleStruct(..) => syn::Error::new( ast.span(), - "impl_reflect_struct is only supported for standard structs", + "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(), } } diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index a0e0e95f1e2b7..4174134e457c0 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -7,8 +7,8 @@ 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, unlike their struct counterparts, -/// are a lot more complex. Users will need to be mindful of conventions, +/// 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 @@ -33,7 +33,8 @@ use std::slice::Iter; /// 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, on the other hand, are represented by a _name_. +/// so on. Fields in struct variants (excluding tuple structs), on the other hand, are +/// represented by a _name_. /// /// # Implementation /// From 4b1bf2132fdfd9c482e2bbe7e57229850d06c1d5 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 4 Jul 2022 11:54:37 -0700 Subject: [PATCH 71/75] Split up doc comments across lines This was done to aid reviewers of this PR and future PRs --- crates/bevy_reflect/src/enums/enum_trait.rs | 58 ++++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 4174134e457c0..ab894df4aaa82 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -7,16 +7,17 @@ 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. +/// 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. +/// 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_. @@ -31,9 +32,10 @@ use std::slice::Iter; /// | 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 +/// 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 @@ -42,40 +44,44 @@ use std::slice::Iter; /// > 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. +/// 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. +/// 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... +/// 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. +/// 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. +/// 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. +/// 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. +/// 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 From db777acf0b874e7262a727c7c41d0eea542f6768 Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Mon, 4 Jul 2022 11:56:31 -0700 Subject: [PATCH 72/75] Update crates/bevy_reflect/src/enums/enum_trait.rs Co-authored-by: Alice Cecile --- crates/bevy_reflect/src/enums/enum_trait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index ab894df4aaa82..2a25169ec951a 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -128,7 +128,7 @@ pub trait Enum: Reflect { } } -/// A container for compile-time enum info. +/// A container for compile-time enum info, used by [`TypeInfo`]. #[derive(Clone, Debug)] pub struct EnumInfo { type_name: &'static str, From 2160eeeb2288ea249fe40588e073c161604c048b Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 4 Jul 2022 12:21:21 -0700 Subject: [PATCH 73/75] Fixed doc link --- crates/bevy_reflect/src/enums/enum_trait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 2a25169ec951a..9ee110d8f0a05 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -128,7 +128,7 @@ pub trait Enum: Reflect { } } -/// A container for compile-time enum info, used by [`TypeInfo`]. +/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo). #[derive(Clone, Debug)] pub struct EnumInfo { type_name: &'static str, From 2497ecd9e350d06a9e6cec5cee461cf2425c3e23 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 2 Aug 2022 14:21:54 -0700 Subject: [PATCH 74/75] Remove unnecessary allocations --- .../bevy_reflect/bevy_reflect_derive/src/derive_data.rs | 8 +++----- .../bevy_reflect/bevy_reflect_derive/src/impls/structs.rs | 5 +---- .../bevy_reflect_derive/src/impls/tuple_structs.rs | 5 +---- 3 files changed, 5 insertions(+), 13 deletions(-) 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 3eb9bed751857..422809d6db804 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -5,7 +5,7 @@ use quote::quote; 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, Variant}; +use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant}; pub(crate) enum ReflectDerive<'a> { Struct(ReflectStruct<'a>), @@ -268,10 +268,8 @@ impl<'a> ReflectStruct<'a> { } /// Get a collection of all active types. - pub fn active_types(&self) -> Vec { - self.active_fields() - .map(|field| field.data.ty.clone()) - .collect::>() + pub fn active_types(&self) -> impl Iterator { + self.active_fields().map(|field| &field.data.ty) } /// The complete set of fields in this struct. diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index 502e7c876d20b..4dca3a4feea16 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -31,10 +31,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) }) .collect::>(); - let field_types = reflect_struct - .active_fields() - .map(|field| field.data.ty.clone()) - .collect::>(); + let field_types = reflect_struct.active_types(); let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); 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 index 0bf67081e1ac0..0ad33ba4bb8ce 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -14,10 +14,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { .active_fields() .map(|field| Member::Unnamed(Index::from(field.index))) .collect::>(); - let field_types = reflect_struct - .active_fields() - .map(|field| field.data.ty.clone()) - .collect::>(); + let field_types = reflect_struct.active_types(); let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); From 5686686668716764c609559d5215bb9ef6235f07 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 2 Aug 2022 14:48:06 -0700 Subject: [PATCH 75/75] Remove as_meta(), which adds redundant allocations --- .../bevy_reflect_derive/src/lib.rs | 18 ++++++++++++------ .../bevy_reflect_derive/src/reflect_value.rs | 11 ----------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 19502098e73f1..d7a7515683a11 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -94,9 +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 meta = reflect_value_def.as_meta(); - impls::impl_value(&meta) + 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 @@ -167,7 +170,10 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let meta = reflect_value_def.as_meta(); - from_reflect::impl_value(&meta) + 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/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index 390b07abdbed6..ec54b99a6f404 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -1,5 +1,4 @@ use crate::container_attributes::ReflectTraits; -use crate::ReflectMeta; use proc_macro2::Ident; use syn::parse::{Parse, ParseStream}; use syn::token::{Paren, Where}; @@ -25,16 +24,6 @@ pub(crate) struct ReflectValueDef { pub traits: Option, } -impl ReflectValueDef { - pub fn as_meta(&self) -> ReflectMeta { - ReflectMeta::new( - &self.type_name, - &self.generics, - self.traits.clone().unwrap_or_default(), - ) - } -} - impl Parse for ReflectValueDef { fn parse(input: ParseStream) -> syn::Result { let type_ident = input.parse::()?;