Skip to content

Commit

Permalink
Add FromReflect trait to convert dynamic types to concrete types
Browse files Browse the repository at this point in the history
  • Loading branch information
Davier committed Dec 16, 2021
1 parent 7356f15 commit a3a3522
Show file tree
Hide file tree
Showing 11 changed files with 490 additions and 29 deletions.
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 {
} 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

0 comments on commit a3a3522

Please sign in to comment.