Skip to content

Commit b464258

Browse files
committed
Add automatic reflection registration
1 parent f8e4b75 commit b464258

File tree

9 files changed

+228
-36
lines changed

9 files changed

+228
-36
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

+56-22
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>),
@@ -323,6 +323,7 @@ impl<'a> ReflectMeta<'a> {
323323
self.traits.idents(),
324324
self.generics,
325325
None,
326+
None::<std::iter::Empty<&Type>>,
326327
)
327328
}
328329

@@ -347,22 +348,7 @@ impl<'a> ReflectStruct<'a> {
347348
&self.serialization_denylist
348349
}
349350

350-
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
351-
///
352-
/// Returns a specific implementation for structs and this method should be preffered over the generic [`get_type_registration`](crate::ReflectMeta) method
353-
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
354-
let reflect_path = self.meta.bevy_reflect_path();
355-
356-
crate::registration::impl_get_type_registration(
357-
self.meta.type_name(),
358-
reflect_path,
359-
self.meta.traits().idents(),
360-
self.meta.generics(),
361-
Some(&self.serialization_denylist),
362-
)
363-
}
364-
365-
/// Get a collection of types which are exposed to the reflection API
351+
/// Get a collection of types which are exposed to the reflection API.
366352
pub fn active_types(&self) -> Vec<syn::Type> {
367353
self.fields
368354
.iter()
@@ -371,25 +357,39 @@ impl<'a> ReflectStruct<'a> {
371357
.collect::<Vec<_>>()
372358
}
373359

374-
/// Get an iterator of fields which are exposed to the reflection API
360+
/// Get an iterator of fields which are exposed to the reflection API.
375361
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
376-
self.fields
362+
self.fields()
377363
.iter()
378364
.filter(move |field| field.attrs.ignore.is_active())
379365
}
380366

381-
/// Get an iterator of fields which are ignored by the reflection API
367+
/// Get an iterator of fields which are ignored by the reflection API.
382368
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
383-
self.fields
369+
self.fields()
384370
.iter()
385371
.filter(move |field| field.attrs.ignore.is_ignored())
386372
}
387373

388374
/// The complete set of fields in this struct.
389-
#[allow(dead_code)]
390375
pub fn fields(&self) -> &[StructField<'a>] {
391376
&self.fields
392377
}
378+
379+
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
380+
///
381+
/// Returns a specific implementation for structs.
382+
/// This method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method.
383+
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
384+
crate::registration::impl_get_type_registration(
385+
self.meta.type_name,
386+
&self.meta.bevy_reflect_path,
387+
self.meta.traits.idents(),
388+
self.meta.generics,
389+
Some(&self.serialization_denylist),
390+
Some(self.active_types().iter()),
391+
)
392+
}
393393
}
394394

395395
impl<'a> ReflectEnum<'a> {
@@ -410,4 +410,38 @@ impl<'a> ReflectEnum<'a> {
410410
pub fn variants(&self) -> &[EnumVariant<'a>] {
411411
&self.variants
412412
}
413+
414+
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
415+
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
416+
crate::registration::impl_get_type_registration(
417+
self.meta.type_name,
418+
&self.meta.bevy_reflect_path,
419+
self.meta.traits.idents(),
420+
self.meta.generics,
421+
None,
422+
Some(
423+
self.variants()
424+
.iter()
425+
.flat_map(|variant| variant.active_fields())
426+
.map(|field| &field.data.ty),
427+
),
428+
)
429+
}
430+
}
431+
432+
impl<'a> EnumVariant<'a> {
433+
/// The complete set of fields in this variant.
434+
pub fn fields(&self) -> &[StructField<'a>] {
435+
match &self.fields {
436+
EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => fields,
437+
EnumVariantFields::Unit => &[],
438+
}
439+
}
440+
441+
/// Get an iterator of fields which are exposed to the reflection API
442+
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
443+
self.fields()
444+
.iter()
445+
.filter(|field| !field.attrs.ignore.is_ignored())
446+
}
413447
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
8484
bevy_reflect_path,
8585
);
8686

87-
let get_type_registration_impl = reflect_enum.meta().get_type_registration();
87+
let get_type_registration_impl = reflect_enum.get_type_registration();
8888
let (impl_generics, ty_generics, where_clause) =
8989
reflect_enum.meta().generics().split_for_impl();
9090

crates/bevy_reflect/bevy_reflect_derive/src/registration.rs

+13-2
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();
@@ -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

+1-1
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

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::std_traits::ReflectDefault;
2-
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectOwned};
2+
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectOwned, TypeRegistry};
33
use crate::{
44
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum,
55
EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter,
@@ -304,12 +304,16 @@ macro_rules! impl_reflect_for_veclike {
304304
}
305305
}
306306

