Skip to content

Commit c6604aa

Browse files
PROMETHIA-27ItsDoot
authored andcommitted
Add macro to implement reflect for struct types and migrate glam types (bevyengine#4540)
# Objective Relevant issue: bevyengine#4474 Currently glam types implement Reflect as a value, which is problematic for reflection, making scripting/editor work much more difficult. This PR re-implements them as structs. ## Solution Added a new proc macro, `impl_reflect_struct`, which replaces `impl_reflect_value` and `impl_from_reflect_value` for glam types. This macro could also be used for other types, but I don't know of any that would require it. It's specifically useful for foreign types that cannot derive Reflect normally. --- ## Changelog ### Added - `impl_reflect_struct` proc macro ### Changed - Glam reflect impls have been replaced with `impl_reflect_struct` - from_reflect's `impl_struct` altered to take an optional custom constructor, allowing non-default non-constructible foreign types to use it - Calls to `impl_struct` altered to conform to new signature - Altered glam types (All vec/mat combinations) have a different serialization structure, as they are reflected differently now. ## Migration Guide This will break altered glam types serialized to RON scenes, as they will expect to be serialized/deserialized as structs rather than values now. A future PR to add custom serialization for non-value types is likely on the way to restore previous behavior. Additionally, calls to `impl_struct` must add a `None` parameter to the end of the call to restore previous behavior. Co-authored-by: PROMETHIA-27 <[email protected]>
1 parent 8cd9999 commit c6604aa

File tree

4 files changed

+471
-51
lines changed

4 files changed

+471
-51
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs

+25-9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub fn impl_struct(
88
bevy_reflect_path: &Path,
99
active_fields: &[(&Field, usize)],
1010
ignored_fields: &[(&Field, usize)],
11+
custom_constructor: Option<proc_macro2::TokenStream>,
1112
) -> TokenStream {
1213
let field_names = active_fields
1314
.iter()
@@ -60,20 +61,35 @@ pub fn impl_struct(
6061
#(#field_types: #bevy_reflect_path::FromReflect,)*
6162
});
6263

64+
let constructor = if let Some(constructor) = custom_constructor {
65+
quote!(
66+
let mut value: Self = #constructor;
67+
#(
68+
value.#field_idents = {
69+
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)?
70+
};
71+
)*
72+
Some(value)
73+
)
74+
} else {
75+
quote!(
76+
Some(
77+
Self {
78+
#(#field_idents: {
79+
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)?
80+
},)*
81+
#(#ignored_field_idents: Default::default(),)*
82+
}
83+
)
84+
)
85+
};
86+
6387
TokenStream::from(quote! {
6488
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
6589
{
6690
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
67-
use #bevy_reflect_path::Struct;
6891
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-
)
92+
#constructor
7793
} else {
7894
None
7995
}

crates/bevy_reflect/bevy_reflect_derive/src/lib.rs

+201-4
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,7 @@ fn impl_struct(
287287

288288
#[inline]
289289
fn clone_value(&self) -> Box<dyn #bevy_reflect_path::Reflect> {
290-
use #bevy_reflect_path::Struct;
291-
Box::new(self.clone_dynamic())
290+
Box::new(#bevy_reflect_path::Struct::clone_dynamic(self))
292291
}
293292
#[inline]
294293
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
@@ -298,11 +297,10 @@ fn impl_struct(
298297

299298
#[inline]
300299
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
301-
use #bevy_reflect_path::Struct;
302300
if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() {
303301
for (i, value) in struct_value.iter_fields().enumerate() {
304302
let name = struct_value.name_at(i).unwrap();
305-
self.field_mut(name).map(|v| v.apply(value));
303+
#bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value));
306304
}
307305
} else {
308306
panic!("Attempted to apply non-struct type to struct type.");
@@ -607,6 +605,204 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
607605
)
608606
}
609607

608+
/// Represents the information needed to implement a type as a Reflect Struct.
609+
///
610+
/// # Example
611+
/// ```ignore
612+
/// impl_reflect_struct!(
613+
/// // attrs
614+
/// // |----------------------------------------|
615+
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
616+
/// // type_name generics
617+
/// // |-------------------||----------|
618+
/// struct ThingThatImReflecting<T1, T2, T3> {
619+
/// x: T1, // |
620+
/// y: T2, // |- fields
621+
/// z: T3 // |
622+
/// }
623+
/// );
624+
/// ```
625+
struct ReflectStructDef {
626+
type_name: Ident,
627+
generics: Generics,
628+
attrs: ReflectAttrs,
629+
fields: Fields,
630+
}
631+
632+
impl Parse for ReflectStructDef {
633+
fn parse(input: ParseStream) -> syn::Result<Self> {
634+
let ast = input.parse::<DeriveInput>()?;
635+
636+
let type_name = ast.ident;
637+
let generics = ast.generics;
638+
let fields = match ast.data {
639+
Data::Struct(data) => data.fields,
640+
Data::Enum(data) => {
641+
return Err(syn::Error::new_spanned(
642+
data.enum_token,
643+
"Enums are not currently supported for reflection",
644+
))
645+
}
646+
Data::Union(data) => {
647+
return Err(syn::Error::new_spanned(
648+
data.union_token,
649+
"Unions are not supported for reflection",
650+
))
651+
}
652+
};
653+
654+
let mut attrs = ReflectAttrs::default();
655+
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
656+
let meta_list = if let Meta::List(meta_list) = attribute {
657+
meta_list
658+
} else {
659+
continue;
660+
};
661+
662+
if let Some(ident) = meta_list.path.get_ident() {
663+
if ident == REFLECT_ATTRIBUTE_NAME || ident == REFLECT_VALUE_ATTRIBUTE_NAME {
664+
attrs = ReflectAttrs::from_nested_metas(&meta_list.nested);
665+
}
666+
}
667+
}
668+
669+
Ok(Self {
670+
type_name,
671+
generics,
672+
attrs,
673+
fields,
674+
})
675+
}
676+
}
677+
678+
/// A replacement for `#[derive(Reflect)]` to be used with foreign types which
679+
/// the definitions of cannot be altered.
680+
///
681+
/// This macro is an alternative to [`impl_reflect_value!`] and [`impl_from_reflect_value!`]
682+
/// which implement foreign types as Value types. Note that there is no `impl_from_reflect_struct`,
683+
/// as this macro will do the job of both. This macro implements them as `Struct` types,
684+
/// which have greater functionality. The type being reflected must be in scope, as you cannot
685+
/// qualify it in the macro as e.g. `bevy::prelude::Vec3`.
686+
///
687+
/// It may be necessary to add `#[reflect(Default)]` for some types, specifically non-constructible
688+
/// foreign types. Without `Default` reflected for such types, you will usually get an arcane
689+
/// error message and fail to compile. If the type does not implement `Default`, it may not
690+
/// be possible to reflect without extending the macro.
691+
///
692+
/// # Example
693+
/// Implementing `Reflect` for `bevy::prelude::Vec3` as a struct type:
694+
/// ```ignore
695+
/// use bevy::prelude::Vec3;
696+
///
697+
/// impl_reflect_struct!(
698+
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
699+
/// struct Vec3 {
700+
/// x: f32,
701+
/// y: f32,
702+
/// z: f32
703+
/// }
704+
/// );
705+
/// ```
706+
#[proc_macro]
707+
pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
708+
let ReflectStructDef {
709+
type_name,
710+
generics,
711+
attrs,
712+
fields,
713+
} = parse_macro_input!(input as ReflectStructDef);
714+
715+
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
716+
717+
let fields_and_args = fields
718+
.iter()
719+
.enumerate()
720+
.map(|(i, f)| {
721+
(
722+
f,
723+
f.attrs
724+
.iter()
725+
.find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME)
726+
.map(|a| {
727+
syn::custom_keyword!(ignore);
728+
let mut attribute_args = PropAttributeArgs { ignore: None };
729+
a.parse_args_with(|input: ParseStream| {
730+
if input.parse::<Option<ignore>>()?.is_some() {
731+
attribute_args.ignore = Some(true);
732+
return Ok(());
733+
}
734+
Ok(())
735+
})
736+
.expect("Invalid 'property' attribute format.");
737+
738+
attribute_args
739+
}),
740+
i,
741+
)
742+
})
743+
.collect::<Vec<(&Field, Option<PropAttributeArgs>, usize)>>();
744+
let active_fields = fields_and_args
745+
.iter()
746+
.filter(|(_field, attrs, _i)| {
747+
attrs.is_none()
748+
|| match attrs.as_ref().unwrap().ignore {
749+
Some(ignore) => !ignore,
750+
None => true,
751+
}
752+
})
753+
.map(|(f, _attr, i)| (*f, *i))
754+
.collect::<Vec<(&Field, usize)>>();
755+
let ignored_fields = fields_and_args
756+
.iter()
757+
.filter(|(_field, attrs, _i)| {
758+
attrs
759+
.as_ref()
760+
.map(|attrs| attrs.ignore.unwrap_or(false))
761+
.unwrap_or(false)
762+
})
763+
.map(|(f, _attr, i)| (*f, *i))
764+
.collect::<Vec<(&Field, usize)>>();
765+
766+
let constructor = if attrs
767+
.data
768+
.contains(&Ident::new("ReflectDefault", Span::call_site()))
769+
{
770+
Some(quote! { Default::default() })
771+
} else {
772+
None
773+
};
774+
775+
let registration_data = &attrs.data;
776+
let get_type_registration_impl =
777+
impl_get_type_registration(&type_name, &bevy_reflect_path, registration_data, &generics);
778+
779+
let impl_struct: proc_macro2::TokenStream = impl_struct(
780+
&type_name,
781+
&generics,
782+
&get_type_registration_impl,
783+
&bevy_reflect_path,
784+
&attrs,
785+
&active_fields,
786+
)
787+
.into();
788+
789+
let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(
790+
&type_name,
791+
&generics,
792+
&bevy_reflect_path,
793+
&active_fields,
794+
&ignored_fields,
795+
constructor,
796+
)
797+
.into();
798+
799+
TokenStream::from(quote! {
800+
#impl_struct
801+
802+
#impl_from_struct
803+
})
804+
}
805+
610806
#[derive(Default)]
611807
struct ReflectAttrs {
612808
reflect_hash: TraitImpl,
@@ -862,6 +1058,7 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
8621058
&bevy_reflect_path,
8631059
&active_fields,
8641060
&ignored_fields,
1061+
None,
8651062
),
8661063
DeriveType::TupleStruct => from_reflect::impl_tuple_struct(
8671064
type_name,

0 commit comments

Comments
 (0)