Skip to content

Commit 6292e07

Browse files
committed
Added ReflectFromReflect
1 parent 8c6bbfc commit 6292e07

File tree

6 files changed

+151
-37
lines changed

6 files changed

+151
-37
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

+2-13
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,7 @@ impl<'a> ReflectMeta<'a> {
337337

338338
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
339339
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
340-
crate::registration::impl_get_type_registration(
341-
self.type_name,
342-
&self.bevy_reflect_path,
343-
self.traits.idents(),
344-
self.generics,
345-
None,
346-
)
340+
crate::registration::impl_get_type_registration(self, None)
347341
}
348342

349343
/// The collection of docstrings for this type, if any.
@@ -371,13 +365,8 @@ impl<'a> ReflectStruct<'a> {
371365
///
372366
/// Returns a specific implementation for structs and this method should be preffered over the generic [`get_type_registration`](crate::ReflectMeta) method
373367
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
374-
let reflect_path = self.meta.bevy_reflect_path();
375-
376368
crate::registration::impl_get_type_registration(
377-
self.meta.type_name(),
378-
reflect_path,
379-
self.meta.traits().idents(),
380-
self.meta.generics(),
369+
self.meta(),
381370
Some(&self.serialization_denylist),
382371
)
383372
}

crates/bevy_reflect/bevy_reflect_derive/src/registration.rs

+16-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
//! Contains code related specifically to Bevy's type registration.
22
3+
use crate::ReflectMeta;
34
use bit_set::BitSet;
4-
use proc_macro2::Ident;
55
use quote::quote;
6-
use syn::{Generics, Path};
76

