Skip to content

Commit 162c3d0

Browse files
committed
Add automatic reflection registration
1 parent 1914a3f commit 162c3d0

File tree

9 files changed

+229
-36
lines changed

9 files changed

+229
-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
@@ -83,7 +83,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
8383
bevy_reflect_path,
8484
);
8585

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

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,
@@ -298,12 +298,16 @@ impl<T: FromReflect> Typed for Vec<T> {
298298
}
299299
}
300300

301-
impl<T: FromReflect> GetTypeRegistration for Vec<T> {
301+
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for Vec<T> {
302302
fn get_type_registration() -> TypeRegistration {
303303
let mut registration = TypeRegistration::of::<Vec<T>>();
304304
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
305305
registration
306306
}
307+
308+
fn register_type_dependencies(registry: &mut TypeRegistry) {
309+
registry.try_register::<T>();
310+
}
307311
}
308312

309313
impl<T: FromReflect> FromReflect for Vec<T> {
@@ -469,14 +473,19 @@ impl<K: FromReflect + Eq + Hash, V: FromReflect> Typed for HashMap<K, V> {
469473

470474
impl<K, V> GetTypeRegistration for HashMap<K, V>
471475
where
472-
K: FromReflect + Eq + Hash,
473-
V: FromReflect,
476+
K: FromReflect + GetTypeRegistration + Eq + Hash,
477+
V: FromReflect + GetTypeRegistration,
474478
{
475479
fn get_type_registration() -> TypeRegistration {
476480
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
477481
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
478482
registration
479483
}
484+
485+
fn register_type_dependencies(registry: &mut TypeRegistry) {
486+
registry.try_register::<K>();
487+
registry.try_register::<V>();
488+
}
480489
}
481490

482491
impl<K: FromReflect + Eq + Hash, V: FromReflect> FromReflect for HashMap<K, V> {
@@ -638,10 +647,14 @@ impl<T: Reflect, const N: usize> Typed for [T; N] {
638647
macro_rules! impl_array_get_type_registration {
639648
($($N:expr)+) => {
640649
$(
641-
impl<T: Reflect > GetTypeRegistration for [T; $N] {
650+
impl<T: Reflect + GetTypeRegistration> GetTypeRegistration for [T; $N] {
642651
fn get_type_registration() -> TypeRegistration {
643652
TypeRegistration::of::<[T; $N]>()
644653
}
654+
655+
fn register_type_dependencies(registry: &mut TypeRegistry) {
656+
registry.try_register::<T>();
657+
}
645658
}
646659
)+
647660
};
@@ -734,10 +747,14 @@ impl Reflect for Cow<'static, str> {
734747
}
735748
}
736749

737-
impl<T: FromReflect> GetTypeRegistration for Option<T> {
750+
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for Option<T> {
738751
fn get_type_registration() -> TypeRegistration {
739752
TypeRegistration::of::<Option<T>>()
740753
}
754+
755+
fn register_type_dependencies(registry: &mut TypeRegistry) {
756+
registry.try_register::<T>();
757+
}
741758
}
742759

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

crates/bevy_reflect/src/lib.rs

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