Skip to content

Commit

Permalink
Add automatic reflection registration
Browse files Browse the repository at this point in the history
  • Loading branch information
MrGVSV committed Aug 24, 2022
1 parent 886837d commit 304de59
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 18 deletions.
48 changes: 45 additions & 3 deletions crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ impl<'a> ReflectMeta<'a> {
&self.bevy_reflect_path,
self.traits.idents(),
self.generics,
None::<std::iter::Empty<&Type>>,
)
}
}
Expand All @@ -259,12 +260,12 @@ impl<'a> ReflectStruct<'a> {

/// Get an iterator over the active fields.
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields.iter().filter(|field| !field.attrs.ignore)
self.fields().iter().filter(|field| !field.attrs.ignore)
}

/// Get an iterator over the ignored fields.
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields.iter().filter(|field| field.attrs.ignore)
self.fields().iter().filter(|field| field.attrs.ignore)
}

/// Get a collection of all active types.
Expand All @@ -273,10 +274,20 @@ impl<'a> ReflectStruct<'a> {
}

/// The complete set of fields in this struct.
#[allow(dead_code)]
pub fn fields(&self) -> &[StructField<'a>] {
&self.fields
}

/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
crate::registration::impl_get_type_registration(
self.meta.type_name,
&self.meta.bevy_reflect_path,
self.meta.traits.idents(),
self.meta.generics,
Some(self.active_types()),
)
}
}

impl<'a> ReflectEnum<'a> {
Expand Down Expand Up @@ -309,4 +320,35 @@ impl<'a> ReflectEnum<'a> {
pub fn variants(&self) -> &[EnumVariant<'a>] {
&self.variants
}

/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
crate::registration::impl_get_type_registration(
self.meta.type_name,
&self.meta.bevy_reflect_path,
self.meta.traits.idents(),
self.meta.generics,
Some(
self.active_variants()
.flat_map(|variant| variant.active_fields())
.map(|field| &field.data.ty),
),
)
}
}

impl<'a> EnumVariant<'a> {
/// The complete set of fields in this variant.
pub fn fields(&self) -> &[StructField<'a>] {
match &self.fields {
EnumVariantFields::Named(fields) => fields,
EnumVariantFields::Unnamed(fields) => fields,
EnumVariantFields::Unit => &[],
}
}

/// Get an iterator over the active fields in this variant.
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields().iter().filter(|field| !field.attrs.ignore)
}
}
2 changes: 1 addition & 1 deletion crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
bevy_reflect_path,
);

let get_type_registration_impl = reflect_enum.meta().get_type_registration();
let get_type_registration_impl = reflect_enum.get_type_registration();
let (impl_generics, ty_generics, where_clause) =
reflect_enum.meta().generics().split_for_impl();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
bevy_reflect_path,
);

let get_type_registration_impl = reflect_struct.meta().get_type_registration();
let get_type_registration_impl = reflect_struct.get_type_registration();
let (impl_generics, ty_generics, where_clause) =
reflect_struct.meta().generics().split_for_impl();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use syn::{Index, Member};
pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let struct_name = reflect_struct.meta().type_name();
let get_type_registration_impl = reflect_struct.meta().get_type_registration();
let get_type_registration_impl = reflect_struct.get_type_registration();

let field_idents = reflect_struct
.active_fields()
Expand Down
15 changes: 13 additions & 2 deletions crates/bevy_reflect/bevy_reflect_derive/src/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@
use proc_macro2::Ident;
use quote::quote;
use syn::{Generics, Path};
use syn::{Generics, Path, Type};

