Skip to content

Commit 3ebcf82

Browse files
committed
Add automatic reflection registration
1 parent ccf7c65 commit 3ebcf82

File tree

8 files changed

+228
-35
lines changed

8 files changed

+228
-35
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use quote::quote;
77
use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
88
use syn::punctuated::Punctuated;
99
use syn::spanned::Spanned;
10-
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant};
10+
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant};
1111

1212
pub(crate) enum ReflectDerive<'a> {
1313
Struct(ReflectStruct<'a>),
@@ -257,6 +257,7 @@ impl<'a> ReflectMeta<'a> {
257257
self.traits.idents(),
258258
self.generics,
259259
None,
260+
None::<std::iter::Empty<&Type>>,
260261
)
261262
}
262263
}
@@ -275,22 +276,7 @@ impl<'a> ReflectStruct<'a> {
275276
&self.serialization_denylist
276277
}
277278

278-
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
279-
///
280-
/// Returns a specific implementation for structs and this method should be preffered over the generic [`get_type_registration`](crate::ReflectMeta) method
281-
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
282-
let reflect_path = self.meta.bevy_reflect_path();
283-
284-
crate::registration::impl_get_type_registration(
285-
self.meta.type_name(),
286-
reflect_path,
287-
self.meta.traits().idents(),
288-
self.meta.generics(),
289-
Some(&self.serialization_denylist),
290-
)
291-
}
292-
293-
/// Get a collection of types which are exposed to the reflection API
279+
/// Get a collection of types which are exposed to the reflection API.
294280
pub fn active_types(&self) -> Vec<syn::Type> {
295281
self.fields
296282
.iter()
@@ -299,25 +285,39 @@ impl<'a> ReflectStruct<'a> {
299285
.collect::<Vec<_>>()
300286
}
301287

302-
/// Get an iterator of fields which are exposed to the reflection API
288+
/// Get an iterator of fields which are exposed to the reflection API.
303289
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
304-
self.fields
290+
self.fields()
305291
.iter()
306292
.filter(move |field| field.attrs.ignore.is_active())
307293
}
308294

309-
/// Get an iterator of fields which are ignored by the reflection API
295+
/// Get an iterator of fields which are ignored by the reflection API.
310296
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
311-
self.fields
297+
self.fields()
312298
.iter()
313299
.filter(move |field| field.attrs.ignore.is_ignored())
314300
}
315301

316302
/// The complete set of fields in this struct.
317-
#[allow(dead_code)]
318303
pub fn fields(&self) -> &[StructField<'a>] {
319304
&self.fields
320305
}
306+
307+
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
308+
///
309+
/// Returns a specific implementation for structs.
310+
/// This method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method.
311+
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
312+
crate::registration::impl_get_type_registration(
313+
self.meta.type_name,
314+
&self.meta.bevy_reflect_path,
315+
self.meta.traits.idents(),
316+
self.meta.generics,
317+
Some(&self.serialization_denylist),
318+
Some(self.active_types().iter()),
319+
)
320+
}
321321
}
322322

323323
impl<'a> ReflectEnum<'a> {
@@ -338,4 +338,39 @@ impl<'a> ReflectEnum<'a> {
338338
pub fn variants(&self) -> &[EnumVariant<'a>] {
339339
&self.variants
340340
}
341+
342+
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
343+
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
344+
crate::registration::impl_get_type_registration(
345+
self.meta.type_name,
346+
&self.meta.bevy_reflect_path,
347+
self.meta.traits.idents(),
348+
self.meta.generics,
349+
None,
350+
Some(
351+
self.variants()
352+
.iter()
353+
.flat_map(|variant| variant.active_fields())
354+
.map(|field| &field.data.ty),
355+
),
356+
)
357+
}
358+
}
359+
360+
impl<'a> EnumVariant<'a> {
361+
/// The complete set of fields in this variant.
362+
pub fn fields(&self) -> &[StructField<'a>] {
363+
match &self.fields {
364+
EnumVariantFields::Named(fields) => fields,
365+
EnumVariantFields::Unnamed(fields) => fields,
366+
EnumVariantFields::Unit => &[],
367+
}
368+
}
369+
370+
/// Get an iterator of fields which are exposed to the reflection API
371+
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
372+
self.fields()
373+
.iter()
374+
.filter(|field| !field.attrs.ignore.is_ignored())
375+
}
341376
}

crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
6666
bevy_reflect_path,
6767
);
6868

69-
let get_type_registration_impl = reflect_enum.meta().get_type_registration();
69+
let get_type_registration_impl = reflect_enum.get_type_registration();
7070
let (impl_generics, ty_generics, where_clause) =
7171
reflect_enum.meta().generics().split_for_impl();
7272

