diff --git a/crates/bevy_macro_utils/src/lib.rs b/crates/bevy_macro_utils/src/lib.rs index 4883999039dd8..d1ab5cc40f60f 100644 --- a/crates/bevy_macro_utils/src/lib.rs +++ b/crates/bevy_macro_utils/src/lib.rs @@ -78,6 +78,22 @@ impl BevyManifest { deps.and_then(find_in_deps) .or_else(|| deps_dev.and_then(find_in_deps)) } + + /// Returns the path for the crate with the given name. + /// + /// This is a convenience method for constructing a [manifest] and + /// calling the [`get_path`] method. + /// + /// This method should only be used where you just need the path and can't + /// cache the [manifest]. If caching is possible, it's recommended to create + /// the [manifest] yourself and use the [`get_path`] method. + /// + /// [`get_path`]: Self::get_path + /// [manifest]: Self + pub fn get_path_direct(name: &str) -> syn::Path { + Self::default().get_path(name) + } + pub fn get_path(&self, name: &str) -> syn::Path { self.maybe_get_path(name) .unwrap_or_else(|| Self::parse_str(name)) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs new file mode 100644 index 0000000000000..8e16e77626c5a --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -0,0 +1,234 @@ +//! Contains code related to container attributes for reflected types. +//! +//! A container attribute is an attribute which applies to an entire struct or enum +//! as opposed to a particular field or variant. An example of such an attribute is +//! the derive helper attribute for `Reflect`, which looks like: +//! `#[reflect(PartialEq, Default, ...)]` and `#[reflect_value(PartialEq, Default, ...)]`. + +use crate::utility; +use proc_macro2::Ident; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::Comma; +use syn::{Meta, NestedMeta, Path}; + +// The "special" trait idents that are used internally for reflection. +// Received via attributes like `#[reflect(PartialEq, Hash, ...)]` +const PARTIAL_EQ_ATTR: &str = "PartialEq"; +const HASH_ATTR: &str = "Hash"; +const SERIALIZE_ATTR: &str = "Serialize"; + +/// A marker for trait implementations registered via the `Reflect` derive macro. +#[derive(Clone)] +pub(crate) enum TraitImpl { + /// The trait is not registered as implemented. + NotImplemented, + /// The trait is registered as implemented. + Implemented, + + // TODO: This can be made to use `ExprPath` instead of `Ident`, allowing for fully qualified paths to be used + /// The trait is registered with a custom function rather than an actual implementation. + Custom(Ident), +} + +impl Default for TraitImpl { + fn default() -> Self { + Self::NotImplemented + } +} + +/// A collection of traits that have been registered for a reflected type. +/// +/// This keeps track of a few traits that are utilized internally for reflection +/// (we'll call these traits _special traits_ within this context), but it +/// will also keep track of all registered traits. Traits are registered as part of the +/// `Reflect` derive macro using the helper attribute: `#[reflect(...)]`. +/// +/// The list of special traits are as follows: +/// * `Hash` +/// * `PartialEq` +/// * `Serialize` +/// +/// When registering a trait, there are a few things to keep in mind: +/// * Traits must have a valid `Reflect{}` struct in scope. For example, `Default` +/// needs `bevy_reflect::prelude::ReflectDefault` in scope. +/// * Traits must be single path identifiers. This means you _must_ use `Default` +/// instead of `std::default::Default` (otherwise it will try to register `Reflectstd`!) +/// * A custom function may be supplied in place of an actual implementation +/// for the special traits (but still follows the same single-path identifier +/// rules as normal). +/// +/// # Example +/// +/// Registering the `Default` implementation: +/// +/// ```ignore +/// // Import ReflectDefault so it's accessible by the derive macro +/// use bevy_reflect::prelude::ReflectDefault. +/// +/// #[derive(Reflect, Default)] +/// #[reflect(Default)] +/// struct Foo; +/// ``` +/// +/// Registering the `Hash` implementation: +/// +/// ```ignore +/// // `Hash` is a "special trait" and does not need (nor have) a ReflectHash struct +/// +/// #[derive(Reflect, Hash)] +/// #[reflect(Hash)] +/// struct Foo; +/// ``` +/// +/// Registering the `Hash` implementation using a custom function: +/// +/// ```ignore +/// // This function acts as our `Hash` implementation and +/// // corresponds to the `Reflect::reflect_hash` method. +/// fn get_hash(foo: &Foo) -> Option { +/// Some(123) +/// } +/// +/// #[derive(Reflect)] +/// // Register the custom `Hash` function +/// #[reflect(Hash(get_hash))] +/// struct Foo; +/// ``` +/// +/// > __Note:__ Registering a custom function only works for special traits. +/// +#[derive(Default)] +pub(crate) struct ReflectTraits { + hash: TraitImpl, + partial_eq: TraitImpl, + serialize: TraitImpl, + idents: Vec, +} + +impl ReflectTraits { + /// Create a new [`ReflectTraits`] instance from a set of nested metas. + pub fn from_nested_metas(nested_metas: &Punctuated) -> Self { + let mut traits = ReflectTraits::default(); + for nested_meta in nested_metas.iter() { + match nested_meta { + // Handles `#[reflect( Hash, Default, ... )]` + NestedMeta::Meta(Meta::Path(path)) => { + // Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`) + let ident = if let Some(segment) = path.segments.iter().next() { + segment.ident.to_string() + } else { + continue; + }; + + match ident.as_str() { + PARTIAL_EQ_ATTR => traits.partial_eq = TraitImpl::Implemented, + HASH_ATTR => traits.hash = TraitImpl::Implemented, + SERIALIZE_ATTR => traits.serialize = TraitImpl::Implemented, + // We only track reflected idents for traits not considered special + _ => traits.idents.push(utility::get_reflect_ident(&ident)), + } + } + // Handles `#[reflect( Hash(custom_hash_fn) )]` + NestedMeta::Meta(Meta::List(list)) => { + // Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`) + let ident = if let Some(segment) = list.path.segments.iter().next() { + segment.ident.to_string() + } else { + continue; + }; + + let list_meta = list.nested.iter().next(); + if let Some(NestedMeta::Meta(Meta::Path(path))) = list_meta { + if let Some(segment) = path.segments.iter().next() { + // This should be the ident of the custom function + let trait_func_ident = TraitImpl::Custom(segment.ident.clone()); + match ident.as_str() { + PARTIAL_EQ_ATTR => traits.partial_eq = trait_func_ident, + HASH_ATTR => traits.hash = trait_func_ident, + SERIALIZE_ATTR => traits.serialize = trait_func_ident, + _ => {} + } + } + } + } + _ => {} + } + } + + traits + } + + /// Returns true if the given reflected trait name (i.e. `ReflectDefault` for `Default`) + /// is registered for this type. + pub fn contains(&self, name: &str) -> bool { + self.idents.iter().any(|ident| ident == name) + } + + /// The list of reflected traits by their reflected ident (i.e. `ReflectDefault` for `Default`). + pub fn idents(&self) -> &[Ident] { + &self.idents + } + + /// Returns the logic for `Reflect::reflect_hash` as a `TokenStream`. + /// + /// If `Hash` was not registered, returns `None`. + pub fn get_hash_impl(&self, path: &Path) -> Option { + match &self.hash { + TraitImpl::Implemented => Some(quote! { + use std::hash::{Hash, Hasher}; + let mut hasher = #path::ReflectHasher::default(); + Hash::hash(&std::any::Any::type_id(self), &mut hasher); + Hash::hash(self, &mut hasher); + Some(hasher.finish()) + }), + TraitImpl::Custom(impl_fn) => Some(quote! { + Some(#impl_fn(self)) + }), + TraitImpl::NotImplemented => None, + } + } + + /// Returns the logic for `Reflect::reflect_partial_eq` as a `TokenStream`. + /// + /// If `PartialEq` was not registered, returns `None`. + pub fn get_partial_eq_impl(&self) -> Option { + match &self.partial_eq { + TraitImpl::Implemented => Some(quote! { + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + Some(std::cmp::PartialEq::eq(self, value)) + } else { + Some(false) + } + }), + TraitImpl::Custom(impl_fn) => Some(quote! { + Some(#impl_fn(self, value)) + }), + TraitImpl::NotImplemented => None, + } + } + + /// Returns the logic for `Reflect::serializable` as a `TokenStream`. + /// + /// If `Serialize` was not registered, returns `None`. + pub fn get_serialize_impl(&self, path: &Path) -> Option { + match &self.serialize { + TraitImpl::Implemented => Some(quote! { + Some(#path::serde::Serializable::Borrowed(self)) + }), + TraitImpl::Custom(impl_fn) => Some(quote! { + Some(#impl_fn(self)) + }), + TraitImpl::NotImplemented => None, + } + } +} + +impl Parse for ReflectTraits { + fn parse(input: ParseStream) -> syn::Result { + let result = Punctuated::::parse_terminated(input)?; + Ok(ReflectTraits::from_nested_metas(&result)) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs new file mode 100644 index 0000000000000..219fe8158bcac --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -0,0 +1,197 @@ +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, +} + +/// Data used by derive macros for `Reflect` and `FromReflect` +/// +/// # Example +/// ```ignore +/// // attrs +/// // |----------------------------------------| +/// #[reflect(PartialEq, Serialize, Deserialize, Default)] +/// // type_name generics +/// // |-------------------||----------| +/// 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, + 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(), + }; + + // Should indicate whether `#[reflect_value]` was used + let mut force_reflect_value = false; + + for attribute in input.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 { + output.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); + } + } + } + + 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; + } + return Ok(output); + } + _ => { + return Ok(output); + } + }; + + let mut errors: Option = None; + output.fields = 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 { + index, + attrs, + data: field, + } + }) + .collect::>(); + if let Some(errs) = errors { + return Err(errs); + } + + Ok(output) + } + + /// 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 [`DeriveType`] of this struct. + pub fn derive_type(&self) -> &DeriveType { + &self.derive_type + } + + /// The registered reflect traits on this struct. + pub fn traits(&self) -> &ReflectTraits { + &self.traits + } + + /// The name of this struct. + pub fn type_name(&self) -> &'a Ident { + self.type_name + } + + /// The generics associated with this struct. + pub fn generics(&self) -> &'a Generics { + 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 + } + + /// Returns the `GetTypeRegistration` impl as a `TokenStream`. + pub fn get_type_registration(&self) -> proc_macro2::TokenStream { + crate::registration::impl_get_type_registration( + self.type_name, + &self.bevy_reflect_path, + self.traits.idents(), + self.generics, + ) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs new file mode 100644 index 0000000000000..a0f3d2b6bc3a0 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs @@ -0,0 +1,76 @@ +//! Contains code related to field attributes for reflected types. +//! +//! A field attribute is an attribute which applies to particular field or variant +//! as opposed to an entire struct or enum. An example of such an attribute is +//! the derive helper attribute for `Reflect`, which looks like: `#[reflect(ignore)]`. + +use crate::REFLECT_ATTRIBUTE_NAME; +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{Attribute, Meta, NestedMeta}; + +pub(crate) static IGNORE_ATTR: &str = "ignore"; + +/// A container for attributes defined on a field reflected type's field. +#[derive(Default)] +pub(crate) struct ReflectFieldAttr { + /// Determines if this field should be ignored. + pub ignore: bool, +} + +/// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`). +pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result { + let mut args = ReflectFieldAttr::default(); + let mut errors: Option = None; + + let attrs = attrs + .iter() + .filter(|a| a.path.is_ident(REFLECT_ATTRIBUTE_NAME)); + for attr in attrs { + let meta = attr.parse_meta()?; + if let Err(err) = parse_meta(&mut args, &meta) { + if let Some(ref mut error) = errors { + error.combine(err); + } else { + errors = Some(err); + } + } + } + + if let Some(error) = errors { + Err(error) + } else { + Ok(args) + } +} + +fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error> { + match meta { + Meta::Path(path) if path.is_ident(IGNORE_ATTR) => { + args.ignore = true; + Ok(()) + } + Meta::Path(path) => Err(syn::Error::new( + path.span(), + format!("unknown attribute parameter: {}", path.to_token_stream()), + )), + Meta::NameValue(pair) => { + let path = &pair.path; + Err(syn::Error::new( + path.span(), + format!("unknown attribute parameter: {}", path.to_token_stream()), + )) + } + Meta::List(list) if !list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => { + Err(syn::Error::new(list.path.span(), "unexpected property")) + } + Meta::List(list) => { + for nested in list.nested.iter() { + if let NestedMeta::Meta(meta) = nested { + parse_meta(args, meta)?; + } + } + Ok(()) + } + } +} 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 aabf75d904479..8e9e9c679295b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -1,130 +1,88 @@ +use crate::ReflectDeriveData; use proc_macro::TokenStream; +use proc_macro2::Span; use quote::quote; -use syn::{Field, Generics, Ident, Index, Member, Path}; +use syn::{Field, Generics, Ident, Index, Lit, LitInt, LitStr, Member, Path}; -pub fn impl_struct( - struct_name: &Ident, +/// Implements `FromReflect` for the given struct +pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { + impl_struct_internal(derive_data, false) +} + +/// Implements `FromReflect` for the given tuple struct +pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { + impl_struct_internal(derive_data, true) +} + +/// Implements `FromReflect` for the given value type +pub(crate) fn impl_value( + type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path, - active_fields: &[(&Field, usize)], - ignored_fields: &[(&Field, usize)], - custom_constructor: Option, ) -> TokenStream { - let field_names = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| index.to_string()) - }) - .collect::>(); - let field_idents = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); - - let field_types = active_fields - .iter() - .map(|(field, _index)| field.ty.clone()) - .collect::>(); - let field_count = active_fields.len(); - let ignored_field_idents = ignored_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); - 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 { + fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { + Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone()) + } + } + }) +} - // Add FromReflect bound for each active field - let mut where_from_reflect_clause = if where_clause.is_some() { - quote! {#where_clause} - } else if field_count > 0 { - quote! {where} +/// Container for a struct's members (field name or index) and their +/// corresponding values. +struct MemberValuePair(Vec, Vec); + +impl MemberValuePair { + pub fn new(items: (Vec, Vec)) -> Self { + Self(items.0, items.1) + } +} + +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(); + + let ref_struct = Ident::new("__ref_struct", Span::call_site()); + let ref_struct_type = if is_tuple { + Ident::new("TupleStruct", Span::call_site()) } else { - quote! {} + Ident::new("Struct", Span::call_site()) }; - where_from_reflect_clause.extend(quote! { - #(#field_types: #bevy_reflect_path::FromReflect,)* - }); - let constructor = if let Some(constructor) = custom_constructor { + let field_types = derive_data.active_types(); + let MemberValuePair(ignored_members, ignored_values) = + get_ignored_fields(derive_data, is_tuple); + let MemberValuePair(active_members, active_values) = + get_active_fields(derive_data, &ref_struct, &ref_struct_type, is_tuple); + + let constructor = if derive_data.traits().contains("ReflectDefault") { quote!( - let mut value: Self = #constructor; + let mut __this = Self::default(); #( - value.#field_idents = { - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)? - }; + __this.#active_members = #active_values; )* - Some(value) + Some(__this) ) } else { quote!( Some( Self { - #(#field_idents: { - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)? - },)* - #(#ignored_field_idents: Default::default(),)* + #(#active_members: #active_values,)* + #(#ignored_members: #ignored_values,)* } ) ) }; - TokenStream::from(quote! { - impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause - { - fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() { - #constructor - } else { - None - } - } - } - }) -} - -pub fn impl_tuple_struct( - struct_name: &Ident, - generics: &Generics, - bevy_reflect_path: &Path, - active_fields: &[(&Field, usize)], - ignored_fields: &[(&Field, usize)], -) -> TokenStream { - let field_idents = active_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let field_types = active_fields - .iter() - .map(|(field, _index)| field.ty.clone()) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); - let ignored_field_idents = ignored_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // Add FromReflect bound for each active field let mut where_from_reflect_clause = if where_clause.is_some() { quote! {#where_clause} - } else if field_count > 0 { + } else if !active_members.is_empty() { quote! {where} } else { quote! {} @@ -137,16 +95,8 @@ pub fn impl_tuple_struct( impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - use #bevy_reflect_path::TupleStruct; - if let #bevy_reflect_path::ReflectRef::TupleStruct(ref_tuple_struct) = reflect.reflect_ref() { - Some( - Self{ - #(#field_idents: - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_tuple_struct.field(#field_indices)?)? - ,)* - #(#ignored_field_idents: Default::default(),)* - } - ) + if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct) = reflect.reflect_ref() { + #constructor } else { None } @@ -155,13 +105,78 @@ pub fn impl_tuple_struct( }) } -pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream { - 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 { - fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone()) - } - } - }) +/// Get the collection of ignored field definitions +fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> MemberValuePair { + MemberValuePair::new( + derive_data + .ignored_fields() + .map(|field| { + let member = get_ident(field.data, field.index, is_tuple); + let value = quote! { + Default::default() + }; + + (member, value) + }) + .unzip(), + ) +} + +/// Get the collection of active field definitions +fn get_active_fields( + derive_data: &ReflectDeriveData, + dyn_struct_name: &Ident, + struct_type: &Ident, + is_tuple: bool, +) -> MemberValuePair { + let bevy_reflect_path = derive_data.bevy_reflect_path(); + + MemberValuePair::new( + derive_data + .active_fields() + .map(|field| { + let member = get_ident(field.data, field.index, is_tuple); + let accessor = get_field_accessor(field.data, field.index, is_tuple); + let ty = field.data.ty.clone(); + + let value = quote! { { + <#ty as #bevy_reflect_path::FromReflect>::from_reflect( + // Accesses the field on the given dynamic struct or tuple struct + #bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)? + )? + }}; + + (member, value) + }) + .unzip(), + ) +} + +/// Returns the member for a given field of a struct or tuple struct. +fn get_ident(field: &Field, index: usize, is_tuple: bool) -> Member { + if is_tuple { + Member::Unnamed(Index::from(index)) + } else { + field + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(index))) + } +} + +/// Returns the accessor for a given field of a struct or tuple struct. +/// +/// This differs from a member in that it needs to be a number for tuple structs +/// and a string for standard structs. +fn get_field_accessor(field: &Field, index: usize, is_tuple: bool) -> Lit { + if is_tuple { + Lit::Int(LitInt::new(&index.to_string(), Span::call_site())) + } else { + field + .ident + .as_ref() + .map(|ident| Lit::Str(LitStr::new(&ident.to_string(), Span::call_site()))) + .unwrap_or_else(|| Lit::Str(LitStr::new(&index.to_string(), Span::call_site()))) + } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs new file mode 100644 index 0000000000000..18b4c0ab96e4b --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -0,0 +1,410 @@ +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_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = derive_data + .traits() + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let serialize_fn = derive_data + .traits() + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = derive_data + .traits() + .get_partial_eq_impl() + .unwrap_or_else(|| { + quote! { + #bevy_reflect_path::struct_partial_eq(self, value) + } + }); + + 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 + + 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 + } + } + + // SAFE: any and any_mut both return self + unsafe 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 any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn 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) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + } + }) +} + +/// 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_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = derive_data + .traits() + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let serialize_fn = derive_data + .traits() + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = derive_data + .traits() + .get_partial_eq_impl() + .unwrap_or_else(|| { + quote! { + #bevy_reflect_path::tuple_struct_partial_eq(self, value) + } + }); + + let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); + TokenStream::from(quote! { + #get_type_registration_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 + } + } + + // SAFE: any and any_mut both return self + unsafe 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 any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn 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) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + } + }) +} + +/// 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) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = reflect_attrs + .get_partial_eq_impl() + .unwrap_or_else(|| quote!(None)); + let serialize_fn = reflect_attrs + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + TokenStream::from(quote! { + #get_type_registration_impl + + // SAFE: any and any_mut both return self + unsafe 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 any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn 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.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) + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 2b5f93a85edd4..8d59def2c0f9b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -1,680 +1,121 @@ +//! This crate contains macros used by Bevy's `Reflect` API. +//! +//! The main export of this crate is the derive macro for [`Reflect`]. This allows +//! types to easily implement `Reflect` along with other `bevy_reflect` traits, +//! such as `Struct`, `GetTypeRegistration`, and more— all with a single derive! +//! +//! Some other noteworthy exports include the derive macros for [`FromReflect`] and +//! [`TypeUuid`], as well as the [`reflect_trait`] attribute macro. +//! +//! [`Reflect`]: crate::derive_reflect +//! [`FromReflect`]: crate::derive_from_reflect +//! [`TypeUuid`]: crate::derive_type_uuid +//! [`reflect_trait`]: macro@reflect_trait + extern crate proc_macro; +mod container_attributes; +mod derive_data; +mod field_attributes; mod from_reflect; -mod reflect_trait; +mod impls; +mod reflect_value; +mod registration; +mod trait_reflection; mod type_uuid; +mod utility; -use bevy_macro_utils::BevyManifest; +use crate::derive_data::ReflectDeriveData; +use derive_data::DeriveType; use proc_macro::TokenStream; -use proc_macro2::Span; use quote::quote; -use syn::{ - parenthesized, - parse::{Parse, ParseStream}, - parse_macro_input, - punctuated::Punctuated, - token::{Comma, Paren, Where}, - Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, Member, Meta, NestedMeta, - Path, -}; - -#[derive(Default)] -struct PropAttributeArgs { - pub ignore: Option, -} +use reflect_value::ReflectValueDef; +use syn::{parse_macro_input, DeriveInput}; -#[derive(Clone)] -enum TraitImpl { - NotImplemented, - Implemented, - Custom(Ident), -} - -impl Default for TraitImpl { - fn default() -> Self { - Self::NotImplemented - } -} - -enum DeriveType { - Struct, - TupleStruct, - UnitStruct, - Value, -} - -static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; -static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; +pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; +pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; #[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let unit_struct_punctuated = Punctuated::new(); - let (fields, mut derive_type) = match &ast.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => (&fields.named, DeriveType::Struct), - Data::Struct(DataStruct { - fields: Fields::Unnamed(fields), - .. - }) => (&fields.unnamed, DeriveType::TupleStruct), - Data::Struct(DataStruct { - fields: Fields::Unit, - .. - }) => (&unit_struct_punctuated, DeriveType::UnitStruct), - _ => (&unit_struct_punctuated, DeriveType::Value), - }; - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - let type_name = &ast.ident; - - let mut reflect_attrs = ReflectAttrs::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 { - reflect_attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { - derive_type = DeriveType::Value; - reflect_attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } - } - } - - let registration_data = &reflect_attrs.data; - let get_type_registration_impl = impl_get_type_registration( - type_name, - &bevy_reflect_path, - registration_data, - &ast.generics, - ); + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), + }; - match derive_type { - DeriveType::Struct | DeriveType::UnitStruct => impl_struct( - type_name, - &ast.generics, - &get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), - DeriveType::TupleStruct => impl_tuple_struct( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), - DeriveType::Value => impl_value( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, + 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(), ), } } -fn impl_struct( - struct_name: &Ident, - generics: &Generics, - get_type_registration_impl: &proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, - active_fields: &[(&Field, usize)], -) -> TokenStream { - let field_names = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| index.to_string()) - }) - .collect::>(); - let field_idents = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); - - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - let partial_eq_fn = match reflect_attrs.reflect_partial_eq { - TraitImpl::NotImplemented => quote! { - use #bevy_reflect_path::Struct; - #bevy_reflect_path::struct_partial_eq(self, value) - }, - TraitImpl::Implemented | TraitImpl::Custom(_) => reflect_attrs.get_partial_eq_impl(), - }; - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - TokenStream::from(quote! { - #get_type_registration_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 - } - } - - // SAFE: any and any_mut both return self - unsafe 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 any(&self) -> &dyn std::any::Any { - self - } - #[inline] - fn 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) - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - } - }) -} - -fn impl_tuple_struct( - struct_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, - active_fields: &[(&Field, usize)], -) -> TokenStream { - let field_idents = active_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); +/// Derives the `FromReflect` trait. +/// +/// This macro supports the following field attributes: +/// * `#[reflect(ignore)]`: Ignores the field. This requires the field to implement [`Default`]. +/// +#[proc_macro_derive(FromReflect, attributes(reflect))] +pub fn derive_from_reflect(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - let partial_eq_fn = match reflect_attrs.reflect_partial_eq { - TraitImpl::NotImplemented => quote! { - use #bevy_reflect_path::TupleStruct; - #bevy_reflect_path::tuple_struct_partial_eq(self, value) - }, - TraitImpl::Implemented | TraitImpl::Custom(_) => reflect_attrs.get_partial_eq_impl(), + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), }; - let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics { - 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 - } - } - - // SAFE: any and any_mut both return self - unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics { - #[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 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::TupleStruct; - Box::new(self.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) { - use #bevy_reflect_path::TupleStruct; - if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - self.field_mut(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) - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - } - }) + 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(), + ), + } } -fn impl_value( - type_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, -) -> TokenStream { - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = reflect_attrs.get_partial_eq_impl(); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - // SAFE: any and any_mut both return self - unsafe 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 any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn 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.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) - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - } - }) -} -struct ReflectDef { - type_name: Ident, - generics: Generics, - attrs: Option, +// From https://github.com/randomPoison/type-uuid +#[proc_macro_derive(TypeUuid, attributes(uuid))] +pub fn derive_type_uuid(input: TokenStream) -> TokenStream { + type_uuid::type_uuid_derive(input) } -impl Parse for ReflectDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let type_ident = input.parse::()?; - let generics = input.parse::()?; - let mut lookahead = input.lookahead1(); - let mut where_clause = None; - if lookahead.peek(Where) { - where_clause = Some(input.parse()?); - lookahead = input.lookahead1(); - } - - let mut attrs = None; - if lookahead.peek(Paren) { - let content; - parenthesized!(content in input); - attrs = Some(content.parse::()?); - } - - Ok(ReflectDef { - type_name: type_ident, - generics: Generics { - where_clause, - ..generics - }, - attrs, - }) - } +#[proc_macro_attribute] +pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { + trait_reflection::reflect_trait(&args, input) } #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectDef); + let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + let bevy_reflect_path = utility::get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; - let reflect_attrs = reflect_value_def.attrs.unwrap_or_default(); - let registration_data = &reflect_attrs.data; - let get_type_registration_impl = impl_get_type_registration( + 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, ); - impl_value( + impls::impl_value( ty, &reflect_value_def.generics, get_type_registration_impl, &bevy_reflect_path, - &reflect_attrs, + &reflect_traits, ) } -/// Represents the information needed to implement a type as a Reflect Struct. -/// -/// # Example -/// ```ignore -/// impl_reflect_struct!( -/// // attrs -/// // |----------------------------------------| -/// #[reflect(PartialEq, Serialize, Deserialize, Default)] -/// // type_name generics -/// // |-------------------||----------| -/// struct ThingThatImReflecting { -/// x: T1, // | -/// y: T2, // |- fields -/// z: T3 // | -/// } -/// ); -/// ``` -struct ReflectStructDef { - type_name: Ident, - generics: Generics, - attrs: ReflectAttrs, - fields: Fields, -} - -impl Parse for ReflectStructDef { - fn parse(input: ParseStream) -> syn::Result { - let ast = input.parse::()?; - - let type_name = ast.ident; - let generics = ast.generics; - let fields = match ast.data { - Data::Struct(data) => data.fields, - Data::Enum(data) => { - return Err(syn::Error::new_spanned( - data.enum_token, - "Enums are not currently supported for reflection", - )) - } - Data::Union(data) => { - return Err(syn::Error::new_spanned( - data.union_token, - "Unions are not supported for reflection", - )) - } - }; - - let mut attrs = ReflectAttrs::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 || ident == REFLECT_VALUE_ATTRIBUTE_NAME { - attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } - } - } - - Ok(Self { - type_name, - generics, - attrs, - fields, - }) - } -} - /// A replacement for `#[derive(Reflect)]` to be used with foreign types which /// the definitions of cannot be altered. /// @@ -705,96 +146,14 @@ impl Parse for ReflectStructDef { /// ``` #[proc_macro] pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { - let ReflectStructDef { - type_name, - generics, - attrs, - fields, - } = parse_macro_input!(input as ReflectStructDef); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - let ignored_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs - .as_ref() - .map(|attrs| attrs.ignore.unwrap_or(false)) - .unwrap_or(false) - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let constructor = if attrs - .data - .contains(&Ident::new("ReflectDefault", Span::call_site())) - { - Some(quote! { Default::default() }) - } else { - None + let ast = parse_macro_input!(input as DeriveInput); + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), }; - let registration_data = &attrs.data; - let get_type_registration_impl = - impl_get_type_registration(&type_name, &bevy_reflect_path, registration_data, &generics); - - let impl_struct: proc_macro2::TokenStream = impl_struct( - &type_name, - &generics, - &get_type_registration_impl, - &bevy_reflect_path, - &attrs, - &active_fields, - ) - .into(); - - let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct( - &type_name, - &generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, - constructor, - ) - .into(); + let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&derive_data).into(); + let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(&derive_data).into(); TokenStream::from(quote! { #impl_struct @@ -803,279 +162,11 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { }) } -#[derive(Default)] -struct ReflectAttrs { - reflect_hash: TraitImpl, - reflect_partial_eq: TraitImpl, - serialize: TraitImpl, - data: Vec, -} - -impl ReflectAttrs { - fn from_nested_metas(nested_metas: &Punctuated) -> Self { - let mut attrs = ReflectAttrs::default(); - for nested_meta in nested_metas.iter() { - match nested_meta { - NestedMeta::Lit(_) => {} - NestedMeta::Meta(meta) => match meta { - Meta::Path(path) => { - if let Some(segment) = path.segments.iter().next() { - let ident = segment.ident.to_string(); - match ident.as_str() { - "PartialEq" => attrs.reflect_partial_eq = TraitImpl::Implemented, - "Hash" => attrs.reflect_hash = TraitImpl::Implemented, - "Serialize" => attrs.serialize = TraitImpl::Implemented, - _ => attrs.data.push(Ident::new( - &format!("Reflect{}", segment.ident), - Span::call_site(), - )), - } - } - } - Meta::List(list) => { - let ident = if let Some(segment) = list.path.segments.iter().next() { - segment.ident.to_string() - } else { - continue; - }; - - if let Some(list_nested) = list.nested.iter().next() { - match list_nested { - NestedMeta::Meta(list_nested_meta) => match list_nested_meta { - Meta::Path(path) => { - if let Some(segment) = path.segments.iter().next() { - match ident.as_str() { - "PartialEq" => { - attrs.reflect_partial_eq = - TraitImpl::Custom(segment.ident.clone()); - } - "Hash" => { - attrs.reflect_hash = - TraitImpl::Custom(segment.ident.clone()); - } - "Serialize" => { - attrs.serialize = - TraitImpl::Custom(segment.ident.clone()); - } - _ => {} - } - } - } - Meta::List(_) => {} - Meta::NameValue(_) => {} - }, - NestedMeta::Lit(_) => {} - } - } - } - Meta::NameValue(_) => {} - }, - } - } - - attrs - } - - fn get_hash_impl(&self, path: &Path) -> proc_macro2::TokenStream { - match &self.reflect_hash { - TraitImpl::Implemented => quote! { - use std::hash::{Hash, Hasher}; - let mut hasher = #path::ReflectHasher::default(); - Hash::hash(&std::any::Any::type_id(self), &mut hasher); - Hash::hash(self, &mut hasher); - Some(hasher.finish()) - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } - - fn get_partial_eq_impl(&self) -> proc_macro2::TokenStream { - match &self.reflect_partial_eq { - TraitImpl::Implemented => quote! { - let value = value.any(); - if let Some(value) = value.downcast_ref::() { - Some(std::cmp::PartialEq::eq(self, value)) - } else { - Some(false) - } - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self, value)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } - - fn get_serialize_impl(&self, path: &Path) -> proc_macro2::TokenStream { - match &self.serialize { - TraitImpl::Implemented => quote! { - Some(#path::serde::Serializable::Borrowed(self)) - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } -} - -impl Parse for ReflectAttrs { - fn parse(input: ParseStream) -> syn::Result { - let result = Punctuated::::parse_terminated(input)?; - Ok(ReflectAttrs::from_nested_metas(&result)) - } -} - -fn impl_get_type_registration( - type_name: &Ident, - bevy_reflect_path: &Path, - registration_data: &[Ident], - generics: &Generics, -) -> proc_macro2::TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - quote! { - #[allow(unused_mut)] - impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { - fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { - let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); - #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* - registration - } - } - } -} - -// From https://github.com/randomPoison/type-uuid -#[proc_macro_derive(TypeUuid, attributes(uuid))] -pub fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - type_uuid::type_uuid_derive(input) -} - -#[proc_macro_attribute] -pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { - reflect_trait::reflect_trait(&args, input) -} - -#[proc_macro_derive(FromReflect)] -pub fn derive_from_reflect(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as DeriveInput); - let unit_struct_punctuated = Punctuated::new(); - let (fields, mut derive_type) = match &ast.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => (&fields.named, DeriveType::Struct), - Data::Struct(DataStruct { - fields: Fields::Unnamed(fields), - .. - }) => (&fields.unnamed, DeriveType::TupleStruct), - Data::Struct(DataStruct { - fields: Fields::Unit, - .. - }) => (&unit_struct_punctuated, DeriveType::UnitStruct), - _ => (&unit_struct_punctuated, DeriveType::Value), - }; - - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - let ignored_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs - .as_ref() - .map(|attrs| attrs.ignore.unwrap_or(false)) - .unwrap_or(false) - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - let type_name = &ast.ident; - - 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_VALUE_ATTRIBUTE_NAME { - derive_type = DeriveType::Value; - } - } - } - - match derive_type { - DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct( - type_name, - &ast.generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, - None, - ), - DeriveType::TupleStruct => from_reflect::impl_tuple_struct( - type_name, - &ast.generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, - ), - DeriveType::Value => from_reflect::impl_value(type_name, &ast.generics, &bevy_reflect_path), - } -} - #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectDef); + let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + 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) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs new file mode 100644 index 0000000000000..ec54b99a6f404 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -0,0 +1,54 @@ +use crate::container_attributes::ReflectTraits; +use proc_macro2::Ident; +use syn::parse::{Parse, ParseStream}; +use syn::token::{Paren, Where}; +use syn::{parenthesized, Generics}; + +/// A struct used to define a simple reflected value type (such as primitives). +/// +/// This takes the form: +/// +/// ```ignore +/// // Standard +/// foo(TraitA, TraitB) +/// +/// // With generics +/// foo(TraitA, TraitB) +/// +/// // With generics and where clause +/// foo where T1: Bar (TraitA, TraitB) +/// ``` +pub(crate) struct ReflectValueDef { + pub type_name: Ident, + pub generics: Generics, + pub traits: Option, +} + +impl Parse for ReflectValueDef { + fn parse(input: ParseStream) -> syn::Result { + let type_ident = input.parse::()?; + let generics = input.parse::()?; + let mut lookahead = input.lookahead1(); + let mut where_clause = None; + if lookahead.peek(Where) { + where_clause = Some(input.parse()?); + lookahead = input.lookahead1(); + } + + let mut traits = None; + if lookahead.peek(Paren) { + let content; + parenthesized!(content in input); + traits = Some(content.parse::()?); + } + + Ok(ReflectValueDef { + type_name: type_ident, + generics: Generics { + where_clause, + ..generics + }, + traits, + }) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs new file mode 100644 index 0000000000000..ea3157242211e --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -0,0 +1,25 @@ +//! Contains code related specifically to Bevy's type registration. + +use proc_macro2::Ident; +use quote::quote; +use syn::{Generics, Path}; + +/// Creates the `GetTypeRegistration` impl for the given type data. +pub(crate) fn impl_get_type_registration( + type_name: &Ident, + bevy_reflect_path: &Path, + registration_data: &[Ident], + generics: &Generics, +) -> proc_macro2::TokenStream { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + quote! { + #[allow(unused_mut)] + impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { + fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { + let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); + #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* + registration + } + } + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs b/crates/bevy_reflect/bevy_reflect_derive/src/trait_reflection.rs similarity index 87% rename from crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs rename to crates/bevy_reflect/bevy_reflect_derive/src/trait_reflection.rs index c3632c2d3ef49..fe4d4a6bb257e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/trait_reflection.rs @@ -1,10 +1,9 @@ use bevy_macro_utils::BevyManifest; use proc_macro::TokenStream; -use proc_macro2::Span; use quote::quote; -use syn::{parse::Parse, parse_macro_input, Attribute, Ident, ItemTrait, Token}; +use syn::{parse::Parse, parse_macro_input, Attribute, ItemTrait, Token}; -pub struct TraitInfo { +pub(crate) struct TraitInfo { item_trait: ItemTrait, } @@ -22,13 +21,12 @@ impl Parse for TraitInfo { } } -pub fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream { +pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream { let trait_info = parse_macro_input!(input as TraitInfo); let item_trait = &trait_info.item_trait; let trait_ident = &item_trait.ident; let trait_vis = &item_trait.vis; - let reflect_trait_ident = - Ident::new(&format!("Reflect{}", item_trait.ident), Span::call_site()); + let reflect_trait_ident = crate::utility::get_reflect_ident(&item_trait.ident.to_string()); let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); TokenStream::from(quote! { #item_trait diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs b/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs index 382036a9f3713..8adb2dbcdad7e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs @@ -5,7 +5,7 @@ use quote::quote; use syn::*; use uuid::Uuid; -pub fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub(crate) fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Construct a representation of Rust code as a syntax tree // that we can manipulate let mut ast: DeriveInput = syn::parse(input).unwrap(); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs new file mode 100644 index 0000000000000..34fbdf186cc94 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -0,0 +1,23 @@ +//! General-purpose utility functions for internal usage within this crate. + +use bevy_macro_utils::BevyManifest; +use proc_macro2::{Ident, Span}; +use syn::Path; + +/// Returns the correct path for `bevy_reflect`. +pub(crate) fn get_bevy_reflect_path() -> Path { + BevyManifest::get_path_direct("bevy_reflect") +} + +/// Returns the "reflected" ident for a given string. +/// +/// # Example +/// +/// ```ignore +/// let reflected: Ident = get_reflect_ident("Hash"); +/// assert_eq!("ReflectHash", reflected.to_string()); +/// ``` +pub(crate) fn get_reflect_ident(name: &str) -> Ident { + let reflected = format!("Reflect{}", name); + Ident::new(&reflected, Span::call_site()) +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index d8f7b42d8e80a..90def413de98b 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -85,7 +85,6 @@ mod tests { #[cfg(feature = "glam")] use ::glam::{vec3, Vec3}; use ::serde::de::DeserializeSeed; - use ::serde::Serialize; use bevy_utils::HashMap; use ron::{ ser::{to_string_pretty, PrettyConfig}, @@ -478,6 +477,7 @@ mod tests { #[cfg(feature = "glam")] mod glam { use super::*; + use ::serde::Serialize; #[test] fn vec3_serialization() {