/// Creates the `GetTypeRegistration` impl for the given type data.
pub(crate) fn impl_get_type_registration(
pub(crate) fn impl_get_type_registration<'a>(
type_name: &Ident,
bevy_reflect_path: &Path,
registration_data: &[Ident],
generics: &Generics,
type_dependencies: Option<impl Iterator<Item = &'a Type>>,
) -> proc_macro2::TokenStream {
let type_deps_fn = type_dependencies.map(|deps| {
quote! {
fn register_type_dependencies(registry: &mut #bevy_reflect_path::__macro_exports::TypeRegistry) {
#(registry.try_register::<#deps>();)*
}
}
});

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
#[allow(unused_mut)]
Expand All @@ -21,6 +30,8 @@ pub(crate) fn impl_get_type_registration(
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
registration
}

#type_deps_fn
}
}
}
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/enums/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ mod tests {
#[test]
fn enum_should_allow_generics() {
#[derive(Reflect, Debug, PartialEq)]
enum TestEnum<T: FromReflect> {
enum TestEnum<T: FromReflect + GetTypeRegistration> {
A,
B(T),
C { value: T },
Expand Down
29 changes: 23 additions & 6 deletions crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{self as bevy_reflect, ReflectFromPtr};
use crate::{self as bevy_reflect, ReflectFromPtr, TypeRegistry};
use crate::{
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum,
EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter,
Expand Down Expand Up @@ -195,12 +195,16 @@ impl<T: FromReflect> Typed for Vec<T> {
}
}

impl<T: FromReflect> GetTypeRegistration for Vec<T> {
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for Vec<T> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Vec<T>>();
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
registration
}

fn register_type_dependencies(registry: &mut TypeRegistry) {
registry.try_register::<T>();
}
}

impl<T: FromReflect> FromReflect for Vec<T> {
Expand Down Expand Up @@ -346,14 +350,19 @@ impl<K: FromReflect + Eq + Hash, V: FromReflect> Typed for HashMap<K, V> {

impl<K, V> GetTypeRegistration for HashMap<K, V>
where
K: FromReflect + Clone + Eq + Hash,
V: FromReflect + Clone,
K: FromReflect + GetTypeRegistration + Eq + Hash,
V: FromReflect + GetTypeRegistration,
{
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
registration
}

fn register_type_dependencies(registry: &mut TypeRegistry) {
registry.try_register::<K>();
registry.try_register::<V>();
}
}

impl<K: FromReflect + Eq + Hash, V: FromReflect> FromReflect for HashMap<K, V> {
Expand Down Expand Up @@ -498,10 +507,14 @@ impl<T: Reflect, const N: usize> Typed for [T; N] {
macro_rules! impl_array_get_type_registration {
($($N:expr)+) => {
$(
impl<T: Reflect > GetTypeRegistration for [T; $N] {
impl<T: Reflect + GetTypeRegistration> GetTypeRegistration for [T; $N] {
fn get_type_registration() -> TypeRegistration {
TypeRegistration::of::<[T; $N]>()
}

fn register_type_dependencies(registry: &mut TypeRegistry) {
registry.try_register::<T>();
}
}
)+
};
Expand Down Expand Up @@ -586,10 +599,14 @@ impl Reflect for Cow<'static, str> {
}
}

impl<T: FromReflect> GetTypeRegistration for Option<T> {
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for Option<T> {
fn get_type_registration() -> TypeRegistration {
TypeRegistration::of::<Option<T>>()
}

fn register_type_dependencies(registry: &mut TypeRegistry) {
registry.try_register::<T>();
}
}

impl<T: FromReflect> Enum for Option<T> {
Expand Down
98 changes: 97 additions & 1 deletion crates/bevy_reflect/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ pub use erased_serde;
pub mod __macro_exports {
use crate::Uuid;

pub use crate::TypeRegistry;

/// Generates a new UUID from the given UUIDs `a` and `b`,
/// where the bytes are generated by a bitwise `a ^ b.rotate_right(1)`.
/// The generated UUID will be a `UUIDv4` (meaning that the bytes should be random, not e.g. derived from the system time).
Expand Down Expand Up @@ -99,6 +101,7 @@ mod tests {
ser::{to_string_pretty, PrettyConfig},
Deserializer,
};
use std::any::TypeId;
use std::fmt::{Debug, Formatter};

use super::prelude::*;
Expand Down Expand Up @@ -436,6 +439,99 @@ mod tests {
assert_eq!(new_foo, expected_new_foo);
}

#[test]
fn should_auto_register_fields() {
#[derive(Reflect, FromReflect)]
struct Foo {
bar: Bar,
}

#[derive(Reflect, FromReflect)]
enum Bar {
Variant(Baz),
}

#[derive(Reflect, FromReflect)]
struct Baz(usize);

// === Basic === //
let mut registry = TypeRegistry::empty();
registry.register::<Foo>();

assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `Foo`"
);

// === Option === //
let mut registry = TypeRegistry::empty();
registry.register::<Option<Foo>>();

assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `Option<Foo>`"
);

// === Tuple === //
let mut registry = TypeRegistry::empty();
registry.register::<(Foo, Foo)>();

assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `(Foo, Foo)`"
);

// === Array === //
let mut registry = TypeRegistry::empty();
registry.register::<[Foo; 3]>();

assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `[Foo; 3]`"
);

// === Vec === //
let mut registry = TypeRegistry::empty();
registry.register::<Vec<Foo>>();

assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `Vec<Foo>`"
);

// === HashMap === //
let mut registry = TypeRegistry::empty();
registry.register::<HashMap<i32, Foo>>();

assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `HashMap<i32, Foo>`"
);
}

#[test]
fn should_not_auto_register_existing_types() {
#[derive(Reflect)]
struct Foo {
bar: Bar,
}

#[derive(Reflect, Default)]
struct Bar(usize);

let mut registry = TypeRegistry::empty();
registry.register::<Bar>();
registry.register_type_data::<Bar, ReflectDefault>();
registry.register::<Foo>();

assert!(
registry
.get_type_data::<ReflectDefault>(TypeId::of::<Bar>())
.is_some(),
"registry should contain existing registration for `Bar`"
);
}

#[test]
fn reflect_serialize() {
#[derive(Reflect)]
Expand Down Expand Up @@ -628,7 +724,7 @@ mod tests {

// Struct (generic)
#[derive(Reflect)]
struct MyGenericStruct<T: Reflect> {
struct MyGenericStruct<T: Reflect + GetTypeRegistration> {
foo: T,
bar: usize,
}
Expand Down
8 changes: 6 additions & 2 deletions crates/bevy_reflect/src/tuple.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::utility::NonGenericTypeInfoCell;
use crate::{
DynamicInfo, FromReflect, GetTypeRegistration, Reflect, ReflectMut, ReflectRef, TypeInfo,
TypeRegistration, Typed, UnnamedField,
TypeRegistration, TypeRegistry, Typed, UnnamedField,
};
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
Expand Down Expand Up @@ -533,10 +533,14 @@ macro_rules! impl_reflect_tuple {
}
}

impl<$($name: Reflect + Typed),*> GetTypeRegistration for ($($name,)*) {
impl<$($name: Reflect + Typed + GetTypeRegistration),*> GetTypeRegistration for ($($name,)*) {
fn get_type_registration() -> TypeRegistration {
TypeRegistration::of::<($($name,)*)>()
}

fn register_type_dependencies(_registry: &mut TypeRegistry) {
$(_registry.try_register::<$name>();)*
}
}

impl<$($name: FromReflect),*> FromReflect for ($($name,)*)
Expand Down
Loading

0 comments on commit 304de59

Please sign in to comment.