From d8e595e69f446d5ca4e75387981c3931200a26c3 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 12 Oct 2022 14:24:26 -0700 Subject: [PATCH 1/2] Added ReflectFromReflect --- crates/bevy_reflect/src/from_reflect.rs | 92 +++++++++++++++++++++++++ crates/bevy_reflect/src/lib.rs | 39 +++++++++++ crates/bevy_reflect/src/reflect.rs | 17 +---- 3 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 crates/bevy_reflect/src/from_reflect.rs diff --git a/crates/bevy_reflect/src/from_reflect.rs b/crates/bevy_reflect/src/from_reflect.rs new file mode 100644 index 0000000000000..9a7f09c8772ee --- /dev/null +++ b/crates/bevy_reflect/src/from_reflect.rs @@ -0,0 +1,92 @@ +use crate::{FromType, Reflect}; + +/// A trait for types which can be constructed from a reflected type. +/// +/// This trait can be derived on types which implement [`Reflect`]. Some complex +/// types (such as `Vec`) may only be reflected if their element types +/// implement this trait. +/// +/// For structs and tuple structs, fields marked with the `#[reflect(ignore)]` +/// attribute will be constructed using the `Default` implementation of the +/// field type, rather than the corresponding field value (if any) of the +/// reflected value. +pub trait FromReflect: Reflect + Sized { + /// Constructs a concrete instance of `Self` from a reflected value. + fn from_reflect(reflect: &dyn Reflect) -> Option; +} + +/// Type data that represents the [`FromReflect`] trait and allows it to be used dynamically. +/// +/// `FromReflect` allows dynamic types (e.g. [`DynamicStruct`], [`DynamicEnum`], etc.) to be converted +/// to their full, concrete types. This is most important when it comes to deserialization where it isn't +/// guaranteed that every field exists when trying to construct the final output. +/// +/// However, to do this, you normally need to specify the exact concrete type: +/// +/// ``` +/// # use bevy_reflect::{DynamicTupleStruct, FromReflect, Reflect}; +/// #[derive(Reflect, FromReflect, PartialEq, Eq, Debug)] +/// struct Foo(#[reflect(default = "default_value")] usize); +/// +/// fn default_value() -> usize { 123 } +/// +/// let reflected = DynamicTupleStruct::default(); +/// +/// let concrete: Foo = ::from_reflect(&reflected).unwrap(); +/// +/// assert_eq!(Foo(123), concrete); +/// ``` +/// +/// In a dynamic context where the type might not be known at compile-time, this is nearly impossible to do. +/// That is why this type data struct exists— it allows us to construct the full type without knowing +/// what the actual type is. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::{DynamicTupleStruct, FromReflect, Reflect, ReflectFromReflect, TypeRegistry}; +/// # #[derive(Reflect, FromReflect, PartialEq, Eq, Debug)] +/// # #[reflect(FromReflect)] +/// # struct Foo(#[reflect(default = "default_value")] usize); +/// # fn default_value() -> usize { 123 } +/// # let mut registry = TypeRegistry::new(); +/// # registry.register::(); +/// +/// let mut reflected = DynamicTupleStruct::default(); +/// reflected.set_name(std::any::type_name::().to_string()); +/// +/// let registration = registry.get_with_name(reflected.type_name()).unwrap(); +/// let rfr = registration.data::().unwrap(); +/// +/// let concrete: Box = rfr.from_reflect(&reflected).unwrap(); +/// +/// assert_eq!(Foo(123), concrete.take::().unwrap()); +/// ``` +/// +/// [`DynamicStruct`]: crate::DynamicStruct +/// [`DynamicEnum`]: crate::DynamicEnum +#[derive(Clone)] +pub struct ReflectFromReflect { + from_reflect: fn(&dyn Reflect) -> Option>, +} + +impl ReflectFromReflect { + /// Perform a [`FromReflect::from_reflect`] conversion on the given reflection object. + /// + /// This will convert the object to a concrete type if it wasn't already, and return + /// the value as `Box`. + #[allow(clippy::wrong_self_convention)] + pub fn from_reflect(&self, reflect_value: &dyn Reflect) -> Option> { + (self.from_reflect)(reflect_value) + } +} + +impl FromType for ReflectFromReflect { + fn from_type() -> Self { + Self { + from_reflect: |reflect_value| { + T::from_reflect(reflect_value).map(|value| Box::new(value) as Box) + }, + } + } +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index a9e57d3fbfac0..bf20bc2ec9b1f 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -2,6 +2,7 @@ mod array; mod fields; +mod from_reflect; mod list; mod map; mod path; @@ -47,6 +48,7 @@ pub mod prelude { pub use array::*; pub use enums::*; pub use fields::*; +pub use from_reflect::*; pub use impls::*; pub use list::*; pub use map::*; @@ -103,6 +105,7 @@ mod tests { ser::{to_string_pretty, PrettyConfig}, Deserializer, }; + use std::any::TypeId; use std::fmt::{Debug, Formatter}; use super::prelude::*; @@ -244,6 +247,42 @@ mod tests { assert_eq!(values, vec![1]); } + #[test] + fn should_call_from_reflect_dynamically() { + #[derive(Reflect, FromReflect)] + #[reflect(FromReflect)] + struct MyStruct { + foo: usize, + } + + // Register + let mut registry = TypeRegistry::default(); + registry.register::(); + + // Get type data + let type_id = TypeId::of::(); + let rfr = registry + .get_type_data::(type_id) + .expect("the FromReflect trait should be registered"); + + // Call from_reflect + let mut dynamic_struct = DynamicStruct::default(); + dynamic_struct.insert("foo", 123usize); + let reflected = rfr + .from_reflect(&dynamic_struct) + .expect("the type should be properly reflected"); + + // Assert + let expected = MyStruct { foo: 123 }; + assert!(expected + .reflect_partial_eq(reflected.as_ref()) + .unwrap_or_default()); + let not_expected = MyStruct { foo: 321 }; + assert!(!not_expected + .reflect_partial_eq(reflected.as_ref()) + .unwrap_or_default()); + } + #[test] fn from_reflect_should_use_default_field_attributes() { #[derive(Reflect, FromReflect, Eq, PartialEq, Debug)] diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index fb8285fd9de0c..c29c525888d88 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -190,21 +190,6 @@ pub trait Reflect: Any + Send + Sync { } } -/// A trait for types which can be constructed from a reflected type. -/// -/// This trait can be derived on types which implement [`Reflect`]. Some complex -/// types (such as `Vec`) may only be reflected if their element types -/// implement this trait. -/// -/// For structs and tuple structs, fields marked with the `#[reflect(ignore)]` -/// attribute will be constructed using the `Default` implementation of the -/// field type, rather than the corresponding field value (if any) of the -/// reflected value. -pub trait FromReflect: Reflect + Sized { - /// Constructs a concrete instance of `Self` from a reflected value. - fn from_reflect(reflect: &dyn Reflect) -> Option; -} - impl Debug for dyn Reflect { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.debug(f) @@ -255,6 +240,8 @@ impl dyn Reflect { /// a different type, like the Dynamic\*\*\* types do, you can call `represents` /// to determine what type they represent. Represented types cannot be downcasted /// to, but you can use [`FromReflect`] to create a value of the represented type from them. + /// + /// [`FromReflect`]: crate::FromReflect #[inline] pub fn is(&self) -> bool { self.type_id() == TypeId::of::() From a9a3afd8665df36727be041213156eb0b22e13c0 Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Sun, 11 Dec 2022 08:34:09 -0700 Subject: [PATCH 2/2] Update crates/bevy_reflect/src/from_reflect.rs Co-authored-by: ira --- crates/bevy_reflect/src/from_reflect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/from_reflect.rs b/crates/bevy_reflect/src/from_reflect.rs index 9a7f09c8772ee..e9df08aefc459 100644 --- a/crates/bevy_reflect/src/from_reflect.rs +++ b/crates/bevy_reflect/src/from_reflect.rs @@ -81,7 +81,7 @@ impl ReflectFromReflect { } } -impl FromType for ReflectFromReflect { +impl FromType for ReflectFromReflect { fn from_type() -> Self { Self { from_reflect: |reflect_value| {