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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Add FromReflect trait to convert dynamic types to concrete types #1395

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions crates/bevy_asset/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,25 @@ use crate::{
Asset, Assets,
};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
use bevy_utils::Uuid;
use crossbeam_channel::{Receiver, Sender};
use serde::{Deserialize, Serialize};

/// A unique, stable asset id
#[derive(
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
Debug,
Clone,
Copy,
Eq,
PartialEq,
Hash,
Ord,
PartialOrd,
Serialize,
Deserialize,
Reflect,
FromReflect,
)]
#[reflect_value(Serialize, Deserialize, PartialEq, Hash)]
pub enum HandleId {
Expand Down Expand Up @@ -58,7 +69,7 @@ impl HandleId {
///
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
/// collection.
#[derive(Component, Reflect)]
#[derive(Component, Reflect, FromReflect)]
#[reflect(Component)]
pub struct Handle<T>
where
Expand All @@ -77,6 +88,13 @@ enum HandleType {
Strong(Sender<RefChange>),
}

// FIXME: This only is needed because `Handle`'s field `handle_type` is currently ignored for reflection
impl Default for HandleType {
fn default() -> Self {
Self::Weak
}
}

impl Debug for HandleType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_ecs/src/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::{
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
world::{FromWorld, World},
};
use bevy_reflect::{impl_reflect_value, FromType, Reflect, ReflectDeserialize};
use bevy_reflect::{
impl_from_reflect_value, impl_reflect_value, FromType, Reflect, ReflectDeserialize,
};

#[derive(Clone)]
pub struct ReflectComponent {
Expand Down Expand Up @@ -121,6 +123,7 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
}

impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize));
impl_from_reflect_value!(Entity);

#[derive(Clone)]
pub struct ReflectMapEntities {
Expand Down
151 changes: 151 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{Field, Generics, Ident, Index, Member, Path};

pub fn impl_struct(
struct_name: &Ident,
generics: &Generics,
bevy_reflect_path: &Path,
active_fields: &[(&Field, usize)],
ignored_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::<Vec<String>>();
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::<Vec<_>>();

let field_types = active_fields
.iter()
.map(|(field, _index)| field.ty.clone())
.collect::<Vec<_>>();
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::<Vec<_>>();

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 {
quote! {where}
} else {
quote! {}
};
where_from_reflect_clause.extend(quote! {
#(#field_types: #bevy_reflect_path::FromReflect,)*
});

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<Self> {
use #bevy_reflect_path::Struct;
if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() {
Some(
Self{
#(#field_idents: {
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_struct.field(#field_names)?)?
},)*
#(#ignored_field_idents: Default::default(),)*
}
)
} 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::<Vec<_>>();
let field_types = active_fields
.iter()
.map(|(field, _index)| field.ty.clone())
.collect::<Vec<_>>();
let field_count = active_fields.len();
let field_indices = (0..field_count).collect::<Vec<usize>>();
let ignored_field_idents = ignored_fields
.iter()
.map(|(_field, index)| Member::Unnamed(Index::from(*index)))
.collect::<Vec<_>>();

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 {
quote! {where}
} else {
quote! {}
};
where_from_reflect_clause.extend(quote! {
#(#field_types: #bevy_reflect_path::FromReflect,)*
});

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<Self> {
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(),)*
}
)
} else {
None
}
}
}
})
}

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<Self> {
Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone())
}
}
})
}
116 changes: 116 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extern crate proc_macro;

mod from_reflect;
mod reflect_trait;
mod type_uuid;

Expand Down Expand Up @@ -740,3 +741,118 @@ pub fn external_type_uuid(tokens: proc_macro::TokenStream) -> proc_macro::TokenS
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::<Option<ignore>>()?.is_some() {
attribute_args.ignore = Some(true);
return Ok(());
}
Ok(())
})
.expect("Invalid 'property' attribute format.");

attribute_args
}),
i,
)
})
.collect::<Vec<(&Field, Option<PropAttributeArgs>, 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::<Vec<(&Field, usize)>>();
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::<Vec<(&Field, usize)>>();

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_ATTRIBUTE_NAME {
Davier marked this conversation as resolved.
Show resolved Hide resolved
} else 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,
),
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 bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
let ty = &reflect_value_def.type_name;
from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path)
}
15 changes: 14 additions & 1 deletion crates/bevy_reflect/src/impls/glam.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate as bevy_reflect;
use crate::ReflectDeserialize;
use bevy_reflect_derive::impl_reflect_value;
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
use glam::{IVec2, IVec3, IVec4, Mat3, Mat4, Quat, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4};

impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize));
Expand All @@ -15,3 +15,16 @@ impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize));

impl_from_reflect_value!(IVec2);
impl_from_reflect_value!(IVec3);
impl_from_reflect_value!(IVec4);
impl_from_reflect_value!(UVec2);
impl_from_reflect_value!(UVec3);
impl_from_reflect_value!(UVec4);
impl_from_reflect_value!(Vec2);
impl_from_reflect_value!(Vec3);
impl_from_reflect_value!(Vec4);
impl_from_reflect_value!(Mat3);
impl_from_reflect_value!(Mat4);
impl_from_reflect_value!(Quat);
Loading