From a3a352281e32e7e3483b12bf691b477118b9ca4a Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Sun, 25 Jul 2021 08:47:49 +0200 Subject: [PATCH 1/4] Add FromReflect trait to convert dynamic types to concrete types --- crates/bevy_asset/src/handle.rs | 24 ++- crates/bevy_ecs/src/reflect.rs | 5 +- .../bevy_reflect_derive/src/from_reflect.rs | 151 ++++++++++++++++++ .../bevy_reflect_derive/src/lib.rs | 116 ++++++++++++++ crates/bevy_reflect/src/impls/glam.rs | 15 +- crates/bevy_reflect/src/impls/smallvec.rs | 33 +++- crates/bevy_reflect/src/impls/std.rs | 92 +++++++++-- crates/bevy_reflect/src/lib.rs | 57 ++++++- crates/bevy_reflect/src/path.rs | 2 +- crates/bevy_reflect/src/reflect.rs | 5 + crates/bevy_reflect/src/tuple.rs | 19 ++- 11 files changed, 490 insertions(+), 29 deletions(-) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index 5aeceda9253e1..bdbba2a25f150 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -10,14 +10,25 @@ use crate::{ Asset, Assets, }; use bevy_ecs::{component::Component, reflect::ReflectComponent}; -use bevy_reflect::{Reflect, ReflectDeserialize}; +use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize}; use bevy_utils::Uuid; use crossbeam_channel::{Receiver, Sender}; use serde::{Deserialize, Serialize}; /// A unique, stable asset id #[derive( - Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect, + Debug, + Clone, + Copy, + Eq, + PartialEq, + Hash, + Ord, + PartialOrd, + Serialize, + Deserialize, + Reflect, + FromReflect, )] #[reflect_value(Serialize, Deserialize, PartialEq, Hash)] pub enum HandleId { @@ -58,7 +69,7 @@ impl HandleId { /// /// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets) /// collection. -#[derive(Component, Reflect)] +#[derive(Component, Reflect, FromReflect)] #[reflect(Component)] pub struct Handle where @@ -77,6 +88,13 @@ enum HandleType { Strong(Sender), } +// FIXME: This only is needed because `Handle`'s field `handle_type` is currently ignored for reflection +impl Default for HandleType { + fn default() -> Self { + Self::Weak + } +} + impl Debug for HandleType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index a967c60449c8a..4881dd83f0edd 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -6,7 +6,9 @@ use crate::{ entity::{Entity, EntityMap, MapEntities, MapEntitiesError}, world::{FromWorld, World}, }; -use bevy_reflect::{impl_reflect_value, FromType, Reflect, ReflectDeserialize}; +use bevy_reflect::{ + impl_from_reflect_value, impl_reflect_value, FromType, Reflect, ReflectDeserialize, +}; #[derive(Clone)] pub struct ReflectComponent { @@ -121,6 +123,7 @@ impl FromType for ReflectComponent { } impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize)); +impl_from_reflect_value!(Entity); #[derive(Clone)] pub struct ReflectMapEntities { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs new file mode 100644 index 0000000000000..68d18d36a6f4c --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -0,0 +1,151 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{Field, Generics, Ident, Index, Member, Path}; + +pub fn impl_struct( + struct_name: &Ident, + generics: &Generics, + bevy_reflect_path: &Path, + active_fields: &[(&Field, usize)], + ignored_fields: &[(&Field, usize)], +) -> TokenStream { + let field_names = active_fields + .iter() + .map(|(field, index)| { + field + .ident + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| index.to_string()) + }) + .collect::>(); + let field_idents = active_fields + .iter() + .map(|(field, index)| { + field + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) + }) + .collect::>(); + + let field_types = active_fields + .iter() + .map(|(field, _index)| field.ty.clone()) + .collect::>(); + let field_count = active_fields.len(); + let ignored_field_idents = ignored_fields + .iter() + .map(|(field, index)| { + field + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) + }) + .collect::>(); + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + // Add FromReflect bound for each active field + let mut where_from_reflect_clause = if where_clause.is_some() { + quote! {#where_clause} + } else if field_count > 0 { + quote! {where} + } else { + quote! {} + }; + where_from_reflect_clause.extend(quote! { + #(#field_types: #bevy_reflect_path::FromReflect,)* + }); + + TokenStream::from(quote! { + impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause + { + fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { + use #bevy_reflect_path::Struct; + if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() { + Some( + Self{ + #(#field_idents: { + <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_struct.field(#field_names)?)? + },)* + #(#ignored_field_idents: Default::default(),)* + } + ) + } else { + None + } + } + } + }) +} + +pub fn impl_tuple_struct( + struct_name: &Ident, + generics: &Generics, + bevy_reflect_path: &Path, + active_fields: &[(&Field, usize)], + ignored_fields: &[(&Field, usize)], +) -> TokenStream { + let field_idents = active_fields + .iter() + .map(|(_field, index)| Member::Unnamed(Index::from(*index))) + .collect::>(); + let field_types = active_fields + .iter() + .map(|(field, _index)| field.ty.clone()) + .collect::>(); + let field_count = active_fields.len(); + let field_indices = (0..field_count).collect::>(); + let ignored_field_idents = ignored_fields + .iter() + .map(|(_field, index)| Member::Unnamed(Index::from(*index))) + .collect::>(); + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // Add FromReflect bound for each active field + let mut where_from_reflect_clause = if where_clause.is_some() { + quote! {#where_clause} + } else if field_count > 0 { + quote! {where} + } else { + quote! {} + }; + where_from_reflect_clause.extend(quote! { + #(#field_types: #bevy_reflect_path::FromReflect,)* + }); + + TokenStream::from(quote! { + impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause + { + fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { + use #bevy_reflect_path::TupleStruct; + if let #bevy_reflect_path::ReflectRef::TupleStruct(ref_tuple_struct) = reflect.reflect_ref() { + Some( + Self{ + #(#field_idents: + <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_tuple_struct.field(#field_indices)?)? + ,)* + #(#ignored_field_idents: Default::default(),)* + } + ) + } else { + None + } + } + } + }) +} + +pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + TokenStream::from(quote! { + impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { + fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { + Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone()) + } + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 45d40b0ff0a58..4f481cb45b412 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -1,5 +1,6 @@ extern crate proc_macro; +mod from_reflect; mod reflect_trait; mod type_uuid; @@ -740,3 +741,118 @@ pub fn external_type_uuid(tokens: proc_macro::TokenStream) -> proc_macro::TokenS pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { reflect_trait::reflect_trait(args, input) } + +#[proc_macro_derive(FromReflect)] +pub fn derive_from_reflect(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let unit_struct_punctuated = Punctuated::new(); + let (fields, mut derive_type) = match &ast.data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => (&fields.named, DeriveType::Struct), + Data::Struct(DataStruct { + fields: Fields::Unnamed(fields), + .. + }) => (&fields.unnamed, DeriveType::TupleStruct), + Data::Struct(DataStruct { + fields: Fields::Unit, + .. + }) => (&unit_struct_punctuated, DeriveType::UnitStruct), + _ => (&unit_struct_punctuated, DeriveType::Value), + }; + + let fields_and_args = fields + .iter() + .enumerate() + .map(|(i, f)| { + ( + f, + f.attrs + .iter() + .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) + .map(|a| { + syn::custom_keyword!(ignore); + let mut attribute_args = PropAttributeArgs { ignore: None }; + a.parse_args_with(|input: ParseStream| { + if input.parse::>()?.is_some() { + attribute_args.ignore = Some(true); + return Ok(()); + } + Ok(()) + }) + .expect("Invalid 'property' attribute format."); + + attribute_args + }), + i, + ) + }) + .collect::, usize)>>(); + let active_fields = fields_and_args + .iter() + .filter(|(_field, attrs, _i)| { + attrs.is_none() + || match attrs.as_ref().unwrap().ignore { + Some(ignore) => !ignore, + None => true, + } + }) + .map(|(f, _attr, i)| (*f, *i)) + .collect::>(); + let ignored_fields = fields_and_args + .iter() + .filter(|(_field, attrs, _i)| { + attrs + .as_ref() + .map(|attrs| attrs.ignore.unwrap_or(false)) + .unwrap_or(false) + }) + .map(|(f, _attr, i)| (*f, *i)) + .collect::>(); + + let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + let type_name = &ast.ident; + + for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { + let meta_list = if let Meta::List(meta_list) = attribute { + meta_list + } else { + continue; + }; + + if let Some(ident) = meta_list.path.get_ident() { + if ident == REFLECT_ATTRIBUTE_NAME { + } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { + derive_type = DeriveType::Value; + } + } + } + + match derive_type { + DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct( + type_name, + &ast.generics, + &bevy_reflect_path, + &active_fields, + &ignored_fields, + ), + DeriveType::TupleStruct => from_reflect::impl_tuple_struct( + type_name, + &ast.generics, + &bevy_reflect_path, + &active_fields, + &ignored_fields, + ), + DeriveType::Value => from_reflect::impl_value(type_name, &ast.generics, &bevy_reflect_path), + } +} + +#[proc_macro] +pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { + let reflect_value_def = parse_macro_input!(input as ReflectDef); + + let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + let ty = &reflect_value_def.type_name; + from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path) +} diff --git a/crates/bevy_reflect/src/impls/glam.rs b/crates/bevy_reflect/src/impls/glam.rs index 93c749159e11e..de0c74c31c249 100644 --- a/crates/bevy_reflect/src/impls/glam.rs +++ b/crates/bevy_reflect/src/impls/glam.rs @@ -1,6 +1,6 @@ use crate as bevy_reflect; use crate::ReflectDeserialize; -use bevy_reflect_derive::impl_reflect_value; +use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value}; use glam::{IVec2, IVec3, IVec4, Mat3, Mat4, Quat, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4}; impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize)); @@ -15,3 +15,16 @@ impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize)); impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize)); impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize)); impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize)); + +impl_from_reflect_value!(IVec2); +impl_from_reflect_value!(IVec3); +impl_from_reflect_value!(IVec4); +impl_from_reflect_value!(UVec2); +impl_from_reflect_value!(UVec3); +impl_from_reflect_value!(UVec4); +impl_from_reflect_value!(Vec2); +impl_from_reflect_value!(Vec3); +impl_from_reflect_value!(Vec4); +impl_from_reflect_value!(Mat3); +impl_from_reflect_value!(Mat4); +impl_from_reflect_value!(Quat); diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index 942f5d3abf910..4d6417c6df068 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -1,11 +1,11 @@ use smallvec::{Array, SmallVec}; use std::any::Any; -use crate::{serde::Serializable, List, ListIter, Reflect, ReflectMut, ReflectRef}; +use crate::{serde::Serializable, FromReflect, List, ListIter, Reflect, ReflectMut, ReflectRef}; impl List for SmallVec where - T::Item: Reflect + Clone, + T::Item: FromReflect + Clone, { fn get(&self, index: usize) -> Option<&dyn Reflect> { if index < SmallVec::len(self) { @@ -29,10 +29,12 @@ where fn push(&mut self, value: Box) { let value = value.take::().unwrap_or_else(|value| { - panic!( - "Attempted to push invalid value of type {}.", - value.type_name() - ) + ::Item::from_reflect(&*value).unwrap_or_else(|| { + panic!( + "Attempted to push invalid value of type {}.", + value.type_name() + ) + }) }); SmallVec::push(self, value); } @@ -48,7 +50,7 @@ where // SAFE: any and any_mut both return self unsafe impl Reflect for SmallVec where - T::Item: Reflect + Clone, + T::Item: FromReflect + Clone, { fn type_name(&self) -> &str { std::any::type_name::() @@ -95,3 +97,20 @@ where None } } + +impl FromReflect for SmallVec +where + T::Item: FromReflect + Clone, +{ + fn from_reflect(reflect: &dyn Reflect) -> Option { + if let ReflectRef::List(ref_list) = reflect.reflect_ref() { + let mut new_list = Self::with_capacity(ref_list.len()); + for field in ref_list.iter() { + new_list.push(::Item::from_reflect(field)?); + } + Some(new_list) + } else { + None + } + } +} diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index bb8067fee5066..9f4d7708eec31 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,11 +1,12 @@ use crate as bevy_reflect; use crate::{ - map_partial_eq, serde::Serializable, DynamicMap, FromType, GetTypeRegistration, List, ListIter, - Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef, TypeRegistration, + map_partial_eq, serde::Serializable, DynamicMap, FromReflect, FromType, GetTypeRegistration, + List, ListIter, Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef, + TypeRegistration, }; -use bevy_reflect_derive::impl_reflect_value; -use bevy_utils::{Duration, HashMap, HashSet}; +use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value}; +use bevy_utils::{AHashExt, Duration, HashMap, HashSet}; use serde::{Deserialize, Serialize}; use std::{ any::Any, @@ -35,7 +36,34 @@ impl_reflect_value!(HashSet Deserial impl_reflect_value!(Range Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); impl_reflect_value!(Duration); -impl List for Vec { +impl_from_reflect_value!(bool); +impl_from_reflect_value!(u8); +impl_from_reflect_value!(u16); +impl_from_reflect_value!(u32); +impl_from_reflect_value!(u64); +impl_from_reflect_value!(u128); +impl_from_reflect_value!(usize); +impl_from_reflect_value!(i8); +impl_from_reflect_value!(i16); +impl_from_reflect_value!(i32); +impl_from_reflect_value!(i64); +impl_from_reflect_value!(i128); +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> + Reflect + 'static> +); +impl_from_reflect_value!( + HashSet Deserialize<'de> + Send + Sync + 'static> +); +impl_from_reflect_value!( + Range Deserialize<'de> + Send + Sync + 'static> +); +impl_from_reflect_value!(Duration); + +impl List for Vec { fn get(&self, index: usize) -> Option<&dyn Reflect> { <[T]>::get(self, index).map(|value| value as &dyn Reflect) } @@ -57,17 +85,19 @@ impl List for Vec { fn push(&mut self, value: Box) { let value = value.take::().unwrap_or_else(|value| { - panic!( - "Attempted to push invalid value of type {}.", - value.type_name() - ) + T::from_reflect(&*value).unwrap_or_else(|| { + panic!( + "Attempted to push invalid value of type {}.", + value.type_name() + ) + }) }); Vec::push(self, value); } } // SAFE: any and any_mut both return self -unsafe impl Reflect for Vec { +unsafe impl Reflect for Vec { fn type_name(&self) -> &str { std::any::type_name::() } @@ -114,7 +144,7 @@ unsafe impl Reflect for Vec { } } -impl Deserialize<'de>> GetTypeRegistration for Vec { +impl Deserialize<'de>> GetTypeRegistration for Vec { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); registration.insert::(FromType::>::from_type()); @@ -122,7 +152,21 @@ impl Deserialize<'de>> GetTypeRegistration for Vec { } } -impl Map for HashMap { +impl FromReflect for Vec { + fn from_reflect(reflect: &dyn Reflect) -> Option { + if let ReflectRef::List(ref_list) = reflect.reflect_ref() { + let mut new_list = Self::with_capacity(ref_list.len()); + for field in ref_list.iter() { + new_list.push(T::from_reflect(field)?); + } + Some(new_list) + } else { + None + } + } +} + +impl Map for HashMap { fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> { key.downcast_ref::() .and_then(|key| HashMap::get(self, key)) @@ -163,7 +207,7 @@ impl Map for HashMap { } // SAFE: any and any_mut both return self -unsafe impl Reflect for HashMap { +unsafe impl Reflect for HashMap { fn type_name(&self) -> &str { std::any::type_name::() } @@ -230,6 +274,22 @@ where } } +impl FromReflect for HashMap { + fn from_reflect(reflect: &dyn Reflect) -> Option { + if let ReflectRef::Map(ref_map) = reflect.reflect_ref() { + let mut new_map = Self::with_capacity(ref_map.len()); + for (key, value) in ref_map.iter() { + let new_key = K::from_reflect(key)?; + let new_value = V::from_reflect(value)?; + new_map.insert(new_key, new_value); + } + Some(new_map) + } else { + None + } + } +} + // SAFE: any and any_mut both return self unsafe impl Reflect for Cow<'static, str> { fn type_name(&self) -> &str { @@ -298,3 +358,9 @@ impl GetTypeRegistration for Cow<'static, str> { registration } } + +impl FromReflect for Cow<'static, str> { + fn from_reflect(reflect: &dyn crate::Reflect) -> Option { + Some(reflect.any().downcast_ref::>()?.clone()) + } +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 7726ce7ee4e76..7b051bae89d14 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -194,7 +194,7 @@ mod tests { #[test] fn reflect_complex_patch() { - #[derive(Reflect, Eq, PartialEq, Debug)] + #[derive(Reflect, Eq, PartialEq, Debug, FromReflect)] #[reflect(PartialEq)] struct Foo { a: u32, @@ -204,17 +204,25 @@ mod tests { d: HashMap, e: Bar, f: (i32, Vec, Bar), + g: Vec<(Baz, HashMap)>, } - #[derive(Reflect, Eq, PartialEq, Debug)] + #[derive(Reflect, Eq, PartialEq, Clone, Debug, FromReflect)] #[reflect(PartialEq)] struct Bar { x: u32, } + #[derive(Reflect, Eq, PartialEq, Debug, FromReflect)] + struct Baz(String); + let mut hash_map = HashMap::default(); hash_map.insert(1, 1); hash_map.insert(2, 2); + + let mut hash_map_baz = HashMap::default(); + hash_map_baz.insert(1, Bar { x: 0 }); + let mut foo = Foo { a: 1, _b: 1, @@ -222,6 +230,7 @@ mod tests { d: hash_map, e: Bar { x: 1 }, f: (1, vec![1, 2], Bar { x: 1 }), + g: vec![(Baz("string".to_string()), hash_map_baz)], }; let mut foo_patch = DynamicStruct::default(); @@ -248,11 +257,36 @@ mod tests { tuple.insert(bar_patch); foo_patch.insert("f", tuple); + let mut composite = DynamicList::default(); + composite.push({ + let mut tuple = DynamicTuple::default(); + tuple.insert({ + let mut tuple_struct = DynamicTupleStruct::default(); + tuple_struct.insert("new_string".to_string()); + tuple_struct + }); + tuple.insert({ + let mut map = DynamicMap::default(); + map.insert(1usize, { + let mut struct_ = DynamicStruct::default(); + struct_.insert("x", 7u32); + struct_ + }); + map + }); + tuple + }); + foo_patch.insert("g", composite); + foo.apply(&foo_patch); let mut hash_map = HashMap::default(); hash_map.insert(1, 1); hash_map.insert(2, 3); + + let mut hash_map_baz = HashMap::default(); + hash_map_baz.insert(1, Bar { x: 7 }); + let expected_foo = Foo { a: 2, _b: 1, @@ -260,9 +294,28 @@ mod tests { d: hash_map, e: Bar { x: 2 }, f: (2, vec![3, 4, 5], Bar { x: 2 }), + g: vec![(Baz("new_string".to_string()), hash_map_baz.clone())], }; assert_eq!(foo, expected_foo); + + let new_foo = Foo::from_reflect(&foo_patch) + .expect("error while creating a concrete type from a dynamic type"); + + let mut hash_map = HashMap::default(); + hash_map.insert(2, 3); + + let expected_new_foo = Foo { + a: 2, + _b: 0, + c: vec![3, 4, 5], + d: hash_map, + e: Bar { x: 2 }, + f: (2, vec![3, 4, 5], Bar { x: 2 }), + g: vec![(Baz("new_string".to_string()), hash_map_baz)], + }; + + assert_eq!(new_foo, expected_new_foo); } #[test] diff --git a/crates/bevy_reflect/src/path.rs b/crates/bevy_reflect/src/path.rs index 12f36e5dd7426..44b4450c0dd82 100644 --- a/crates/bevy_reflect/src/path.rs +++ b/crates/bevy_reflect/src/path.rs @@ -326,7 +326,7 @@ mod tests { bar: C, } - #[derive(Reflect)] + #[derive(Reflect, FromReflect)] struct C { baz: f32, } diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 9b0dea4b09a39..10615b7078843 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -47,6 +47,11 @@ pub unsafe trait Reflect: Any + Send + Sync { fn serializable(&self) -> Option; } +pub trait FromReflect: Reflect + Sized { + /// Creates a clone of a reflected value, converting it to a concrete type if it was a dynamic types (e.g. [DynamicStruct]) + fn from_reflect(reflect: &dyn Reflect) -> Option; +} + impl Debug for dyn Reflect { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Reflect({})", self.type_name()) diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index 75366d2a6b04b..ed921aaf9e147 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -1,6 +1,6 @@ use std::any::Any; -use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef}; +use crate::{serde::Serializable, FromReflect, Reflect, ReflectMut, ReflectRef}; pub trait Tuple: Reflect { fn field(&self, index: usize) -> Option<&dyn Reflect>; @@ -329,6 +329,23 @@ macro_rules! impl_reflect_tuple { None } } + + impl<$($name: FromReflect),*> FromReflect for ($($name,)*) + { + fn from_reflect(reflect: &dyn Reflect) -> Option { + if let ReflectRef::Tuple(_ref_tuple) = reflect.reflect_ref() { + Some( + ( + $( + <$name as FromReflect>::from_reflect(_ref_tuple.field($index)?)?, + )* + ) + ) + } else { + None + } + } + } } } From d4e5162f6b0a6f4b4b6ba3cd2e5ba4c58353c57d Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Thu, 16 Dec 2021 21:49:32 +0100 Subject: [PATCH 2/4] Derive FromReflect for TextSection --- crates/bevy_render/src/color/mod.rs | 4 ++-- crates/bevy_text/src/lib.rs | 3 +-- crates/bevy_text/src/text.rs | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/bevy_render/src/color/mod.rs b/crates/bevy_render/src/color/mod.rs index 5a3816e205711..ea17e9bde1857 100644 --- a/crates/bevy_render/src/color/mod.rs +++ b/crates/bevy_render/src/color/mod.rs @@ -5,11 +5,11 @@ pub use colorspace::*; use crate::color::{HslRepresentation, SrgbColorSpace}; use bevy_core::Bytes; use bevy_math::{Vec3, Vec4}; -use bevy_reflect::{Reflect, ReflectDeserialize}; +use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize}; use serde::{Deserialize, Serialize}; use std::ops::{Add, AddAssign, Mul, MulAssign}; -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect, FromReflect)] #[reflect(PartialEq, Serialize, Deserialize)] pub enum Color { /// sRGBA color diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index 0b81909b3cec1..49ae22fbe5ba5 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -41,8 +41,7 @@ impl Plugin for TextPlugin { fn build(&self, app: &mut App) { app.add_asset::() .add_asset::() - // TODO: uncomment when #2215 is fixed - // .register_type::() + .register_type::() .register_type::() .register_type::() .init_asset_loader::() diff --git a/crates/bevy_text/src/text.rs b/crates/bevy_text/src/text.rs index 56d3631c40d26..2f2b37ab9a19e 100644 --- a/crates/bevy_text/src/text.rs +++ b/crates/bevy_text/src/text.rs @@ -1,7 +1,7 @@ use bevy_asset::Handle; use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; use bevy_math::Size; -use bevy_reflect::{Reflect, ReflectDeserialize}; +use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize}; use bevy_render::color::Color; use serde::{Deserialize, Serialize}; @@ -65,7 +65,7 @@ impl Text { } } -#[derive(Debug, Default, Clone, Reflect)] +#[derive(Debug, Default, Clone, FromReflect, Reflect)] pub struct TextSection { pub value: String, pub style: TextStyle, @@ -134,7 +134,7 @@ impl From for glyph_brush_layout::VerticalAlign { } } -#[derive(Clone, Debug, Reflect)] +#[derive(Clone, Debug, Reflect, FromReflect)] pub struct TextStyle { pub font: Handle, pub font_size: f32, From bb37b335722b303db371c2ecd1bad064c6cbb508 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Sun, 26 Dec 2021 19:30:02 +0100 Subject: [PATCH 3/4] Remove unnecessary line --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 4f481cb45b412..55201bff9116e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -822,8 +822,7 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream { }; if let Some(ident) = meta_list.path.get_ident() { - if ident == REFLECT_ATTRIBUTE_NAME { - } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { + if ident == REFLECT_VALUE_ATTRIBUTE_NAME { derive_type = DeriveType::Value; } } From 19afc4bfdc2faa6709be0db4a40ab0defcdc266e Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 26 Dec 2021 10:48:24 -0800 Subject: [PATCH 4/4] fix doc link --- crates/bevy_reflect/src/reflect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 10615b7078843..7ad42390bf72e 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -48,7 +48,7 @@ pub unsafe trait Reflect: Any + Send + Sync { } pub trait FromReflect: Reflect + Sized { - /// Creates a clone of a reflected value, converting it to a concrete type if it was a dynamic types (e.g. [DynamicStruct]) + /// Creates a clone of a reflected value, converting it to a concrete type if it was a dynamic types (e.g. [`DynamicStruct`](crate::DynamicStruct)) fn from_reflect(reflect: &dyn Reflect) -> Option; }