crates/bevy_reflect/bevy_reflect_derive/src/registration.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,25 @@
33
use bit_set::BitSet;
44
use proc_macro2::Ident;
55
use quote::quote;
6-
use syn::{Generics, Path};
6+
use syn::{Generics, Path, Type};
77

88
/// Creates the `GetTypeRegistration` impl for the given type data.
9-
pub(crate) fn impl_get_type_registration(
9+
pub(crate) fn impl_get_type_registration<'a>(
1010
type_name: &Ident,
1111
bevy_reflect_path: &Path,
1212
registration_data: &[Ident],
1313
generics: &Generics,
1414
serialization_denylist: Option<&BitSet<u32>>,
15+
type_dependencies: Option<impl Iterator<Item = &'a Type>>,
1516
) -> proc_macro2::TokenStream {
17+
let type_deps_fn = type_dependencies.map(|deps| {
18+
quote! {
19+
fn register_type_dependencies(registry: &mut #bevy_reflect_path::__macro_exports::TypeRegistry) {
20+
#(registry.try_register::<#deps>();)*
21+
}
22+
}
23+
});
24+
1625
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1726
let serialization_data = serialization_denylist.map(|denylist| {
1827
let denylist = denylist.into_iter().map(|v| v as usize);
@@ -32,6 +41,8 @@ pub(crate) fn impl_get_type_registration(
3241
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
3342
registration
3443
}
44+
45+
#type_deps_fn
3546
}
3647
}
3748
}

