Skip to content

Commit a3a3522

Browse files
committed
Add FromReflect trait to convert dynamic types to concrete types
1 parent 7356f15 commit a3a3522

File tree

11 files changed

+490
-29
lines changed

11 files changed

+490
-29
lines changed

crates/bevy_asset/src/handle.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,25 @@ use crate::{
1010
Asset, Assets,
1111
};
1212
use bevy_ecs::{component::Component, reflect::ReflectComponent};
13-
use bevy_reflect::{Reflect, ReflectDeserialize};
13+
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
1414
use bevy_utils::Uuid;
1515
use crossbeam_channel::{Receiver, Sender};
1616
use serde::{Deserialize, Serialize};
1717

1818
/// A unique, stable asset id
1919
#[derive(
20-
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
20+
Debug,
21+
Clone,
22+
Copy,
23+
Eq,
24+
PartialEq,
25+
Hash,
26+
Ord,
27+
PartialOrd,
28+
Serialize,
29+
Deserialize,
30+
Reflect,
31+
FromReflect,
2132
)]
2233
#[reflect_value(Serialize, Deserialize, PartialEq, Hash)]
2334
pub enum HandleId {
@@ -58,7 +69,7 @@ impl HandleId {
5869
///
5970
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
6071
/// collection.
61-
#[derive(Component, Reflect)]
72+
#[derive(Component, Reflect, FromReflect)]
6273
#[reflect(Component)]
6374
pub struct Handle<T>
6475
where
@@ -77,6 +88,13 @@ enum HandleType {
7788
Strong(Sender<RefChange>),
7889
}
7990

91+
// FIXME: This only is needed because `Handle`'s field `handle_type` is currently ignored for reflection
92+
impl Default for HandleType {
93+
fn default() -> Self {
94+
Self::Weak
95+
}
96+
}
97+
8098
impl Debug for HandleType {
8199
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82100
match self {

crates/bevy_ecs/src/reflect.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::{
66
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
77
world::{FromWorld, World},
88
};
9-
use bevy_reflect::{impl_reflect_value, FromType, Reflect, ReflectDeserialize};
9+
use bevy_reflect::{
10+
impl_from_reflect_value, impl_reflect_value, FromType, Reflect, ReflectDeserialize,
11+
};
1012

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

123125
impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize));
126+
impl_from_reflect_value!(Entity);
124127

125128
#[derive(Clone)]
126129
pub struct ReflectMapEntities {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
use proc_macro::TokenStream;
2+
use quote::quote;
3+
use syn::{Field, Generics, Ident, Index, Member, Path};
4+
5+
pub fn impl_struct(
6+
struct_name: &Ident,
7+
generics: &Generics,
8+
bevy_reflect_path: &Path,
9+
active_fields: &[(&Field, usize)],
10+
ignored_fields: &[(&Field, usize)],
11+
) -> TokenStream {
12+
let field_names = active_fields
13+
.iter()
14+
.map(|(field, index)| {
15+
field
16+
.ident
17+
.as_ref()
18+
.map(|i| i.to_string())
19+
.unwrap_or_else(|| index.to_string())
20+
})
21+
.collect::<Vec<String>>();
22+
let field_idents = active_fields
23+
.iter()
24+
.map(|(field, index)| {
25+
field
26+
.ident
27+
.as_ref()
28+
.map(|ident| Member::Named(ident.clone()))
29+
.unwrap_or_else(|| Member::Unnamed(Index::from(*index)))
30+
})
31+
.collect::<Vec<_>>();
32+
33+
let field_types = active_fields
34+
.iter()
35+
.map(|(field, _index)| field.ty.clone())
36+
.collect::<Vec<_>>();
37+
let field_count = active_fields.len();
38+
let ignored_field_idents = ignored_fields
39+
.iter()
40+
.map(|(field, index)| {
41+
field
42+
.ident
43+
.as_ref()
44+
.map(|ident| Member::Named(ident.clone()))
45+
.unwrap_or_else(|| Member::Unnamed(Index::from(*index)))
46+
})
47+
.collect::<Vec<_>>();
48+
49+
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
50+
51+
// Add FromReflect bound for each active field
52+
let mut where_from_reflect_clause = if where_clause.is_some() {
53+
quote! {#where_clause}
54+
} else if field_count > 0 {
55+
quote! {where}
56+
} else {
57+
quote! {}
58+
};
59+
where_from_reflect_clause.extend(quote! {
60+
#(#field_types: #bevy_reflect_path::FromReflect,)*
61+
});
62+
63+
TokenStream::from(quote! {
64+
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
65+
{
66+
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
67+
use #bevy_reflect_path::Struct;
68+
if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() {
69+
Some(
70+
Self{
71+
#(#field_idents: {
72+
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_struct.field(#field_names)?)?
73+
},)*
74+
#(#ignored_field_idents: Default::default(),)*
75+
}
76+
)
77+
} else {
78+
None
79+
}
80+
}
81+
}
82+
})
83+
}
84+
85+
pub fn impl_tuple_struct(
86+
struct_name: &Ident,
87+
generics: &Generics,
88+
bevy_reflect_path: &Path,
89+
active_fields: &[(&Field, usize)],
90+
ignored_fields: &[(&Field, usize)],
91+
) -> TokenStream {
92+
let field_idents = active_fields
93+
.iter()
94+
.map(|(_field, index)| Member::Unnamed(Index::from(*index)))
95+
.collect::<Vec<_>>();
96+
let field_types = active_fields
97+
.iter()
98+
.map(|(field, _index)| field.ty.clone())
99+
.collect::<Vec<_>>();
100+
let field_count = active_fields.len();
101+
let field_indices = (0..field_count).collect::<Vec<usize>>();
102+
let ignored_field_idents = ignored_fields
103+
.iter()
104+
.map(|(_field, index)| Member::Unnamed(Index::from(*index)))
105+
.collect::<Vec<_>>();
106+
107+
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
108+
// Add FromReflect bound for each active field
109+
let mut where_from_reflect_clause = if where_clause.is_some() {
110+
quote! {#where_clause}
111+
} else if field_count > 0 {
112+
quote! {where}
113+
} else {
114+
quote! {}
115+
};
116+
where_from_reflect_clause.extend(quote! {
117+
#(#field_types: #bevy_reflect_path::FromReflect,)*
118+
});
119+
120+
TokenStream::from(quote! {
121+
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
122+
{
123+
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
124+
use #bevy_reflect_path::TupleStruct;
125+
if let #bevy_reflect_path::ReflectRef::TupleStruct(ref_tuple_struct) = reflect.reflect_ref() {
126+
Some(
127+
Self{
128+
#(#field_idents:
129+
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_tuple_struct.field(#field_indices)?)?
130+
,)*
131+
#(#ignored_field_idents: Default::default(),)*
132+
}
133+
)
134+
} else {
135+
None
136+
}
137+
}
138+
}
139+
})
140+
}
141+
142+
pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream {
143+
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
144+
TokenStream::from(quote! {
145+
impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause {
146+
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
147+
Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone())
148+
}
149+
}
150+
})
151+
}

