Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bevy_reflect: Add ReflectFromReflect #4147

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/bevy_asset/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
Asset, Assets,
};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize, ReflectFromReflect};
use bevy_utils::Uuid;
use crossbeam_channel::{Receiver, Sender};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -95,7 +95,7 @@ impl HandleId {
/// collisions no longer being detected for that entity.
///
#[derive(Component, Reflect, FromReflect)]
#[reflect(Component)]
#[reflect(Component, FromReflect)]
pub struct Handle<T>
where
T: Asset,
Expand Down
37 changes: 37 additions & 0 deletions crates/bevy_reflect/src/from_reflect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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<T>`) 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<Self>;
}

#[derive(Clone)]
pub struct ReflectFromReflect {
from_reflect: fn(&dyn Reflect) -> Option<Box<dyn Reflect>>,
}

impl ReflectFromReflect {
pub fn from_reflect(&self, reflect_value: &dyn Reflect) -> Option<Box<dyn Reflect>> {
(self.from_reflect)(reflect_value)
}
}

impl<T: FromReflect + Reflect> FromType<T> for ReflectFromReflect {
fn from_type() -> Self {
Self {
from_reflect: |reflect_value| {
T::from_reflect(reflect_value).map(|value| Box::new(value) as Box<dyn Reflect>)
},
}
}
}
40 changes: 20 additions & 20 deletions crates/bevy_reflect/src/impls/glam.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
use crate as bevy_reflect;
use crate::ReflectDeserialize;
use crate::{ReflectDeserialize, ReflectFromReflect};
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
use glam::*;

impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize));
impl_reflect_value!(IVec3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(IVec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(UVec2(PartialEq, Serialize, Deserialize));
impl_reflect_value!(UVec3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(UVec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Vec2(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Vec3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Vec3A(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DVec2(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DVec3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DVec4(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_reflect_value!(DMat3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DMat4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DQuat(PartialEq, Serialize, Deserialize));
impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(IVec3(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(IVec4(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(UVec2(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(UVec3(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(UVec4(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(Vec2(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(Vec3(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(Vec3A(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(DVec2(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(DVec3(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(DVec4(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(DMat3(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(DMat4(PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(DQuat(PartialEq, Serialize, Deserialize, FromReflect));

impl_from_reflect_value!(IVec2);
impl_from_reflect_value!(IVec3);
Expand Down
52 changes: 30 additions & 22 deletions crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate as bevy_reflect;
use crate::{
map_partial_eq, serde::Serializable, DynamicMap, FromReflect, FromType, GetTypeRegistration,
List, ListIter, Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef,
TypeRegistration,
List, ListIter, Map, MapIter, Reflect, ReflectDeserialize, ReflectFromReflect, ReflectMut,
ReflectRef, TypeRegistration,
};

use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
Expand All @@ -15,26 +15,32 @@ use std::{
ops::Range,
};

impl_reflect_value!(bool(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(u8(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(u16(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(u32(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(u64(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(u128(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(usize(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(i8(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(i16(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(i32(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(i64(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(i128(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(isize(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(f32(Serialize, Deserialize));
impl_reflect_value!(f64(Serialize, Deserialize));
impl_reflect_value!(String(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(Option<T: Serialize + Clone + for<'de> Deserialize<'de> + Reflect + 'static>(Serialize, Deserialize));
impl_reflect_value!(HashSet<T: Serialize + Hash + Eq + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize));
impl_reflect_value!(Range<T: Serialize + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize));
impl_reflect_value!(Duration(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(bool(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(u8(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(u16(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(u32(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(u64(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(u128(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(usize(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(i8(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(i16(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(i32(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(i64(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(i128(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(isize(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(f32(Serialize, Deserialize, FromReflect));
impl_reflect_value!(f64(Serialize, Deserialize, FromReflect));
impl_reflect_value!(String(Hash, PartialEq, Serialize, Deserialize, FromReflect));
impl_reflect_value!(Option<T: Serialize + Clone + for<'de> Deserialize<'de> + Reflect + 'static>(Serialize, Deserialize, FromReflect));
impl_reflect_value!(HashSet<T: Serialize + Hash + Eq + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize, FromReflect));
impl_reflect_value!(Range<T: Serialize + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize, FromReflect));
impl_reflect_value!(Duration(
Hash,
PartialEq,
Serialize,
Deserialize,
FromReflect
));

impl_from_reflect_value!(bool);
impl_from_reflect_value!(u8);
Expand Down Expand Up @@ -148,6 +154,7 @@ impl<T: FromReflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T>
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Vec<T>>();
registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type());
registration.insert::<ReflectFromReflect>(FromType::<Vec<T>>::from_type());
registration
}
}
Expand Down Expand Up @@ -355,6 +362,7 @@ impl GetTypeRegistration for Cow<'static, str> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Cow<'static, str>>();
registration.insert::<ReflectDeserialize>(FromType::<Cow<'static, str>>::from_type());
registration.insert::<ReflectFromReflect>(FromType::<Cow<'static, str>>::from_type());
registration
}
}
Expand Down
113 changes: 113 additions & 0 deletions crates/bevy_reflect/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")]

mod from_reflect;
mod list;
mod map;
mod path;
Expand All @@ -24,6 +25,7 @@ mod impls {
}

pub mod serde;

pub mod prelude {
#[doc(hidden)]
pub use crate::{
Expand All @@ -32,6 +34,7 @@ pub mod prelude {
};
}

pub use from_reflect::*;
pub use impls::*;
pub use list::*;
pub use map::*;
Expand All @@ -55,6 +58,7 @@ mod tests {
ser::{to_string_pretty, PrettyConfig},
Deserializer,
};
use std::any::TypeId;

use super::*;
use crate as bevy_reflect;
Expand Down Expand Up @@ -423,4 +427,113 @@ mod tests {
std::any::type_name::<TestTupleStruct>()
);
}

#[test]
fn should_reflect_from_type_id() {
#[derive(Reflect, FromReflect)]
#[reflect(FromReflect, MyTrait)]
struct MyStruct {
foo: usize,
}

#[reflect_trait]
trait MyTrait {
fn foo(&self) -> usize;
}

impl MyTrait for MyStruct {
fn foo(&self) -> usize {
self.foo
}
}

// === Struct === //
// Register
let mut registry = TypeRegistry::default();
registry.register::<MyStruct>();

// Get type data
let type_id = TypeId::of::<MyStruct>();
let rfr = registry
.get_type_data::<ReflectFromReflect>(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 unexpected = MyStruct { foo: 321 };
assert!(!unexpected
.reflect_partial_eq(reflected.as_ref())
.unwrap_or_default());

// --- Traits --- //
let my_trait = registry
.get_type_data::<ReflectMyTrait>(type_id)
.expect("the trait should be registered");
let trait_obj = my_trait
.get(reflected.as_ref())
.expect("the reflected value should be convertable to trait object");

assert_eq!(expected.foo, trait_obj.foo());

// === Vec === //
// Register
registry.register::<Vec<usize>>();

// Get type data
let type_id = TypeId::of::<Vec<usize>>();
let rfr = registry
.get_type_data::<ReflectFromReflect>(type_id)
.expect("the FromReflect trait should be registered");

// Call from_reflect
let mut dynamic_list = DynamicList::default();
dynamic_list.push(1usize);
dynamic_list.push(2usize);
dynamic_list.push(3usize);
let reflected = rfr
.from_reflect(&dynamic_list)
.expect("the type should be properly reflected");

// Assert
let expected = vec![1usize, 2usize, 3usize];
assert!(expected
.reflect_partial_eq(reflected.as_ref())
.unwrap_or_default());
let unexpected = vec![1usize, 2usize, 3usize, 4usize];
assert!(!unexpected
.reflect_partial_eq(reflected.as_ref())
.unwrap_or_default());

// === Value === //
// Register
registry.register::<i32>();

// Get type data
let type_id = TypeId::of::<i32>();
let rfr = registry
.get_type_data::<ReflectFromReflect>(type_id)
.expect("the FromReflect trait should be registered");

// Call from_reflect
let dynamic_value: i32 = 123;
let reflected = rfr
.from_reflect(&dynamic_value)
.expect("the type should be properly reflected");

// Assert
let expected: i32 = 123;
assert!(expected
.reflect_partial_eq(reflected.as_ref())
.unwrap_or_default());
}
}
15 changes: 0 additions & 15 deletions crates/bevy_reflect/src/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,21 +135,6 @@ pub unsafe trait Reflect: Any + Send + Sync {
fn serializable(&self) -> Option<Serializable>;
}

/// 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<T>`) 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<Self>;
}

impl Debug for dyn Reflect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Reflect({})", self.type_name())
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_render/src/color/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ pub use colorspace::*;

use crate::color::{HslRepresentation, SrgbColorSpace};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize, ReflectFromReflect};
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Mul, MulAssign};

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect, FromReflect)]
#[reflect(PartialEq, Serialize, Deserialize)]
#[reflect(PartialEq, Serialize, Deserialize, FromReflect)]
pub enum Color {
/// sRGBA color
Rgba {
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_text/src/text.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bevy_asset::Handle;
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize, ReflectFromReflect};
use bevy_render::color::Color;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -65,6 +65,7 @@ impl Text {
}

#[derive(Debug, Default, Clone, FromReflect, Reflect)]
#[reflect(FromReflect)]
pub struct TextSection {
pub value: String,
pub style: TextStyle,
Expand Down Expand Up @@ -134,6 +135,7 @@ impl From<VerticalAlign> for glyph_brush_layout::VerticalAlign {
}

#[derive(Clone, Debug, Reflect, FromReflect)]
#[reflect(FromReflect)]
pub struct TextStyle {
pub font: Handle<Font>,
pub font_size: f32,
Expand Down