# Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
411 lines
14 KiB
Rust
411 lines
14 KiB
Rust
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::<Vec<String>>();
|
|
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::<Vec<_>>();
|
|
let field_count = field_idents.len();
|
|
let field_indices = (0..field_count).collect::<Vec<usize>>();
|
|
|
|
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::<Self>()
|
|
}
|
|
|
|
#[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<dyn #bevy_reflect_path::Reflect> {
|
|
Box::new(#bevy_reflect_path::Struct::clone_dynamic(self))
|
|
}
|
|
#[inline]
|
|
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
|
*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<u64> {
|
|
#hash_fn
|
|
}
|
|
|
|
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option<bool> {
|
|
#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::<Vec<_>>();
|
|
let field_count = field_idents.len();
|
|
let field_indices = (0..field_count).collect::<Vec<usize>>();
|
|
|
|
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::<Self>()
|
|
}
|
|
|
|
#[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<dyn #bevy_reflect_path::Reflect> {
|
|
Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self))
|
|
}
|
|
#[inline]
|
|
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
|
*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<u64> {
|
|
#hash_fn
|
|
}
|
|
|
|
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option<bool> {
|
|
#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::<Self>()
|
|
}
|
|
|
|
#[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<dyn #bevy_reflect_path::Reflect> {
|
|
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>() {
|
|
*self = value.clone();
|
|
} else {
|
|
panic!("Value is not {}.", std::any::type_name::<Self>());
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
|
*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<u64> {
|
|
#hash_fn
|
|
}
|
|
|
|
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option<bool> {
|
|
#partial_eq_fn
|
|
}
|
|
|
|
fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> {
|
|
#serialize_fn
|
|
}
|
|
}
|
|
})
|
|
}
|