crates/bevy_reflect/bevy_reflect_derive/src/lib.rs

+116
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate proc_macro;
22

3+
mod from_reflect;
34
mod reflect_trait;
45
mod type_uuid;
56

@@ -740,3 +741,118 @@ pub fn external_type_uuid(tokens: proc_macro::TokenStream) -> proc_macro::TokenS
740741
pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
741742
reflect_trait::reflect_trait(args, input)
742743
}
744+
745+
#[proc_macro_derive(FromReflect)]
746+
pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
747+
let ast = parse_macro_input!(input as DeriveInput);
748+
let unit_struct_punctuated = Punctuated::new();
749+
let (fields, mut derive_type) = match &ast.data {
750+
Data::Struct(DataStruct {
751+
fields: Fields::Named(fields),
752+
..
753+
}) => (&fields.named, DeriveType::Struct),
754+
Data::Struct(DataStruct {
755+
fields: Fields::Unnamed(fields),
756+
..
757+
}) => (&fields.unnamed, DeriveType::TupleStruct),
758+
Data::Struct(DataStruct {
759+
fields: Fields::Unit,
760+
..
761+
}) => (&unit_struct_punctuated, DeriveType::UnitStruct),
762+
_ => (&unit_struct_punctuated, DeriveType::Value),
763+
};
764+
765+
let fields_and_args = fields
766+
.iter()
767+
.enumerate()
768+
.map(|(i, f)| {
769+
(
770+
f,
771+
f.attrs
772+
.iter()
773+
.find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME)
774+
.map(|a| {
775+
syn::custom_keyword!(ignore);
776+
let mut attribute_args = PropAttributeArgs { ignore: None };
777+
a.parse_args_with(|input: ParseStream| {
778+
if input.parse::<Option<ignore>>()?.is_some() {
779+
attribute_args.ignore = Some(true);
780+
return Ok(());
781+
}
782+
Ok(())
783+
})
784+
.expect("Invalid 'property' attribute format.");
785+
786+
attribute_args
787+
}),
788+
i,
789+
)
790+
})
791+
.collect::<Vec<(&Field, Option<PropAttributeArgs>, usize)>>();
792+
let active_fields = fields_and_args
793+
.iter()
794+
.filter(|(_field, attrs, _i)| {
795+
attrs.is_none()
796+
|| match attrs.as_ref().unwrap().ignore {
797+
Some(ignore) => !ignore,
798+
None => true,
799+
}
800+
})
801+
.map(|(f, _attr, i)| (*f, *i))
802+
.collect::<Vec<(&Field, usize)>>();
803+
let ignored_fields = fields_and_args
804+
.iter()
805+
.filter(|(_field, attrs, _i)| {
806+
attrs
807+
.as_ref()
808+
.map(|attrs| attrs.ignore.unwrap_or(false))
809+
.unwrap_or(false)
810+
})
811+
.map(|(f, _attr, i)| (*f, *i))
812+
.collect::<Vec<(&Field, usize)>>();
813+
814+
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
815+
let type_name = &ast.ident;
816+
817+
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
818+
let meta_list = if let Meta::List(meta_list) = attribute {
819+
meta_list
820+
} else {
821+
continue;
822+
};
823+
824+
if let Some(ident) = meta_list.path.get_ident() {
825+
if ident == REFLECT_ATTRIBUTE_NAME {
826+
} else if ident == REFLECT_VALUE_ATTRIBUTE_NAME {
827+
derive_type = DeriveType::Value;
828+
}
829+
}
830+
}
831+
832+
match derive_type {
833+
DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(
834+
type_name,
835+
&ast.generics,
836+
&bevy_reflect_path,
837+
&active_fields,
838+
&ignored_fields,
839+
),
840+
DeriveType::TupleStruct => from_reflect::impl_tuple_struct(
841+
type_name,
842+
&ast.generics,
843+
&bevy_reflect_path,
844+
&active_fields,
845+
&ignored_fields,
846+
),
847+
DeriveType::Value => from_reflect::impl_value(type_name, &ast.generics, &bevy_reflect_path),
848+
}
849+
}
850+
851+
#[proc_macro]
852+
pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
853+
let reflect_value_def = parse_macro_input!(input as ReflectDef);
854+
855+
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
856+
let ty = &reflect_value_def.type_name;
857+
from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path)
858+
}

crates/bevy_reflect/src/impls/glam.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate as bevy_reflect;
22
use crate::ReflectDeserialize;
3-
use bevy_reflect_derive::impl_reflect_value;
3+
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
44
use glam::{IVec2, IVec3, IVec4, Mat3, Mat4, Quat, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4};
55

66
impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize));
@@ -15,3 +15,16 @@ impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize));
1515
impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize));
1616
impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize));
1717
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize));
18+
19+
impl_from_reflect_value!(IVec2);
20+
impl_from_reflect_value!(IVec3);
21+
impl_from_reflect_value!(IVec4);
22+
impl_from_reflect_value!(UVec2);
23+
impl_from_reflect_value!(UVec3);
24+
impl_from_reflect_value!(UVec4);
25+
impl_from_reflect_value!(Vec2);
26+
impl_from_reflect_value!(Vec3);
27+
impl_from_reflect_value!(Vec4);
28+
impl_from_reflect_value!(Mat3);
29+
impl_from_reflect_value!(Mat4);
30+
impl_from_reflect_value!(Quat);

0 commit comments

Comments
 (0)