87
/// Creates the `GetTypeRegistration` impl for the given type data.
98
pub(crate) fn impl_get_type_registration(
10-
type_name: &Ident,
11-
bevy_reflect_path: &Path,
12-
registration_data: &[Ident],
13-
generics: &Generics,
9+
meta: &ReflectMeta,
1410
serialization_denylist: Option<&BitSet<u32>>,
1511
) -> proc_macro2::TokenStream {
16-
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
12+
let type_name = meta.type_name();
13+
let bevy_reflect_path = meta.bevy_reflect_path();
14+
let registration_data = meta.traits().idents();
15+
let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl();
16+
17+
let from_reflect_data = if meta.from_reflect().should_auto_derive() {
18+
Some(quote! {
19+
registration.insert::<#bevy_reflect_path::ReflectFromReflect>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());
20+
})
21+
} else {
22+
None
23+
};
24+
1725
let serialization_data = serialization_denylist.map(|denylist| {
1826
let denylist = denylist.into_iter();
1927
quote! {
@@ -28,6 +36,7 @@ pub(crate) fn impl_get_type_registration(
2836
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
2937
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
3038
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());
39+
#from_reflect_data
3140
#serialization_data
3241
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
3342
registration
+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use crate::{FromType, Reflect};
2+
3+
/// A trait for types which can be constructed from a reflected type.
4+
///
5+
/// This trait can be derived on types which implement [`Reflect`]. Some complex
6+
/// types (such as `Vec<T>`) may only be reflected if their element types
7+
/// implement this trait.
8+
///
9+
/// For structs and tuple structs, fields marked with the `#[reflect(ignore)]`
10+
/// attribute will be constructed using the `Default` implementation of the
11+
/// field type, rather than the corresponding field value (if any) of the
12+
/// reflected value.
13+
pub trait FromReflect: Reflect + Sized {
14+
/// Constructs a concrete instance of `Self` from a reflected value.
15+
fn from_reflect(reflect: &dyn Reflect) -> Option<Self>;
16+
}
17+
18+
/// Type data that represents the [`FromReflect`] trait and allows it to be used dynamically.
19+
///
20+
/// `FromReflect` allows dynamic types (e.g. [`DynamicStruct`], [`DynamicEnum`], etc.) to be converted
21+
/// to their full, concrete types. This is most important when it comes to deserialization where it isn't
22+
/// guaranteed that every field exists when trying to construct the final output.
23+
///
24+
/// However, to do this, you normally need to specify the exact concrete type:
25+
///
26+
/// ```
27+
/// # use bevy_reflect::{DynamicTupleStruct, FromReflect, Reflect};
28+
/// #[derive(Reflect, PartialEq, Eq, Debug)]
29+
/// struct Foo(#[reflect(default = "default_value")] usize);
30+
///
31+
/// fn default_value() -> usize { 123 }
32+
///
33+
/// let reflected = DynamicTupleStruct::default();
34+
///
35+
/// let concrete: Foo = <Foo as FromReflect>::from_reflect(&reflected).unwrap();
36+
///
37+
/// assert_eq!(Foo(123), concrete);
38+
/// ```
39+
///
40+
/// In a dynamic context where the type might not be known at compile-time, this is nearly impossible to do.
41+
/// That is why this type data struct exists— it allows us to construct the full type without knowing
42+
/// what the actual type is.
43+
///
44+
/// # Example
45+
///
46+
/// ```
47+
/// # use bevy_reflect::{DynamicTupleStruct, FromReflect, Reflect, ReflectFromReflect, TypeRegistry};
48+
/// # #[derive(Reflect, PartialEq, Eq, Debug)]
49+
/// # struct Foo(#[reflect(default = "default_value")] usize);
50+
/// # fn default_value() -> usize { 123 }
51+
/// # let mut registry = TypeRegistry::new();
52+
/// # registry.register::<Foo>();
53+
///
54+
/// let mut reflected = DynamicTupleStruct::default();
55+
/// reflected.set_name(std::any::type_name::<Foo>().to_string());
56+
///
57+
/// let registration = registry.get_with_name(reflected.type_name()).unwrap();
58+
/// let rfr = registration.data::<ReflectFromReflect>().unwrap();
59+
///
60+
/// let concrete: Box<dyn Reflect> = rfr.from_reflect(&reflected).unwrap();
61+
///
62+
/// assert_eq!(Foo(123), concrete.take::<Foo>().unwrap());
63+
/// ```
64+
///
65+
/// [`DynamicStruct`]: crate::DynamicStruct
66+
/// [`DynamicEnum`]: crate::DynamicEnum
67+
#[derive(Clone)]
68+
pub struct ReflectFromReflect {
69+
from_reflect: fn(&dyn Reflect) -> Option<Box<dyn Reflect>>,
70+
}
71+
72+
impl ReflectFromReflect {
73+
/// Perform a [`FromReflect::from_reflect`] conversion on the given reflection object.
74+
///
75+
/// This will convert the object to a concrete type if it wasn't already, and return
76+
/// the value as `Box<dyn Reflect>`.
77+
#[allow(clippy::wrong_self_convention)]
78+
pub fn from_reflect(&self, reflect_value: &dyn Reflect) -> Option<Box<dyn Reflect>> {
79+
(self.from_reflect)(reflect_value)
80+
}
81+
}
82+
83+
impl<T: FromReflect + Reflect> FromType<T> for ReflectFromReflect {
84+
fn from_type() -> Self {
85+
Self {
86+
from_reflect: |reflect_value| {
87+
T::from_reflect(reflect_value).map(|value| Box::new(value) as Box<dyn Reflect>)
88+
},
89+
}
90+
}
91+
}

crates/bevy_reflect/src/lib.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
mod array;
44
mod fields;
5+
mod from_reflect;
56
mod list;
67
mod map;
78
mod path;
@@ -47,6 +48,7 @@ pub mod prelude {
4748
pub use array::*;
4849
pub use enums::*;
4950
pub use fields::*;
51+
pub use from_reflect::*;
5052
pub use impls::*;
5153
pub use list::*;
5254
pub use map::*;
@@ -103,6 +105,7 @@ mod tests {
103105
ser::{to_string_pretty, PrettyConfig},
104106
Deserializer,
105107
};
108+
use std::any::TypeId;
106109
use std::fmt::{Debug, Formatter};
107110

108111
use super::prelude::*;
@@ -244,6 +247,41 @@ mod tests {
244247
assert_eq!(values, vec![1]);
245248
}
246249

250+
#[test]
251+
fn should_reflect_from_type_id() {
252+
#[derive(Reflect)]
253+
struct MyStruct {
254+
foo: usize,
255+
}
256+
257+
// Register
258+
let mut registry = TypeRegistry::default();
259+
registry.register::<MyStruct>();
260+
261+
// Get type data
262+
let type_id = TypeId::of::<MyStruct>();
263+
let rfr = registry
264+
.get_type_data::<ReflectFromReflect>(type_id)
265+
.expect("the FromReflect trait should be registered");
266+
267+
// Call from_reflect
268+
let mut dynamic_struct = DynamicStruct::default();
269+
dynamic_struct.insert("foo", 123usize);
270+
let reflected = rfr
271+
.from_reflect(&dynamic_struct)
272+
.expect("the type should be properly reflected");
273+
274+
// Assert
275+
let expected = MyStruct { foo: 123 };
276+
assert!(expected
277+
.reflect_partial_eq(reflected.as_ref())
278+
.unwrap_or_default());
279+
let not_expected = MyStruct { foo: 321 };
280+
assert!(!not_expected
281+
.reflect_partial_eq(reflected.as_ref())
282+
.unwrap_or_default());
283+
}
284+
247285
#[test]
248286
fn from_reflect_should_use_default_field_attributes() {
249287
#[derive(Reflect, Eq, PartialEq, Debug)]
@@ -661,7 +699,7 @@ mod tests {
661699

662700
// Struct (generic)
663701
#[derive(Reflect)]
664-
struct MyGenericStruct<T: Reflect> {
702+
struct MyGenericStruct<T: FromReflect> {
665703
foo: T,
666704
bar: usize,
667705
}

crates/bevy_reflect/src/reflect.rs

+2-15
Original file line numberDiff line numberDiff line change
@@ -215,21 +215,6 @@ pub trait Reflect: Any + Send + Sync {
215215
}
216216
}
217217

218-
/// A trait for types which can be constructed from a reflected type.
219-
///
220-
/// This trait can be derived on types which implement [`Reflect`]. Some complex
221-
/// types (such as `Vec<T>`) may only be reflected if their element types
222-
/// implement this trait.
223-
///
224-
/// For structs and tuple structs, fields marked with the `#[reflect(ignore)]`
225-
/// attribute will be constructed using the `Default` implementation of the
226-
/// field type, rather than the corresponding field value (if any) of the
227-
/// reflected value.
228-
pub trait FromReflect: Reflect + Sized {
229-
/// Constructs a concrete instance of `Self` from a reflected value.
230-
fn from_reflect(reflect: &dyn Reflect) -> Option<Self>;
231-
}
232-
233218
impl Debug for dyn Reflect {
234219
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235220
self.debug(f)
@@ -280,6 +265,8 @@ impl dyn Reflect {
280265
/// a different type, like the Dynamic\*\*\* types do, you can call `represents`
281266
/// to determine what type they represent. Represented types cannot be downcasted
282267
/// to, but you can use [`FromReflect`] to create a value of the represented type from them.
268+
///
269+
/// [`FromReflect`]: crate::FromReflect
283270
#[inline]
284271
pub fn is<T: Reflect>(&self) -> bool {
285272
self.type_id() == TypeId::of::<T>()

examples/reflection/generic_reflection.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn main() {
1313
}
1414

1515
#[derive(Reflect)]
16-
struct MyType<T: Reflect> {
16+
struct MyType<T: FromReflect> {
1717
value: T,
1818
}
1919

0 commit comments

Comments
 (0)