crates/bevy_reflect/src/enums/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ mod tests {
316316
#[test]
317317
fn enum_should_allow_generics() {
318318
#[derive(Reflect, Debug, PartialEq)]
319-
enum TestEnum<T: FromReflect> {
319+
enum TestEnum<T: FromReflect + GetTypeRegistration> {
320320
A,
321321
B(T),
322322
C { value: T },

crates/bevy_reflect/src/impls/std.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{self as bevy_reflect, ReflectFromPtr};
1+
use crate::{self as bevy_reflect, ReflectFromPtr, TypeRegistry};
22
use crate::{
33
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum,
44
EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter,
@@ -226,12 +226,16 @@ impl<T: FromReflect> Typed for Vec<T> {
226226
}
227227
}
228228

229-
impl<T: FromReflect> GetTypeRegistration for Vec<T> {
229+
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for Vec<T> {
230230
fn get_type_registration() -> TypeRegistration {
231231
let mut registration = TypeRegistration::of::<Vec<T>>();
232232
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
233233
registration
234234
}
235+
236+
fn register_type_dependencies(registry: &mut TypeRegistry) {
237+
registry.try_register::<T>();
238+
}
235239
}
236240

237241
impl<T: FromReflect> FromReflect for Vec<T> {
@@ -388,14 +392,19 @@ impl<K: FromReflect + Eq + Hash, V: FromReflect> Typed for HashMap<K, V> {
388392

389393
impl<K, V> GetTypeRegistration for HashMap<K, V>
390394
where
391-
K: FromReflect + Eq + Hash,
392-
V: FromReflect,
395+
K: FromReflect + GetTypeRegistration + Eq + Hash,
396+
V: FromReflect + GetTypeRegistration,
393397
{
394398
fn get_type_registration() -> TypeRegistration {
395399
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
396400
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
397401
registration
398402
}
403+
404+
fn register_type_dependencies(registry: &mut TypeRegistry) {
405+
registry.try_register::<K>();
406+
registry.try_register::<V>();
407+
}
399408
}
400409

401410
impl<K: FromReflect + Eq + Hash, V: FromReflect> FromReflect for HashMap<K, V> {
@@ -547,10 +556,14 @@ impl<T: Reflect, const N: usize> Typed for [T; N] {
547556
macro_rules! impl_array_get_type_registration {
548557
($($N:expr)+) => {
549558
$(
550-
impl<T: Reflect > GetTypeRegistration for [T; $N] {
559+
impl<T: Reflect + GetTypeRegistration> GetTypeRegistration for [T; $N] {
551560
fn get_type_registration() -> TypeRegistration {
552561
TypeRegistration::of::<[T; $N]>()
553562
}
563+
564+
fn register_type_dependencies(registry: &mut TypeRegistry) {
565+
registry.try_register::<T>();
566+
}
554567
}
555568
)+
556569
};
@@ -635,10 +648,14 @@ impl Reflect for Cow<'static, str> {
635648
}
636649
}
637650

638-
impl<T: FromReflect> GetTypeRegistration for Option<T> {
651+
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for Option<T> {
639652
fn get_type_registration() -> TypeRegistration {
640653
TypeRegistration::of::<Option<T>>()
641654
}
655+
656+
fn register_type_dependencies(registry: &mut TypeRegistry) {
657+
registry.try_register::<T>();
658+
}
642659
}
643660

644661
impl<T: FromReflect> Enum for Option<T> {

crates/bevy_reflect/src/lib.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ pub use erased_serde;
6666
pub mod __macro_exports {
6767
use crate::Uuid;
6868

69+
pub use crate::TypeRegistry;
70+
6971
/// Generates a new UUID from the given UUIDs `a` and `b`,
7072
/// where the bytes are generated by a bitwise `a ^ b.rotate_right(1)`.
7173
/// The generated UUID will be a `UUIDv4` (meaning that the bytes should be random, not e.g. derived from the system time).
@@ -103,6 +105,7 @@ mod tests {
103105
ser::{to_string_pretty, PrettyConfig},
104106
Deserializer,
105107
};
108+
use std::any::TypeId;
106109
use std::fmt::{Debug, Formatter};
107110

108111
use super::prelude::*;
@@ -440,6 +443,99 @@ mod tests {
440443
assert_eq!(new_foo, expected_new_foo);
441444
}
442445

446+
#[test]
447+
fn should_auto_register_fields() {
448+
#[derive(Reflect, FromReflect)]
449+
struct Foo {
450+
bar: Bar,
451+
}
452+
453+
#[derive(Reflect, FromReflect)]
454+
enum Bar {
455+
Variant(Baz),
456+
}
457+
458+
#[derive(Reflect, FromReflect)]
459+
struct Baz(usize);
460+
461+
// === Basic === //
462+
let mut registry = TypeRegistry::empty();
463+
registry.register::<Foo>();
464+
465+
assert!(
466+
registry.contains(TypeId::of::<Bar>()),
467+
"registry should contain auto-registered `Bar` from `Foo`"
468+
);
469+
470+
// === Option === //
471+
let mut registry = TypeRegistry::empty();
472+
registry.register::<Option<Foo>>();
473+
474+
assert!(
475+
registry.contains(TypeId::of::<Bar>()),
476+
"registry should contain auto-registered `Bar` from `Option<Foo>`"
477+
);
478+
479+
// === Tuple === //
480+
let mut registry = TypeRegistry::empty();
481+
registry.register::<(Foo, Foo)>();
482+
483+
assert!(
484+
registry.contains(TypeId::of::<Bar>()),
485+
"registry should contain auto-registered `Bar` from `(Foo, Foo)`"
486+
);
487+
488+
// === Array === //
489+
let mut registry = TypeRegistry::empty();
490+
registry.register::<[Foo; 3]>();
491+
492+
assert!(
493+
registry.contains(TypeId::of::<Bar>()),
494+
"registry should contain auto-registered `Bar` from `[Foo; 3]`"
495+
);
496+
497+
// === Vec === //
498+
let mut registry = TypeRegistry::empty();
499+
registry.register::<Vec<Foo>>();
500+
501+
assert!(
502+
registry.contains(TypeId::of::<Bar>()),
503+
"registry should contain auto-registered `Bar` from `Vec<Foo>`"
504+
);
505+
506+
// === HashMap === //
507+
let mut registry = TypeRegistry::empty();
508+
registry.register::<HashMap<i32, Foo>>();
509+
510+
assert!(
511+
registry.contains(TypeId::of::<Bar>()),
512+
"registry should contain auto-registered `Bar` from `HashMap<i32, Foo>`"
513+
);
514+
}
515+
516+
#[test]
517+
fn should_not_auto_register_existing_types() {
518+
#[derive(Reflect)]
519+
struct Foo {
520+
bar: Bar,
521+
}
522+
523+
#[derive(Reflect, Default)]
524+
struct Bar(usize);
525+
526+
let mut registry = TypeRegistry::empty();
527+
registry.register::<Bar>();
528+
registry.register_type_data::<Bar, ReflectDefault>();
529+
registry.register::<Foo>();
530+
531+
assert!(
532+
registry
533+
.get_type_data::<ReflectDefault>(TypeId::of::<Bar>())
534+
.is_some(),
535+
"registry should contain existing registration for `Bar`"
536+
);
537+
}
538+
443539
#[test]
444540
fn reflect_serialize() {
445541
#[derive(Reflect)]
@@ -661,7 +757,7 @@ mod tests {
661757

662758
// Struct (generic)
663759
#[derive(Reflect)]
664-
struct MyGenericStruct<T: Reflect> {
760+
struct MyGenericStruct<T: Reflect + GetTypeRegistration> {
665761
foo: T,
666762
bar: usize,
667763
}

0 commit comments

Comments
 (0)