Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - bevy_reflect_derive: Tidying up the code #4712

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
155 changes: 155 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs
@@ -0,0 +1,155 @@
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Meta, NestedMeta, Path};

#[derive(Clone)]
pub enum TraitImpl {
MrGVSV marked this conversation as resolved.
Show resolved Hide resolved
NotImplemented,
Implemented,
Custom(Ident),
MrGVSV marked this conversation as resolved.
Show resolved Hide resolved
}

impl Default for TraitImpl {
fn default() -> Self {
Self::NotImplemented
}
}

#[derive(Default)]
pub struct ReflectAttrs {
MrGVSV marked this conversation as resolved.
Show resolved Hide resolved
reflect_hash: TraitImpl,
pub(crate) reflect_partial_eq: TraitImpl,
serialize: TraitImpl,
data: Vec<Ident>,
}

impl ReflectAttrs {
pub fn from_nested_metas(nested_metas: &Punctuated<NestedMeta, Comma>) -> 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) => {
MrGVSV marked this conversation as resolved.
Show resolved Hide resolved
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() {
MrGVSV marked this conversation as resolved.
Show resolved Hide resolved
"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
}

pub fn data(&self) -> &[Ident] {
&self.data
}

pub 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
},
}
}

pub 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::<Self>() {
Some(std::cmp::PartialEq::eq(self, value))
} else {
Some(false)
}
},
TraitImpl::Custom(impl_fn) => quote! {
Some(#impl_fn(self, value))
},
TraitImpl::NotImplemented => quote! {
None
},
}
}

pub 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<Self> {
let result = Punctuated::<NestedMeta, Comma>::parse_terminated(input)?;
Ok(ReflectAttrs::from_nested_metas(&result))
}
}
198 changes: 198 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs
@@ -0,0 +1,198 @@
use crate::container_attributes::ReflectAttrs;
use crate::field_attributes::{
parse_field_attrs, ReflectFieldAttr, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME,
};
use crate::utility::get_bevy_reflect_path;
use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path};

pub enum DeriveType {
Struct,
TupleStruct,
UnitStruct,
Value,
}

/// Represents a field on a struct or tuple struct.
pub 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<T1, T2, T3> {
/// x: T1, // |
/// y: T2, // |- fields
/// z: T3 // |
/// }
/// ```
pub struct ReflectDeriveData<'a> {
derive_type: DeriveType,
attrs: ReflectAttrs,
type_name: &'a Ident,
generics: &'a Generics,
fields: Vec<StructField<'a>>,
bevy_reflect_path: Path,
}

impl<'a> ReflectDeriveData<'a> {
pub fn from_input(input: &'a DeriveInput) -> Result<Self, syn::Error> {
let mut output = Self {
type_name: &input.ident,
derive_type: DeriveType::Value,
generics: &input.generics,
fields: Vec::new(),
attrs: ReflectAttrs::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.attrs = ReflectAttrs::from_nested_metas(&meta_list.nested);
} else if ident == REFLECT_VALUE_ATTRIBUTE_NAME {
force_reflect_value = true;
output.attrs = ReflectAttrs::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<syn::Error> = 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::<Vec<StructField>>();
if let Some(errs) = errors {
return Err(errs);
}

Ok(output)
}

/// Get an iterator over the active fields
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields.iter().filter(|field| !field.attrs.ignore)
}

/// Get an iterator over the ignored fields
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields.iter().filter(|field| field.attrs.ignore)
}

/// Get a collection of all active types
pub fn active_types(&self) -> Vec<syn::Type> {
self.active_fields()
.map(|field| field.data.ty.clone())
.collect::<Vec<_>>()
}

/// The [`DeriveType`] of this struct.
pub fn derive_type(&self) -> &DeriveType {
&self.derive_type
}

/// The list of reflect-related attributes on this struct.
pub fn attrs(&self) -> &ReflectAttrs {
&self.attrs
}

/// 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.attrs.data(),
self.generics,
)
}
}