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_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_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/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 7cf9fe560b004..259a6f69722f3 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -172,3 +172,43 @@ 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, 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, 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 +/// +/// ```ignore +/// use bevy_reflect_derive::register_all; +/// +/// trait MyTrait {} +/// struct MyType; +/// struct MyOtherType; +/// +/// impl MyTrait for MyType {} +/// +/// // 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] +/// } +/// +/// ``` +#[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..092347c4b0e14 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 quote::{format_ident, 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,148 @@ pub(crate) fn impl_get_type_registration( } } } + +pub fn register_all_internal(item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as RegisterAllData); + + 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_ident: &mut #bevy_reflect_path::TypeRegistry) { + #(#registrations)* + } + }) +} + +/// Maps to the following invocation: +/// +/// ```ignore +/// use bevy_reflect_derive::{register_all, reflect_trait}; +/// +/// trait MyTrait {} +/// struct MyType {} +/// #[reflect_trait] +/// trait MyData {} +/// +/// register_all! { +/// types: [MyType], +/// traits: [MyTrait], +/// data: [ReflectMyData], +/// } +/// ``` +/// +/// > 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 { + type_list: Punctuated, + trait_list: Option>, + data_list: Option>, +} + +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) -> 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. + /// + /// 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); + 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 mut trait_list = None; + let mut type_list = None; + let mut data_list = None; + + 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 { + Err(syn::Error::new( + input.span(), + "missing required field 'types'", + )) + } + } +} + +mod kw { + syn::custom_keyword!(traits); + syn::custom_keyword!(types); + syn::custom_keyword!(data); +} 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..98100c0d91065 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,14 +1,15 @@ -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::serde::Serializable; 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, @@ -16,27 +17,27 @@ use std::{ 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)); +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,17 +56,43 @@ 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); +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> { @@ -541,10 +568,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 +593,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 2abfdc8d65f93..1620f6d30bd3a 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; @@ -465,14 +466,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(); @@ -871,13 +877,15 @@ bevy_reflect::tests::should_reflect_debug::Test { #[cfg(feature = "glam")] mod glam { use super::*; + use std::any::TypeId; #[test] fn vec3_serialization() { let v = vec3(12.0, 3.0, -6.9); let mut registry = TypeRegistry::default(); - registry.add_registration(Vec3::get_type_registration()); + register_type!(registry, Vec3, erased_serde::Serialize); + register_type!(registry, f32, erased_serde::Serialize); let ser = ReflectSerializer::new(&v, ®istry); @@ -948,5 +956,59 @@ 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 {} + + 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/list.rs b/crates/bevy_reflect/src/list.rs index 66d3816707adb..83de466486d54 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -1,10 +1,11 @@ use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; +use crate::serde::Serializable; 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, }; /// 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..755341b76254f 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 { @@ -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 } @@ -221,6 +207,136 @@ impl TypeRegistryArc { } } +#[doc(hidden)] +pub struct ErasedNonNull { + storage: MaybeUninit<[NonNull<()>; 2]>, + ty: TypeId, +} + +impl ErasedNonNull { + /// 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, + storage.as_mut_ptr() as *mut u8, + size, + ); + }; + Self { + storage, + 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( + 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< + 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 + 'static)> = + 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) + }; + Some(f) + } else { + None + } + } + }}; +} + +#[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),* $(,)?) => {{ + 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); + }} + )* + }}; +} + /// A record of data about a type. /// /// This contains the [`TypeInfo`] of the type, as well as its [short name]. @@ -238,6 +354,7 @@ pub struct TypeRegistration { short_name: String, data: HashMap>, type_info: &'static TypeInfo, + trait_casts: HashMap ErasedNonNull>, } impl TypeRegistration { @@ -249,6 +366,28 @@ 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); + // SAFE: Registered trait and type matches the call site + Some(unsafe { raw.into_ref() }) + } else { + None + } + } + /// Returns a reference to the value of type `T` in this registration's type /// data. /// @@ -286,6 +425,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 +498,7 @@ impl Clone for TypeRegistration { TypeRegistration { data, + trait_casts: self.trait_casts.clone(), short_name: self.short_name.clone(), type_info: self.type_info, } @@ -425,9 +566,9 @@ impl Deserialize<'a> + Reflect> FromType for ReflectDeserialize { } #[cfg(test)] -mod test { - use crate::TypeRegistration; - use bevy_utils::HashMap; +mod tests { + use super::*; + use std::any::Any; #[test] fn test_get_short_name() { @@ -497,4 +638,82 @@ 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()); + } + + #[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 _ = 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()); + } } 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,