From f4292f049435b3b411a8bed9cc704dd188d08e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Bergstr=C3=B6m?= Date: Sun, 17 Apr 2022 19:02:28 +0200 Subject: [PATCH 01/13] try out trait casting in bevy_reflect --- crates/bevy_reflect/src/impls/glam.rs | 38 +++--- crates/bevy_reflect/src/impls/std.rs | 86 ++++++------- crates/bevy_reflect/src/lib.rs | 20 +-- crates/bevy_reflect/src/list.rs | 5 +- crates/bevy_reflect/src/serde/de.rs | 2 +- crates/bevy_reflect/src/serde/ser.rs | 34 +++-- crates/bevy_reflect/src/type_registry.rs | 150 ++++++++++++++++++++++- 7 files changed, 249 insertions(+), 86 deletions(-) diff --git a/crates/bevy_reflect/src/impls/glam.rs b/crates/bevy_reflect/src/impls/glam.rs index 101f5e6597d07..14136d5c4fe66 100644 --- a/crates/bevy_reflect/src/impls/glam.rs +++ b/crates/bevy_reflect/src/impls/glam.rs @@ -6,14 +6,14 @@ use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_struct, impl_ref use glam::*; impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct IVec2 { x: i32, y: i32, } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct IVec3 { x: i32, y: i32, @@ -21,7 +21,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct IVec4 { x: i32, y: i32, @@ -31,14 +31,14 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct UVec2 { x: u32, y: u32, } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct UVec3 { x: u32, y: u32, @@ -46,7 +46,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct UVec4 { x: u32, y: u32, @@ -56,14 +56,14 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct Vec2 { x: f32, y: f32, } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct Vec3 { x: f32, y: f32, @@ -71,7 +71,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct Vec3A { x: f32, y: f32, @@ -79,7 +79,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct Vec4 { x: f32, y: f32, @@ -89,14 +89,14 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct DVec2 { x: f64, y: f64, } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct DVec3 { x: f64, y: f64, @@ -104,7 +104,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct DVec4 { x: f64, y: f64, @@ -114,7 +114,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct Mat3 { x_axis: Vec3, y_axis: Vec3, @@ -122,7 +122,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct Mat4 { x_axis: Vec4, y_axis: Vec4, @@ -132,7 +132,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct DMat3 { x_axis: DVec3, y_axis: DVec3, @@ -140,7 +140,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Deserialize, Default)] struct DMat4 { x_axis: DVec4, y_axis: DVec4, @@ -153,8 +153,8 @@ impl_reflect_struct!( // mechanisms for read-only fields. I doubt those mechanisms would be added, // so for now quaternions will remain as values. They are represented identically // to Vec4 and DVec4, so you may use those instead and convert between. -impl_reflect_value!(Quat(Debug, PartialEq, Serialize, Deserialize, Default)); -impl_reflect_value!(DQuat(Debug, PartialEq, Serialize, Deserialize, Default)); +impl_reflect_value!(Quat(Debug, PartialEq, Deserialize, Default)); +impl_reflect_value!(DQuat(Debug, PartialEq, Deserialize, Default)); impl_from_reflect_value!(Quat); impl_from_reflect_value!(DQuat); diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index c5fed20da26ec..45151a38d515f 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,42 +1,43 @@ -use crate as bevy_reflect; +use crate::{self as bevy_reflect}; use crate::{ - map_partial_eq, serde::Serializable, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, - FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, - ReflectDeserialize, ReflectMut, ReflectRef, TypeInfo, TypeRegistration, Typed, ValueInfo, + map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, FromType, + GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, ReflectDeserialize, + ReflectMut, ReflectRef, TypeInfo, TypeRegistration, Typed, ValueInfo, }; use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value}; use bevy_utils::{Duration, HashMap, HashSet}; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use std::{ any::Any, borrow::Cow, hash::{Hash, Hasher}, ops::Range, }; - -impl_reflect_value!(bool(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(char(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u8(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u16(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u32(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u64(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u128(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(usize(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i8(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i16(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i32(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i64(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i128(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(isize(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(String(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(Option Deserialize<'de> + Reflect + 'static>(Serialize, Deserialize)); -impl_reflect_value!(HashSet Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); -impl_reflect_value!(Range Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); -impl_reflect_value!(Duration(Debug, Hash, PartialEq, Serialize, Deserialize)); +use crate::serde::Serializable; + +impl_reflect_value!(bool(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(char(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(u8(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(u16(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(u32(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(u64(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(u128(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(usize(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(i8(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(i16(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(i32(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(i64(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(i128(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(isize(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(f32(Debug, PartialEq, Deserialize)); +impl_reflect_value!(f64(Debug, PartialEq, Deserialize)); +impl_reflect_value!(String(Debug, Hash, PartialEq, Deserialize)); +impl_reflect_value!(Option Deserialize<'de> + Reflect + 'static>(Deserialize)); +impl_reflect_value!(HashSet Deserialize<'de> + Send + Sync + 'static>(Deserialize)); +impl_reflect_value!(Range Deserialize<'de> + Send + Sync + 'static>(Deserialize)); +impl_reflect_value!(Duration(Debug, Hash, PartialEq, Deserialize)); impl_from_reflect_value!(bool); impl_from_reflect_value!(char); @@ -55,15 +56,11 @@ impl_from_reflect_value!(isize); impl_from_reflect_value!(f32); impl_from_reflect_value!(f64); impl_from_reflect_value!(String); +impl_from_reflect_value!(Option Deserialize<'de> + Clone + Reflect + 'static>); impl_from_reflect_value!( - Option Deserialize<'de> + Reflect + 'static> -); -impl_from_reflect_value!( - HashSet Deserialize<'de> + Send + Sync + 'static> -); -impl_from_reflect_value!( - Range Deserialize<'de> + Send + Sync + 'static> + HashSet Deserialize<'de> + Hash + Eq + Clone + Send + Sync + 'static> ); +impl_from_reflect_value!(Range Deserialize<'de> + Clone + Send + Sync + 'static>); impl_from_reflect_value!(Duration); impl Array for Vec { @@ -302,9 +299,9 @@ impl Typed for HashMap { } impl GetTypeRegistration for HashMap -where - K: Reflect + Clone + Eq + Hash + for<'de> Deserialize<'de>, - V: Reflect + Clone + for<'de> Deserialize<'de>, + where + K: Reflect + Clone + Eq + Hash + for<'de> Deserialize<'de>, + V: Reflect + Clone + for<'de> Deserialize<'de>, { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); @@ -541,10 +538,6 @@ unsafe impl Reflect for Cow<'static, str> { Some(false) } } - - fn serializable(&self) -> Option { - Some(Serializable::Borrowed(self)) - } } impl Typed for Cow<'static, str> { @@ -570,13 +563,20 @@ impl FromReflect for Cow<'static, str> { #[cfg(test)] mod tests { - use crate::Reflect; + use crate::{serde::is_serializable, Reflect, TypeRegistry}; use bevy_utils::HashMap; use std::f32::consts::{PI, TAU}; #[test] fn can_serialize_duration() { - assert!(std::time::Duration::ZERO.serializable().is_some()); + let mut registry = TypeRegistry::default(); + macro_rules! register { + ($type_registry:ident, $this_type:ty) => { + crate::register_type!($type_registry, $this_type, erased_serde::Serialize) + }; + } + register!(registry, std::time::Duration); + assert!(is_serializable(®istry, &std::time::Duration::ZERO)); } #[test] diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 5519b8031841c..84180d93c8b06 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -465,14 +465,19 @@ mod tests { h: [2; 2], }; + macro_rules! register { + ($type_registry:ident, $this_type:ty) => { + register_type!($type_registry, $this_type, erased_serde::Serialize) + }; + } let mut registry = TypeRegistry::default(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); + register!(registry, u32); + register!(registry, isize); + register!(registry, usize); + register!(registry, Bar); + register!(registry, String); + register!(registry, i8); + register!(registry, i32); let serializer = ReflectSerializer::new(&foo, ®istry); let serialized = to_string_pretty(&serializer, PrettyConfig::default()).unwrap(); @@ -879,6 +884,7 @@ bevy_reflect::tests::should_reflect_debug::Test { let mut registry = TypeRegistry::default(); registry.add_registration(Vec3::get_type_registration()); + registry.add_registration(f32::get_type_registration()); let ser = ReflectSerializer::new(&v, ®istry); diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 66d3816707adb..6eb56f72a25f5 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -3,9 +3,10 @@ use std::fmt::{Debug, Formatter}; use crate::utility::NonGenericTypeInfoCell; use crate::{ - serde::Serializable, Array, ArrayIter, DynamicArray, DynamicInfo, FromReflect, Reflect, - ReflectMut, ReflectRef, TypeInfo, Typed, + Array, ArrayIter, DynamicArray, DynamicInfo, FromReflect, Reflect, ReflectMut, ReflectRef, + TypeInfo, Typed, }; +use crate::serde::Serializable; /// An ordered, mutable list of [Reflect] items. This corresponds to types like [`std::vec::Vec`]. /// diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index 5ffc141c6a178..66ab4f696a396 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -217,7 +217,7 @@ impl<'a, 'de> Visitor<'de> for ReflectVisitor<'a> { let deserialize_reflect = registration.data::().ok_or_else(|| { de::Error::custom(format_args!( - "The TypeRegistration for {} doesn't have DeserializeReflect", + "The TypeRegistration for {} doesn't have ReflectDeserialize", type_name )) })?; diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index 180bac1c81069..5d2f7db28cea9 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -22,13 +22,13 @@ impl<'a> Serializable<'a> { } } -fn get_serializable(reflect_value: &dyn Reflect) -> Result { - reflect_value.serializable().ok_or_else(|| { - serde::ser::Error::custom(format_args!( - "Type '{}' does not support ReflectValue serialization", - reflect_value.type_name() - )) - }) +pub fn is_serializable(type_registry: &TypeRegistry, reflect_value: &dyn Reflect) -> bool { + let registration = type_registry.get(reflect_value.type_id()); + if let Some(registration) = registration { + registration.has_trait_cast::() + } else { + false + } } pub struct ReflectSerializer<'a> { @@ -97,12 +97,24 @@ impl<'a> Serialize for ReflectValueSerializer<'a> { where S: serde::Serializer, { + let registration = self.registry.get(self.value.type_id()).ok_or_else(|| { + ::custom(format_args!( + "No registration found for {}", + self.value.type_name() + )) + })?; + let serializable = registration + .trait_cast::(self.value) + .ok_or_else(|| { + ::custom(format_args!( + "The TypeRegistration for {} doesn't have Serialize trait cast", + self.value.type_name() + )) + })?; + let mut state = serializer.serialize_map(Some(2))?; state.serialize_entry(type_fields::TYPE, self.value.type_name())?; - state.serialize_entry( - type_fields::VALUE, - get_serializable::(self.value)?.borrow(), - )?; + state.serialize_entry(type_fields::VALUE, serializable)?; state.end() } } diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 60c29a8855626..d99c015921338 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -3,7 +3,7 @@ use bevy_utils::{HashMap, HashSet}; use downcast_rs::{impl_downcast, Downcast}; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use serde::Deserialize; -use std::{any::TypeId, fmt::Debug, sync::Arc}; +use std::{any::TypeId, fmt::Debug, mem::MaybeUninit, ptr::NonNull, sync::Arc}; /// A registry of reflected types. pub struct TypeRegistry { @@ -221,6 +221,91 @@ impl TypeRegistryArc { } } +#[doc(hidden)] +pub struct ErasedNonNull { + storage: MaybeUninit<[NonNull<()>; 2]>, + ty: TypeId, +} + +impl ErasedNonNull { + pub fn new(val: &T, type_id: TypeId) -> Self { + let size = core::mem::size_of::<*const T>(); + assert!(size <= core::mem::size_of::<[NonNull<()>; 2]>()); + let val = val as *const T; + let mut storage = MaybeUninit::uninit(); + unsafe { + core::ptr::copy( + &val as *const *const T as *const u8, + storage.as_mut_ptr() as *mut u8, + size, + ) + }; + Self { + storage, + ty: type_id, + } + } + pub unsafe fn as_ref<'a, T: ?Sized + 'static>(self) -> &'a T { + assert!(self.ty == TypeId::of::()); + let size = core::mem::size_of::<*const T>(); + let mut r: MaybeUninit<*const T> = MaybeUninit::uninit(); + core::ptr::copy( + self.storage.as_ptr() as *mut u8, + r.as_mut_ptr() as *mut u8, + size, + ); + &*r.assume_init() + } +} + +#[macro_export] +macro_rules! maybe_trait_cast { + ($this_type:ty, $trait_type:path) => {{ + { + trait NotTrait { + const CAST_FN: Option fn(&'a $this_type) -> &'a dyn $trait_type> = None; + } + impl NotTrait for T {} + struct IsImplemented(core::marker::PhantomData); + + impl IsImplemented { + #[allow(dead_code)] + const CAST_FN: Option fn(&'a T) -> &'a dyn $trait_type> = Some(|a| a); + } + if IsImplemented::<$this_type>::CAST_FN.is_some() { + let f: fn(&dyn $crate::Reflect) -> $crate::ErasedNonNull = + |val: &dyn $crate::Reflect| { + let cast_fn = IsImplemented::<$this_type>::CAST_FN.unwrap(); + let static_val: &$this_type = val.downcast_ref::<$this_type>().unwrap(); + let trait_val: &dyn $trait_type = (cast_fn)(static_val); + $crate::ErasedNonNull::new( + trait_val, + core::any::TypeId::of::(), + ) + }; + Some(f) + } else { + None + } + } + }}; +} + +#[macro_export] +macro_rules! register_type{ + ($type_registry:ident, $this_type:ty, $($trait_type:path),* $(,)?) => { + {{ + let mut type_registration = <$this_type as $crate::GetTypeRegistration>::get_type_registration(); + $( + if let Some(cast_fn) = $crate::maybe_trait_cast!($this_type, $trait_type) {{ + type_registration.register_trait_cast::(cast_fn); + }} + )* + $type_registry.add_registration(type_registration); + }} + }; +} + /// A record of data about a type. /// /// This contains the [`TypeInfo`] of the type, as well as its [short name]. @@ -238,6 +323,7 @@ pub struct TypeRegistration { short_name: String, data: HashMap>, type_info: &'static TypeInfo, + trait_casts: HashMap ErasedNonNull>, } impl TypeRegistration { @@ -249,6 +335,27 @@ impl TypeRegistration { self.type_info.type_id() } + #[doc(hidden)] + pub fn register_trait_cast( + &mut self, + f: fn(&dyn Reflect) -> ErasedNonNull, + ) { + self.trait_casts.insert(TypeId::of::(), f); + } + + pub fn has_trait_cast(&self) -> bool { + self.trait_casts.contains_key(&TypeId::of::()) + } + + pub fn trait_cast<'a, T: ?Sized + 'static>(&self, val: &'a dyn Reflect) -> Option<&'a T> { + if let Some(cast) = self.trait_casts.get(&TypeId::of::()) { + let raw = cast(val); + Some(unsafe { raw.as_ref() }) + } else { + None + } + } + /// Returns a reference to the value of type `T` in this registration's type /// data. /// @@ -286,6 +393,7 @@ impl TypeRegistration { let type_name = std::any::type_name::(); Self { data: HashMap::default(), + trait_casts: HashMap::default(), short_name: Self::get_short_name(type_name), type_info: T::type_info(), } @@ -358,6 +466,7 @@ impl Clone for TypeRegistration { TypeRegistration { data, + trait_casts: self.trait_casts.clone(), short_name: self.short_name.clone(), type_info: self.type_info, } @@ -426,8 +535,7 @@ impl Deserialize<'a> + Reflect> FromType for ReflectDeserialize { #[cfg(test)] mod test { - use crate::TypeRegistration; - use bevy_utils::HashMap; + use super::*; #[test] fn test_get_short_name() { @@ -497,4 +605,40 @@ mod test { "Option, (String, Option)>>" ); } + + trait Test {} + impl Test for HashMap {} + trait TestNot {} + + // the user should specify all traits in a top-level crate. + // all registration should be done through macros in a top-level crate. + macro_rules! register_type_custom { + ($type_registry:ident, $this_type:ty,$($trait_type:path),*) => { + register_type!( + $type_registry, + $this_type, + erased_serde::Serialize, + $($trait_type,)* + ) + }; + } + #[test] + fn test_trait_cast() { + let mut type_registry = TypeRegistry::default(); + register_type_custom!(type_registry, HashMap, Test, TestNot); + register_type_custom!(type_registry, u32,); + let val = HashMap::::default(); + let ty = type_registry + .get(TypeId::of::>()) + .unwrap(); + assert!(ty.trait_cast::(&val).is_some()); + assert!(ty.trait_cast::(&val).is_some()); + assert!(ty.trait_cast::(&val).is_none()); + + let ty = type_registry.get(TypeId::of::()).unwrap(); + assert!(ty + .trait_cast::(&3u32) + .is_some()); + assert!(ty.trait_cast::(&3u32).is_none()); + } } From e21a6fe8beba81f10538a74ee26bd5bdc1e59c76 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 18 Apr 2022 16:20:06 -0700 Subject: [PATCH 02/13] Added register_all macro --- .../bevy_reflect_derive/src/lib.rs | 28 +++++ .../bevy_reflect_derive/src/registration.rs | 112 ++++++++++++++++++ crates/bevy_reflect/src/lib.rs | 34 +++++- 3 files changed, 173 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 7cf9fe560b004..6771fffc7623e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -172,3 +172,31 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { let ty = &reflect_value_def.type_name; from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path) } + +/// A macro that allows for mass type and trait registration. +/// +/// This will generate a public function with the signature: `fn register_types(&mut TypeRegistry)`. +/// You can then use this generated function to register the given types and traits. +/// +/// # Example +/// +/// ``` +/// use bevy_reflect_derive::register_all; +/// +/// trait MyTrait {} +/// struct MyType; +/// struct MyOtherType; +/// +/// impl MyTrait for MyType {} +/// +/// // Not all types need to implement all traits +/// register_all! { +/// traits: [MyTrait], +/// types: [MyType, MyOtherType], +/// } +/// +/// ``` +#[proc_macro] +pub fn register_all(item: TokenStream) -> TokenStream { + registration::register_all_internal(item) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index ea3157242211e..3b825a942fdfe 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -1,7 +1,12 @@ //! Contains code related specifically to Bevy's type registration. +use bevy_macro_utils::BevyManifest; +use proc_macro::TokenStream; use proc_macro2::Ident; use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::{Iter, Punctuated}; +use syn::{bracketed, parse_macro_input, Token, Type}; use syn::{Generics, Path}; /// Creates the `GetTypeRegistration` impl for the given type data. @@ -23,3 +28,110 @@ pub(crate) fn impl_get_type_registration( } } } + +pub fn register_all_internal(item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as RegisterAllData); + + let registration_params = input.types().map(|ty| { + let trait_type = input.traits(); + quote! { + #ty, #(#trait_type),* + } + }); + + let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + + TokenStream::from(quote! { + pub fn register_types(registry: &mut #bevy_reflect_path::TypeRegistry) { + #(#bevy_reflect_path::register_type!(registry, #registration_params));* + } + }) +} + +/// Maps to the following invocation: +/// +/// ``` +/// use bevy_reflect_derive::register_all; +/// +/// trait MyTrait {} +/// struct MyType {} +/// +/// register_all! { +/// traits: [MyTrait], +/// types: [MyType], +/// } +/// ``` +/// +/// > Note: The order of the `traits` and `types` fields does not matter. Additionally, +/// > the commas (separating and trailing) may be omitted entirely. +struct RegisterAllData { + trait_list: Punctuated, + type_list: Punctuated, +} + +impl RegisterAllData { + /// Returns an iterator over the types to register. + fn types(&self) -> Iter { + self.type_list.iter() + } + + /// Returns an iterator over the traits to register. + fn traits(&self) -> Iter { + self.trait_list.iter() + } + + /// Parse a list of types. + /// + /// This is the portion _after_ the the respective keyword and consumes: `: [ Foo, Bar, Baz ]` + fn parse_list(input: &mut ParseStream) -> syn::Result> { + input.parse::()?; + let list; + bracketed!(list in input); + list.parse_terminated(Type::parse) + } +} + +impl Parse for RegisterAllData { + fn parse(mut input: ParseStream) -> syn::Result { + let trait_list; + let type_list; + + let lookahead = input.lookahead1(); + if lookahead.peek(kw::traits) { + // Parse `traits` then `types` + input.parse::()?; + trait_list = Self::parse_list(&mut input)?; + // Optional separating comma + input.parse::>()?; + input.parse::()?; + type_list = Self::parse_list(&mut input)?; + // Optional trailing comma + input.parse::>()?; + } else if lookahead.peek(kw::types) { + // Parse `types` then `traits` + input.parse::()?; + type_list = Self::parse_list(&mut input)?; + // Optional separating comma + input.parse::>()?; + input.parse::()?; + trait_list = Self::parse_list(&mut input)?; + // Optional trailing comma + input.parse::>()?; + } else { + return Err(syn::Error::new( + input.span(), + "expected either 'traits' or 'types' field", + )); + } + + Ok(Self { + trait_list, + type_list, + }) + } +} + +mod kw { + syn::custom_keyword!(traits); + syn::custom_keyword!(types); +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 84180d93c8b06..be50bc75f575c 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -1,4 +1,4 @@ -#![doc = include_str!("../README.md")] +#![doc = include_str ! ("../README.md")] mod array; mod fields; @@ -12,6 +12,7 @@ mod tuple_struct; mod type_info; mod type_registry; mod type_uuid; + mod impls { #[cfg(feature = "glam")] mod glam; @@ -961,5 +962,36 @@ bevy_reflect::tests::should_reflect_debug::Test { assert_eq!(v, vec3(4.0, 2.0, 1.0)); } + #[test] + fn register_all_types() { + #[derive(Reflect)] + struct Foo; + #[derive(Reflect)] + struct Bar; + #[derive(Reflect)] + struct Baz; + + trait SomeTrait {} + trait NoneTrait {} + + impl SomeTrait for Foo {} + impl SomeTrait for Bar {} + + register_all! { + traits: [SomeTrait], + types: [Foo, Bar, Baz] + } + + let mut registry = TypeRegistry::default(); + register_types(&mut registry); + + let ty = registry.get(TypeId::of::()).unwrap(); + assert!(ty.trait_cast::(&Foo).is_some()); + assert!(ty.trait_cast::(&Foo).is_none()); + + let ty = registry.get(TypeId::of::()).unwrap(); + assert!(ty.trait_cast::(&Baz).is_none()); + assert!(ty.trait_cast::(&Baz).is_none()); + } } } From c582e6e47f178eb57507b009cace6b2b22c7408c Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 26 Apr 2022 20:13:07 -0700 Subject: [PATCH 03/13] Prevent overwriting registration --- crates/bevy_reflect/src/lib.rs | 23 +++++++++++++++++++++++ crates/bevy_reflect/src/type_registry.rs | 20 +++++++++++++------- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index be50bc75f575c..005bd5d14cb74 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -977,18 +977,41 @@ bevy_reflect::tests::should_reflect_debug::Test { impl SomeTrait for Foo {} impl SomeTrait for Bar {} + struct SomeTypeData; + impl TypeData for SomeTypeData { + fn clone_type_data(&self) -> Box { + Box::new(SomeTypeData) + } + } + + // Register a set of types and traits register_all! { traits: [SomeTrait], types: [Foo, Bar, Baz] } let mut registry = TypeRegistry::default(); + + // Pre-register `Foo` + registry.register::(); + let foo = registry.get_mut(TypeId::of::()).unwrap(); + foo.insert(SomeTypeData); + + // Register all and make sure it doesn't overwrite any existing data + assert!(registry + .get_type_data::(TypeId::of::()) + .is_some()); register_types(&mut registry); + assert!(registry + .get_type_data::(TypeId::of::()) + .is_some()); + // Test that Foo has the proper trait casts let ty = registry.get(TypeId::of::()).unwrap(); assert!(ty.trait_cast::(&Foo).is_some()); assert!(ty.trait_cast::(&Foo).is_none()); + // Test that Baz has no trait casts let ty = registry.get(TypeId::of::()).unwrap(); assert!(ty.trait_cast::(&Baz).is_none()); assert!(ty.trait_cast::(&Baz).is_none()); diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index d99c015921338..cff0c2f29e928 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -292,18 +292,22 @@ macro_rules! maybe_trait_cast { } #[macro_export] -macro_rules! register_type{ - ($type_registry:ident, $this_type:ty, $($trait_type:path),* $(,)?) => { - {{ - let mut type_registration = <$this_type as $crate::GetTypeRegistration>::get_type_registration(); +macro_rules! register_type { + ($type_registry:ident, $this_type:ty, $($trait_type:path),* $(,)?) => {{ + let type_registration = match $type_registry.get_mut(::std::any::TypeId::of::<$this_type>()) { + Some(registration) => registration, + None => { + $type_registry.register::<$this_type>(); + $type_registry.get_mut(::std::any::TypeId::of::<$this_type>()).unwrap() + } + }; + $( if let Some(cast_fn) = $crate::maybe_trait_cast!($this_type, $trait_type) {{ type_registration.register_trait_cast::(cast_fn); }} )* - $type_registry.add_registration(type_registration); - }} - }; + }}; } /// A record of data about a type. @@ -607,7 +611,9 @@ mod test { } trait Test {} + impl Test for HashMap {} + trait TestNot {} // the user should specify all traits in a top-level crate. From 51265eda437a4d7937a10bf18d1748b327c00661 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 30 Apr 2022 12:07:49 -0700 Subject: [PATCH 04/13] Improved safety Improved safety removing ability to specify arbitrary types in ErasedNonNull constructor. Added docs and tests. --- crates/bevy_reflect/src/type_registry.rs | 69 +++++++++++++++++++----- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index cff0c2f29e928..8c97c6c6e0942 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -228,11 +228,13 @@ pub struct ErasedNonNull { } impl ErasedNonNull { - pub fn new(val: &T, type_id: TypeId) -> Self { + /// Creates a new type-erased [`ErasedNonNull`] instance for the given value. + pub fn new(val: &T) -> Self { let size = core::mem::size_of::<*const T>(); assert!(size <= core::mem::size_of::<[NonNull<()>; 2]>()); let val = val as *const T; let mut storage = MaybeUninit::uninit(); + // SAFE: Size of reference pointer guaranteed to fit within storage unsafe { core::ptr::copy( &val as *const *const T as *const u8, @@ -242,11 +244,22 @@ impl ErasedNonNull { }; Self { storage, - ty: type_id, + ty: TypeId::of::(), } } - pub unsafe fn as_ref<'a, T: ?Sized + 'static>(self) -> &'a T { - assert!(self.ty == TypeId::of::()); + + /// Converts this type-erased value into a typed one. + /// + /// # Panics + /// + /// Panics if the type `T` does not match the underlying type. + /// + /// # Safety + /// + /// Since the type `T` _must_ match the underlying type (or else panics), this method is + /// guaranteed to be safe. + pub unsafe fn into_ref<'a, T: ?Sized + 'static>(self) -> &'a T { + assert_eq!(self.ty, TypeId::of::()); let size = core::mem::size_of::<*const T>(); let mut r: MaybeUninit<*const T> = MaybeUninit::uninit(); core::ptr::copy( @@ -263,14 +276,17 @@ macro_rules! maybe_trait_cast { ($this_type:ty, $trait_type:path) => {{ { trait NotTrait { - const CAST_FN: Option fn(&'a $this_type) -> &'a dyn $trait_type> = None; + const CAST_FN: Option< + for<'a> fn(&'a $this_type) -> &'a (dyn $trait_type + 'static), + > = None; } impl NotTrait for T {} struct IsImplemented(core::marker::PhantomData); impl IsImplemented { #[allow(dead_code)] - const CAST_FN: Option fn(&'a T) -> &'a dyn $trait_type> = Some(|a| a); + const CAST_FN: Option fn(&'a T) -> &'a (dyn $trait_type + 'static)> = + Some(|a| a); } if IsImplemented::<$this_type>::CAST_FN.is_some() { let f: fn(&dyn $crate::Reflect) -> $crate::ErasedNonNull = @@ -278,10 +294,7 @@ macro_rules! maybe_trait_cast { let cast_fn = IsImplemented::<$this_type>::CAST_FN.unwrap(); let static_val: &$this_type = val.downcast_ref::<$this_type>().unwrap(); let trait_val: &dyn $trait_type = (cast_fn)(static_val); - $crate::ErasedNonNull::new( - trait_val, - core::any::TypeId::of::(), - ) + $crate::ErasedNonNull::new(trait_val) }; Some(f) } else { @@ -354,7 +367,8 @@ impl TypeRegistration { pub fn trait_cast<'a, T: ?Sized + 'static>(&self, val: &'a dyn Reflect) -> Option<&'a T> { if let Some(cast) = self.trait_casts.get(&TypeId::of::()) { let raw = cast(val); - Some(unsafe { raw.as_ref() }) + // SAFE: Registered trait and type matches the call site + Some(unsafe { raw.into_ref() }) } else { None } @@ -538,8 +552,9 @@ impl Deserialize<'a> + Reflect> FromType for ReflectDeserialize { } #[cfg(test)] -mod test { +mod tests { use super::*; + use std::any::Any; #[test] fn test_get_short_name() { @@ -647,4 +662,34 @@ mod test { .is_some()); assert!(ty.trait_cast::(&3u32).is_none()); } + + #[test] + fn erased_non_null_should_work() { + // &str -> &str + let input = "Hello, World!"; + let erased = ErasedNonNull::new(input); + let output = unsafe { erased.into_ref::() }; + assert_eq!(input, output); + assert_eq!(input.type_id(), output.type_id()); + + // &dyn Test -> &dyn Test + let input: &dyn Test = &HashMap::::default(); + let erased = ErasedNonNull::new(input); + let output = unsafe { erased.into_ref::() }; + assert_eq!(input.type_id(), output.type_id()); + + // &() -> &() + let input: () = (); + let erased = ErasedNonNull::new(&input); + let output = unsafe { erased.into_ref::<()>() }; + assert_eq!(input.type_id(), output.type_id()); + } + + #[test] + #[should_panic] + fn erased_non_null_should_panic_for_wrong_type() { + let input = "Hello, World!"; + let erased = ErasedNonNull::new(input); + let output = unsafe { erased.into_ref::() }; + } } From da6db8716ec51deb88bf2ee2cbddae61b2e547eb Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 30 Apr 2022 12:25:35 -0700 Subject: [PATCH 05/13] Removed ReflectSerialize usages --- crates/bevy_asset/src/handle.rs | 2 +- crates/bevy_asset/src/path.rs | 6 +++--- crates/bevy_ecs/src/reflect.rs | 2 +- crates/bevy_reflect/src/type_registry.rs | 2 +- crates/bevy_render/src/camera/camera.rs | 2 +- crates/bevy_render/src/camera/projection.rs | 4 ++-- crates/bevy_render/src/color/mod.rs | 2 +- crates/bevy_text/src/text.rs | 4 ++-- crates/bevy_ui/src/focus.rs | 4 ++-- crates/bevy_ui/src/ui_node.rs | 22 ++++++++++----------- crates/bevy_ui/src/widget/image.rs | 2 +- examples/reflection/reflection_types.rs | 4 ++-- 12 files changed, 28 insertions(+), 28 deletions(-) diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index 3cc5df54b069e..060083809a0ff 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -30,7 +30,7 @@ use serde::{Deserialize, Serialize}; Reflect, FromReflect, )] -#[reflect_value(Serialize, Deserialize, PartialEq, Hash)] +#[reflect_value(Deserialize, PartialEq, Hash)] pub enum HandleId { Id(Uuid, u64), AssetPathId(AssetPathId), diff --git a/crates/bevy_asset/src/path.rs b/crates/bevy_asset/src/path.rs index df99eaee19da1..36d45ddbcdbbc 100644 --- a/crates/bevy_asset/src/path.rs +++ b/crates/bevy_asset/src/path.rs @@ -60,19 +60,19 @@ impl<'a> AssetPath<'a> { #[derive( Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect, )] -#[reflect_value(PartialEq, Hash, Serialize, Deserialize)] +#[reflect_value(PartialEq, Hash, Deserialize)] pub struct AssetPathId(SourcePathId, LabelId); #[derive( Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect, )] -#[reflect_value(PartialEq, Hash, Serialize, Deserialize)] +#[reflect_value(PartialEq, Hash, Deserialize)] pub struct SourcePathId(u64); #[derive( Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect, )] -#[reflect_value(PartialEq, Hash, Serialize, Deserialize)] +#[reflect_value(PartialEq, Hash, Deserialize)] pub struct LabelId(u64); impl<'a> From<&'a Path> for SourcePathId { diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index 4881dd83f0edd..ae023764ac086 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -122,7 +122,7 @@ impl FromType for ReflectComponent { } } -impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(Entity(Hash, PartialEq, Deserialize)); impl_from_reflect_value!(Entity); #[derive(Clone)] diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 8c97c6c6e0942..e5899592e605d 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -690,6 +690,6 @@ mod tests { fn erased_non_null_should_panic_for_wrong_type() { let input = "Hello, World!"; let erased = ErasedNonNull::new(input); - let output = unsafe { erased.into_ref::() }; + let _ = unsafe { erased.into_ref::() }; } } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 00db5f8401f73..847d1b46d5a0d 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -304,7 +304,7 @@ impl RenderTarget { } #[derive(Debug, Clone, Copy, Reflect, Serialize, Deserialize)] -#[reflect_value(Serialize, Deserialize)] +#[reflect_value(Deserialize)] pub enum DepthCalculation { /// Pythagorean distance; works everywhere, more expensive to compute. Distance, diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index bd40d52a620a5..986b0522febf6 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -139,14 +139,14 @@ impl Default for PerspectiveProjection { // TODO: make this a component instead of a property #[derive(Debug, Clone, Reflect, Serialize, Deserialize)] -#[reflect_value(Serialize, Deserialize)] +#[reflect_value(Deserialize)] pub enum WindowOrigin { Center, BottomLeft, } #[derive(Debug, Clone, Reflect, Serialize, Deserialize)] -#[reflect_value(Serialize, Deserialize)] +#[reflect_value(Deserialize)] pub enum ScalingMode { /// Manually specify left/right/top/bottom values. /// Ignore window resizing; the image will stretch. diff --git a/crates/bevy_render/src/color/mod.rs b/crates/bevy_render/src/color/mod.rs index 20bedac8ad8fa..0231be833fecf 100644 --- a/crates/bevy_render/src/color/mod.rs +++ b/crates/bevy_render/src/color/mod.rs @@ -10,7 +10,7 @@ use std::ops::{Add, AddAssign, Mul, MulAssign}; use thiserror::Error; #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect, FromReflect)] -#[reflect(PartialEq, Serialize, Deserialize)] +#[reflect(PartialEq, Deserialize)] pub enum Color { /// sRGBA color Rgba { diff --git a/crates/bevy_text/src/text.rs b/crates/bevy_text/src/text.rs index 02f41ca0e992a..2e7908cec4650 100644 --- a/crates/bevy_text/src/text.rs +++ b/crates/bevy_text/src/text.rs @@ -87,7 +87,7 @@ impl Default for TextAlignment { /// Describes horizontal alignment preference for positioning & bounds. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)] -#[reflect_value(Serialize, Deserialize)] +#[reflect_value(Deserialize)] pub enum HorizontalAlign { /// Leftmost character is immediately to the right of the render position.
/// Bounds start from the render position and advance rightwards. @@ -113,7 +113,7 @@ impl From for glyph_brush_layout::HorizontalAlign { /// Describes vertical alignment preference for positioning & bounds. Currently a placeholder /// for future functionality. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)] -#[reflect_value(Serialize, Deserialize)] +#[reflect_value(Deserialize)] pub enum VerticalAlign { /// Characters/bounds start underneath the render position and progress downwards. Top, diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 03a269c690dca..a0eb7e0c2a598 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -18,7 +18,7 @@ use smallvec::SmallVec; /// /// This is commonly queried with a `Changed` filter. #[derive(Component, Copy, Clone, Eq, PartialEq, Debug, Reflect, Serialize, Deserialize)] -#[reflect_value(Component, Serialize, Deserialize, PartialEq)] +#[reflect_value(Component, Deserialize, PartialEq)] pub enum Interaction { /// The node has been clicked Clicked, @@ -36,7 +36,7 @@ impl Default for Interaction { /// Describes whether the node should block interactions with lower nodes #[derive(Component, Copy, Clone, Eq, PartialEq, Debug, Reflect, Serialize, Deserialize)] -#[reflect_value(Component, Serialize, Deserialize, PartialEq)] +#[reflect_value(Component, Deserialize, PartialEq)] pub enum FocusPolicy { /// Blocks interaction Block, diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 56390d4a465e1..cbf37c720f769 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -21,7 +21,7 @@ pub struct Node { /// An enum that describes possible types of value in flexbox layout options #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum Val { /// No value defined Undefined, @@ -145,7 +145,7 @@ impl Default for Style { /// How items are aligned according to the cross axis #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum AlignItems { /// Items are aligned at the start FlexStart, @@ -167,7 +167,7 @@ impl Default for AlignItems { /// Works like [`AlignItems`] but applies only to a single item #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum AlignSelf { /// Use the value of [`AlignItems`] Auto, @@ -193,7 +193,7 @@ impl Default for AlignSelf { /// /// It only applies if [`FlexWrap::Wrap`] is present and if there are multiple lines of items. #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum AlignContent { /// Each line moves towards the start of the cross axis FlexStart, @@ -221,7 +221,7 @@ impl Default for AlignContent { /// /// For example English is written LTR (left-to-right) while Arabic is written RTL (right-to-left). #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum Direction { /// Inherit from parent node Inherit, @@ -239,7 +239,7 @@ impl Default for Direction { /// Whether to use Flexbox layout #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum Display { /// Use flexbox Flex, @@ -255,7 +255,7 @@ impl Default for Display { /// Defines how flexbox items are ordered within a flexbox #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum FlexDirection { /// Same way as text direction along the main axis Row, @@ -275,7 +275,7 @@ impl Default for FlexDirection { /// Defines how items are aligned according to the main axis #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum JustifyContent { /// Pushed towards the start FlexStart, @@ -299,7 +299,7 @@ impl Default for JustifyContent { /// Whether to show or hide overflowing items #[derive(Copy, Clone, PartialEq, Debug, Reflect, Serialize, Deserialize)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum Overflow { /// Show overflowing items Visible, @@ -315,7 +315,7 @@ impl Default for Overflow { /// The strategy used to position this node #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum PositionType { /// Relative to all other nodes with the [`PositionType::Relative`] value Relative, @@ -333,7 +333,7 @@ impl Default for PositionType { /// Defines if flexbox items appear on a single line or on multiple lines #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum FlexWrap { /// Single line, will overflow if needed NoWrap, diff --git a/crates/bevy_ui/src/widget/image.rs b/crates/bevy_ui/src/widget/image.rs index 21f7d6288e803..095e49a749a22 100644 --- a/crates/bevy_ui/src/widget/image.rs +++ b/crates/bevy_ui/src/widget/image.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; /// Describes how to resize the Image node #[derive(Component, Debug, Clone, Reflect, Serialize, Deserialize)] -#[reflect_value(Component, Serialize, Deserialize)] +#[reflect_value(Component, Deserialize)] pub enum ImageMode { /// Keep the aspect ratio of the image KeepAspect, diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index d6e7227699eae..54d290d50b344 100644 --- a/examples/reflection/reflection_types.rs +++ b/examples/reflection/reflection_types.rs @@ -37,7 +37,7 @@ pub struct C(usize); /// `Reflect::serialize()`. You can force these implementations to use the actual trait /// implementations (instead of their defaults) like this: #[derive(Reflect, Hash, Serialize, PartialEq)] -#[reflect(Hash, Serialize, PartialEq)] +#[reflect(Hash, PartialEq)] pub struct D { x: usize, } @@ -48,7 +48,7 @@ pub struct D { /// traits on `reflect_value` types to ensure that these values behave as expected when nested /// underneath Reflect-ed structs. #[derive(Reflect, Copy, Clone, PartialEq, Serialize, Deserialize)] -#[reflect_value(PartialEq, Serialize, Deserialize)] +#[reflect_value(PartialEq, Deserialize)] pub enum E { X, Y, From 991af6a37b9f22b8081a9613869c05410a9053be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Bergstr=C3=B6m?= Date: Sun, 29 May 2022 20:30:56 +0200 Subject: [PATCH 06/13] fix tests --- crates/bevy_reflect/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 005bd5d14cb74..93d8d246b814b 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -884,8 +884,8 @@ bevy_reflect::tests::should_reflect_debug::Test { let v = vec3(12.0, 3.0, -6.9); let mut registry = TypeRegistry::default(); - registry.add_registration(Vec3::get_type_registration()); - registry.add_registration(f32::get_type_registration()); + register_type!(registry, Vec3, erased_serde::Serialize); + register_type!(registry, f32, erased_serde::Serialize); let ser = ReflectSerializer::new(&v, ®istry); From 17b31cc2b0a023b7c310c713d0b0546c5ac6627c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Bergstr=C3=B6m?= Date: Sun, 29 May 2022 21:12:27 +0200 Subject: [PATCH 07/13] maybe_type_data macro for getting a FromType reflection struct only if a type implements it, without requiring it at compile time --- crates/bevy_reflect/src/type_registry.rs | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index e5899592e605d..060340a336bb3 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -304,6 +304,34 @@ macro_rules! maybe_trait_cast { }}; } +#[doc(hidden)] +pub trait NotFromType { + const FROM_TYPE_FN: Option Self> = None; +} +impl NotFromType for T {} + +#[macro_export] +macro_rules! maybe_type_data { + ($this_type:ty, $type_data_ty:path) => {{ + { + #[allow(unused_imports)] + use $crate::NotFromType; + struct IsFromTypeImplemented(core::marker::PhantomData); + impl + 'static> IsFromTypeImplemented { + #[allow(unused)] + const FROM_TYPE_FN: Option T> = + Some(>::from_type); + } + if IsFromTypeImplemented::<$type_data_ty>::FROM_TYPE_FN.is_some() { + let v = IsFromTypeImplemented::<$type_data_ty>::FROM_TYPE_FN.unwrap()(); + Some(v) + } else { + None + } + } + }}; +} + #[macro_export] macro_rules! register_type { ($type_registry:ident, $this_type:ty, $($trait_type:path),* $(,)?) => {{ @@ -692,4 +720,14 @@ mod tests { let erased = ErasedNonNull::new(input); let _ = unsafe { erased.into_ref::() }; } + + #[test] + fn from_type_macro() { + let f32_deser = maybe_type_data!(f32, ReflectDeserialize); + let file_deser = maybe_type_data!(std::fs::File, ReflectDeserialize); + let none_deser = maybe_type_data!((), ReflectDeserialize); + assert!(f32_deser.is_some()); + assert!(file_deser.is_none()); + assert!(none_deser.is_some()); + } } From d75871bc1669c8f0b8a9e6bb615a19444b673fa3 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 13 Jun 2022 00:31:28 -0700 Subject: [PATCH 08/13] Replace registrations with register_all --- crates/bevy_core/src/lib.rs | 63 +++++++++++++++--------- crates/bevy_reflect/src/impls/std.rs | 30 +++++++++++ crates/bevy_reflect/src/type_registry.rs | 16 +----- 3 files changed, 72 insertions(+), 37 deletions(-) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 7c7d193564f87..03172250f6c29 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -16,8 +16,7 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_ecs::entity::Entity; -use bevy_utils::HashSet; -use std::ops::Range; +use bevy_reflect::TypeRegistryArc; /// Adds core functionality to Apps. #[derive(Default)] @@ -34,29 +33,49 @@ impl Plugin for CorePlugin { app.register_type::().register_type::(); - register_rust_types(app); - register_math_types(app); + let registry = app.world.resource::(); + let mut registry = registry.write(); + rust_types::register_types(&mut registry); + math_types::register_types(&mut registry); } } -fn register_rust_types(app: &mut App) { - app.register_type::>() - .register_type::() - .register_type::>() - .register_type::>(); +mod rust_types { + use bevy_reflect::erased_serde::Serialize; + use bevy_reflect::register_all; + use bevy_utils::HashSet; + use std::ops::Range; + + register_all! { + traits: [Serialize], + types: [ + String, + Option, + Range, + HashSet + ] + } } -fn register_math_types(app: &mut App) { - app.register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::(); +mod math_types { + use bevy_reflect::erased_serde::Serialize; + use bevy_reflect::register_all; + + register_all! { + traits: [Serialize], + types: [ + bevy_math::IVec2, + bevy_math::IVec3, + bevy_math::IVec4, + bevy_math::UVec2, + bevy_math::UVec3, + bevy_math::UVec4, + bevy_math::Vec2, + bevy_math::Vec3, + bevy_math::Vec4, + bevy_math::Mat3, + bevy_math::Mat4, + bevy_math::Quat, + ] + } } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 45151a38d515f..278ef136c7fcf 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -63,6 +63,36 @@ impl_from_reflect_value!( impl_from_reflect_value!(Range Deserialize<'de> + Clone + Send + Sync + 'static>); impl_from_reflect_value!(Duration); +pub mod registrations { + use crate as bevy_reflect; + use crate::erased_serde::Serialize; + use crate::register_all; + + register_all! { + traits: [Serialize], + types: [ + bool, + char, + u8, + u16, + u32, + u64, + u128, + usize, + i8, + i16, + i32, + i64, + i128, + isize, + f32, + f64, + String, + Option, + ] + } +} + impl Array for Vec { #[inline] fn get(&self, index: usize) -> Option<&dyn Reflect> { diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 060340a336bb3..26983b818099c 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -54,21 +54,7 @@ impl TypeRegistry { /// Create a type registry with default registrations for primitive types. pub fn new() -> Self { let mut registry = Self::empty(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); - registry.register::(); + crate::impls::registrations::register_types(&mut registry); registry } From 0ce3c0fe12c169d229d6058b26b28332b8b7d8b4 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 13 Jun 2022 01:01:55 -0700 Subject: [PATCH 09/13] Formatting --- crates/bevy_reflect/src/impls/std.rs | 8 ++++---- crates/bevy_reflect/src/list.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 278ef136c7fcf..98100c0d91065 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -5,6 +5,7 @@ use crate::{ ReflectMut, ReflectRef, TypeInfo, TypeRegistration, Typed, ValueInfo, }; +use crate::serde::Serializable; use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value}; use bevy_utils::{Duration, HashMap, HashSet}; @@ -15,7 +16,6 @@ use std::{ hash::{Hash, Hasher}, ops::Range, }; -use crate::serde::Serializable; impl_reflect_value!(bool(Debug, Hash, PartialEq, Deserialize)); impl_reflect_value!(char(Debug, Hash, PartialEq, Deserialize)); @@ -329,9 +329,9 @@ impl Typed for HashMap { } impl GetTypeRegistration for HashMap - where - K: Reflect + Clone + Eq + Hash + for<'de> Deserialize<'de>, - V: Reflect + Clone + for<'de> Deserialize<'de>, +where + K: Reflect + Clone + Eq + Hash + for<'de> Deserialize<'de>, + V: Reflect + Clone + for<'de> Deserialize<'de>, { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 6eb56f72a25f5..83de466486d54 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -1,12 +1,12 @@ use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; +use crate::serde::Serializable; use crate::utility::NonGenericTypeInfoCell; use crate::{ Array, ArrayIter, DynamicArray, DynamicInfo, FromReflect, Reflect, ReflectMut, ReflectRef, TypeInfo, Typed, }; -use crate::serde::Serializable; /// An ordered, mutable list of [Reflect] items. This corresponds to types like [`std::vec::Vec`]. /// From feb6c0731a8ea67d7fd61a27b55c891bd3e43fee Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 13 Jun 2022 01:04:22 -0700 Subject: [PATCH 10/13] Fix clippy errors --- crates/bevy_reflect/src/lib.rs | 1 + crates/bevy_reflect/src/type_registry.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 93d8d246b814b..77ff527184bb2 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -876,6 +876,7 @@ bevy_reflect::tests::should_reflect_debug::Test { #[cfg(feature = "glam")] mod glam { + use std::any::TypeId; use super::*; use ::serde::Serialize; diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 26983b818099c..755341b76254f 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -226,7 +226,7 @@ impl ErasedNonNull { &val as *const *const T as *const u8, storage.as_mut_ptr() as *mut u8, size, - ) + ); }; Self { storage, From 5c226e15ac7e2ecb0313afba12a4e397cef23010 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 16 Jun 2022 23:48:08 -0700 Subject: [PATCH 11/13] Added ability to register_all type data --- .../bevy_reflect_derive/src/lib.rs | 18 ++- .../bevy_reflect_derive/src/registration.rs | 134 +++++++++++------- crates/bevy_reflect/src/lib.rs | 2 +- 3 files changed, 102 insertions(+), 52 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 6771fffc7623e..86b7e979b9e78 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -173,10 +173,20 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path) } -/// A macro that allows for mass type and trait registration. +/// A macro that allows for mass type, trait, and type data registration. /// /// This will generate a public function with the signature: `fn register_types(&mut TypeRegistry)`. -/// You can then use this generated function to register the given types and traits. +/// You can then use this generated function to register the given types, traits, and type data. +/// +/// The only required field is `types`. Both the `traits` and `data` fields can be omitted if +/// they are not needed. +/// +/// To register a trait cast, add the trait to the `traits` list. +/// +/// To register type data, add the type data to the `data` list. +/// +/// Everything in the `traits` and `data` lists will be registered for all types in `types` +/// that are able to register them. /// /// # Example /// @@ -189,10 +199,12 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { /// /// impl MyTrait for MyType {} /// -/// // Not all types need to implement all traits +/// // Not all types need to implement all traits or type data /// register_all! { /// traits: [MyTrait], /// types: [MyType, MyOtherType], +/// // You can also register type data as well: +/// // data: [ReflectMyOtherTrait] /// } /// /// ``` diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 3b825a942fdfe..6b013e51d5acf 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -3,7 +3,7 @@ use bevy_macro_utils::BevyManifest; use proc_macro::TokenStream; use proc_macro2::Ident; -use quote::quote; +use quote::{format_ident, quote}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::{Iter, Punctuated}; use syn::{bracketed, parse_macro_input, Token, Type}; @@ -32,18 +32,40 @@ pub(crate) fn impl_get_type_registration( pub fn register_all_internal(item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as RegisterAllData); - let registration_params = input.types().map(|ty| { - let trait_type = input.traits(); - quote! { - #ty, #(#trait_type),* - } - }); - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + let registry_ident = format_ident!("registry"); + + let empty = Punctuated::::default(); + let registrations = input.types().map(|ty| { + let type_data = input.type_data().unwrap_or_else(|| empty.iter()); + let trait_type = input.traits().unwrap_or_else(|| empty.iter()); + quote! {{ + // Get or create registration for type + let type_registration = match #registry_ident.get_mut(::std::any::TypeId::of::<#ty>()) { + Some(registration) => registration, + None => { + #registry_ident.register::<#ty>(); + #registry_ident.get_mut(::std::any::TypeId::of::<#ty>()).unwrap() + } + }; + // Register all possible trait casts + #( + if let Some(cast_fn) = #bevy_reflect_path::maybe_trait_cast!(#ty, #trait_type) { + type_registration.register_trait_cast::(cast_fn); + } + )* + // Register all possible type data + #( + if let Some(data) = #bevy_reflect_path::maybe_type_data!(#ty, #type_data) { + type_registration.insert(data); + } + )* + }} + }); TokenStream::from(quote! { - pub fn register_types(registry: &mut #bevy_reflect_path::TypeRegistry) { - #(#bevy_reflect_path::register_type!(registry, #registration_params));* + pub fn register_types(#registry_ident: &mut #bevy_reflect_path::TypeRegistry) { + #(#registrations)* } }) } @@ -51,22 +73,29 @@ pub fn register_all_internal(item: TokenStream) -> TokenStream { /// Maps to the following invocation: /// /// ``` -/// use bevy_reflect_derive::register_all; +/// use bevy_reflect_derive::{register_all, reflect_trait}; /// /// trait MyTrait {} /// struct MyType {} +/// #[reflect_trait] +/// trait MyData {} /// /// register_all! { -/// traits: [MyTrait], /// types: [MyType], +/// traits: [MyTrait], +/// data: [ReflectMyData], /// } /// ``` /// -/// > Note: The order of the `traits` and `types` fields does not matter. Additionally, +/// > Note: The order of the `traits`, `data`, and `types` fields does not matter. Additionally, /// > the commas (separating and trailing) may be omitted entirely. +/// +/// The only required field in this macro is the `types` field. All others can be omitted if +/// desired. struct RegisterAllData { - trait_list: Punctuated, type_list: Punctuated, + trait_list: Option>, + data_list: Option>, } impl RegisterAllData { @@ -76,8 +105,13 @@ impl RegisterAllData { } /// Returns an iterator over the traits to register. - fn traits(&self) -> Iter { - self.trait_list.iter() + fn traits(&self) -> Option> { + self.trait_list.as_ref().map(|list| list.iter()) + } + + /// Returns an iterator over the type data to register. + fn type_data(&self) -> Option> { + self.data_list.as_ref().map(|list| list.iter()) } /// Parse a list of types. @@ -87,51 +121,55 @@ impl RegisterAllData { input.parse::()?; let list; bracketed!(list in input); - list.parse_terminated(Type::parse) + let parsed = list.parse_terminated(Type::parse)?; + // Parse optional trailing comma + input.parse::>()?; + Ok(parsed) } } impl Parse for RegisterAllData { fn parse(mut input: ParseStream) -> syn::Result { - let trait_list; - let type_list; + let mut trait_list = None; + let mut type_list = None; + let mut data_list = None; - let lookahead = input.lookahead1(); - if lookahead.peek(kw::traits) { - // Parse `traits` then `types` - input.parse::()?; - trait_list = Self::parse_list(&mut input)?; - // Optional separating comma - input.parse::>()?; - input.parse::()?; - type_list = Self::parse_list(&mut input)?; - // Optional trailing comma - input.parse::>()?; - } else if lookahead.peek(kw::types) { - // Parse `types` then `traits` - input.parse::()?; - type_list = Self::parse_list(&mut input)?; - // Optional separating comma - input.parse::>()?; - input.parse::()?; - trait_list = Self::parse_list(&mut input)?; - // Optional trailing comma - input.parse::>()?; + while !input.is_empty() { + let lookahead = input.lookahead1(); + if lookahead.peek(kw::traits) { + input.parse::()?; + trait_list = Some(Self::parse_list(&mut input)?); + } else if lookahead.peek(kw::types) { + input.parse::()?; + type_list = Some(Self::parse_list(&mut input)?); + } else if lookahead.peek(kw::data) { + input.parse::()?; + data_list = Some(Self::parse_list(&mut input)?); + } else { + return Err(syn::Error::new( + input.span(), + "expected either 'traits', 'types', or 'data' field", + )); + } + } + + if let Some(type_list) = type_list { + Ok(Self { + trait_list, + type_list, + data_list, + }) } else { - return Err(syn::Error::new( + Err(syn::Error::new( input.span(), - "expected either 'traits' or 'types' field", - )); + "missing required field 'types'", + )) } - - Ok(Self { - trait_list, - type_list, - }) } } mod kw { syn::custom_keyword!(traits); syn::custom_keyword!(types); + syn::custom_keyword!(data); } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 77ff527184bb2..6b08b9b6a02f0 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -876,9 +876,9 @@ bevy_reflect::tests::should_reflect_debug::Test { #[cfg(feature = "glam")] mod glam { - use std::any::TypeId; use super::*; use ::serde::Serialize; + use std::any::TypeId; #[test] fn vec3_serialization() { From 4abe4e91f055dc50ae6daa77e3a8f026d8c1c40e Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 17 Jun 2022 00:06:42 -0700 Subject: [PATCH 12/13] Fix CI errors --- crates/bevy_reflect/bevy_reflect_derive/src/registration.rs | 2 +- crates/bevy_reflect/src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 6b013e51d5acf..092347c4b0e14 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -72,7 +72,7 @@ pub fn register_all_internal(item: TokenStream) -> TokenStream { /// Maps to the following invocation: /// -/// ``` +/// ```ignore /// use bevy_reflect_derive::{register_all, reflect_trait}; /// /// trait MyTrait {} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 6b08b9b6a02f0..f32181ad7553c 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -877,7 +877,6 @@ bevy_reflect::tests::should_reflect_debug::Test { #[cfg(feature = "glam")] mod glam { use super::*; - use ::serde::Serialize; use std::any::TypeId; #[test] From 57cfaef44f77b797239340f2b5852ec8fbe091ea Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 17 Jun 2022 00:12:59 -0700 Subject: [PATCH 13/13] Ignore doc test --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 86b7e979b9e78..259a6f69722f3 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -190,7 +190,7 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { /// /// # Example /// -/// ``` +/// ```ignore /// use bevy_reflect_derive::register_all; /// /// trait MyTrait {}