307-
impl<T: FromReflect> GetTypeRegistration for $ty {
307+
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for $ty {
308308
fn get_type_registration() -> TypeRegistration {
309309
let mut registration = TypeRegistration::of::<Vec<T>>();
310310
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
311311
registration
312312
}
313+
314+
fn register_type_dependencies(registry: &mut TypeRegistry) {
315+
registry.try_register::<T>();
316+
}
313317
}
314318

315319
impl<T: FromReflect> FromReflect for $ty {
@@ -496,14 +500,19 @@ impl<K: FromReflect + Eq + Hash, V: FromReflect> Typed for HashMap<K, V> {
496500

497501
impl<K, V> GetTypeRegistration for HashMap<K, V>
498502
where
499-
K: FromReflect + Eq + Hash,
500-
V: FromReflect,
503+
K: FromReflect + GetTypeRegistration + Eq + Hash,
504+
V: FromReflect + GetTypeRegistration,
501505
{
502506
fn get_type_registration() -> TypeRegistration {
503507
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
504508
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
505509
registration
506510
}
511+
512+
fn register_type_dependencies(registry: &mut TypeRegistry) {
513+
registry.try_register::<K>();
514+
registry.try_register::<V>();
515+
}
507516
}
508517

509518
impl<K: FromReflect + Eq + Hash, V: FromReflect> FromReflect for HashMap<K, V> {
@@ -665,10 +674,14 @@ impl<T: Reflect, const N: usize> Typed for [T; N] {
665674
macro_rules! impl_array_get_type_registration {
666675
($($N:expr)+) => {
667676
$(
668-
impl<T: Reflect > GetTypeRegistration for [T; $N] {
677+
impl<T: Reflect + GetTypeRegistration> GetTypeRegistration for [T; $N] {
669678
fn get_type_registration() -> TypeRegistration {
670679
TypeRegistration::of::<[T; $N]>()
671680
}
681+
682+
fn register_type_dependencies(registry: &mut TypeRegistry) {
683+
registry.try_register::<T>();
684+
}
672685
}
673686
)+
674687
};
@@ -681,10 +694,14 @@ impl_array_get_type_registration! {
681694
30 31 32
682695
}
683696

684-
impl<T: FromReflect> GetTypeRegistration for Option<T> {
697+
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for Option<T> {
685698
fn get_type_registration() -> TypeRegistration {
686699
TypeRegistration::of::<Option<T>>()
687700
}
701+
702+
fn register_type_dependencies(registry: &mut TypeRegistry) {
703+
registry.try_register::<T>();
704+
}
688705
}
689706

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

crates/bevy_reflect/src/lib.rs

+96-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pub use erased_serde;
6868
pub mod __macro_exports {
6969
use crate::Uuid;
7070

71+
pub use crate::TypeRegistry;
72+
7173
/// Generates a new UUID from the given UUIDs `a` and `b`,
7274
/// where the bytes are generated by a bitwise `a ^ b.rotate_right(1)`.
7375
/// The generated UUID will be a `UUIDv4` (meaning that the bytes should be random, not e.g. derived from the system time).
@@ -479,6 +481,99 @@ mod tests {
479481
assert_eq!(new_foo, expected_new_foo);
480482
}
481483

484+
#[test]
485+
fn should_auto_register_fields() {
486+
#[derive(Reflect, FromReflect)]
487+
struct Foo {
488+
bar: Bar,
489+
}
490+
491+
#[derive(Reflect, FromReflect)]
492+
enum Bar {
493+
Variant(Baz),
494+
}
495+
496+
#[derive(Reflect, FromReflect)]
497+
struct Baz(usize);
498+
499+
// === Basic === //
500+
let mut registry = TypeRegistry::empty();
501+
registry.register::<Foo>();
502+
503+
assert!(
504+
registry.contains(TypeId::of::<Bar>()),
505+
"registry should contain auto-registered `Bar` from `Foo`"
506+
);
507+
508+
// === Option === //
509+
let mut registry = TypeRegistry::empty();
510+
registry.register::<Option<Foo>>();
511+
512+
assert!(
513+
registry.contains(TypeId::of::<Bar>()),
514+
"registry should contain auto-registered `Bar` from `Option<Foo>`"
515+
);
516+
517+
// === Tuple === //
518+
let mut registry = TypeRegistry::empty();
519+
registry.register::<(Foo, Foo)>();
520+
521+
assert!(
522+
registry.contains(TypeId::of::<Bar>()),
523+
"registry should contain auto-registered `Bar` from `(Foo, Foo)`"
524+
);
525+
526+
// === Array === //
527+
let mut registry = TypeRegistry::empty();
528+
registry.register::<[Foo; 3]>();
529+
530+
assert!(
531+
registry.contains(TypeId::of::<Bar>()),
532+
"registry should contain auto-registered `Bar` from `[Foo; 3]`"
533+
);
534+
535+
// === Vec === //
536+
let mut registry = TypeRegistry::empty();
537+
registry.register::<Vec<Foo>>();
538+
539+
assert!(
540+
registry.contains(TypeId::of::<Bar>()),
541+
"registry should contain auto-registered `Bar` from `Vec<Foo>`"
542+
);
543+
544+
// === HashMap === //
545+
let mut registry = TypeRegistry::empty();
546+
registry.register::<HashMap<i32, Foo>>();
547+
548+
assert!(
549+
registry.contains(TypeId::of::<Bar>()),
550+
"registry should contain auto-registered `Bar` from `HashMap<i32, Foo>`"
551+
);
552+
}
553+
554+
#[test]
555+
fn should_not_auto_register_existing_types() {
556+
#[derive(Reflect)]
557+
struct Foo {
558+
bar: Bar,
559+
}
560+
561+
#[derive(Reflect, Default)]
562+
struct Bar(usize);
563+
564+
let mut registry = TypeRegistry::empty();
565+
registry.register::<Bar>();
566+
registry.register_type_data::<Bar, ReflectDefault>();
567+
registry.register::<Foo>();
568+
569+
assert!(
570+
registry
571+
.get_type_data::<ReflectDefault>(TypeId::of::<Bar>())
572+
.is_some(),
573+
"registry should contain existing registration for `Bar`"
574+
);
575+
}
576+
482577
#[test]
483578
fn reflect_serialize() {
484579
#[derive(Reflect)]
@@ -700,7 +795,7 @@ mod tests {
700795

701796
// Struct (generic)
702797
#[derive(Reflect)]
703-
struct MyGenericStruct<T: Reflect> {
798+
struct MyGenericStruct<T: Reflect + GetTypeRegistration> {
704799
foo: T,
705800
bar: usize,
706801
}

0 commit comments

Comments
 (0)