2021-12-18 22:59:55 +00:00
|
|
|
#![doc = include_str!("../README.md")]
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
mod list;
|
|
|
|
mod map;
|
|
|
|
mod path;
|
|
|
|
mod reflect;
|
|
|
|
mod struct_trait;
|
2021-01-08 03:50:09 +00:00
|
|
|
mod tuple;
|
2020-11-28 00:39:59 +00:00
|
|
|
mod tuple_struct;
|
|
|
|
mod type_registry;
|
|
|
|
mod type_uuid;
|
|
|
|
mod impls {
|
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
mod glam;
|
|
|
|
#[cfg(feature = "smallvec")]
|
|
|
|
mod smallvec;
|
|
|
|
mod std;
|
|
|
|
|
|
|
|
#[cfg(feature = "glam")]
|
|
|
|
pub use self::glam::*;
|
|
|
|
#[cfg(feature = "smallvec")]
|
|
|
|
pub use self::smallvec::*;
|
|
|
|
pub use self::std::*;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub mod serde;
|
|
|
|
pub mod prelude {
|
2021-04-27 18:29:33 +00:00
|
|
|
#[doc(hidden)]
|
2020-11-28 00:39:59 +00:00
|
|
|
pub use crate::{
|
|
|
|
reflect_trait, GetField, GetTupleStructField, Reflect, ReflectDeserialize, Struct,
|
|
|
|
TupleStruct,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub use impls::*;
|
|
|
|
pub use list::*;
|
|
|
|
pub use map::*;
|
|
|
|
pub use path::*;
|
|
|
|
pub use reflect::*;
|
|
|
|
pub use struct_trait::*;
|
2021-01-08 03:50:09 +00:00
|
|
|
pub use tuple::*;
|
2020-11-28 00:39:59 +00:00
|
|
|
pub use tuple_struct::*;
|
|
|
|
pub use type_registry::*;
|
|
|
|
pub use type_uuid::*;
|
|
|
|
|
|
|
|
pub use bevy_reflect_derive::*;
|
|
|
|
pub use erased_serde;
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2021-02-22 08:42:19 +00:00
|
|
|
#[allow(clippy::blacklisted_name, clippy::approx_constant)]
|
2020-11-28 00:39:59 +00:00
|
|
|
mod tests {
|
|
|
|
use ::serde::de::DeserializeSeed;
|
|
|
|
use bevy_utils::HashMap;
|
|
|
|
use ron::{
|
|
|
|
ser::{to_string_pretty, PrettyConfig},
|
|
|
|
Deserializer,
|
|
|
|
};
|
|
|
|
|
|
|
|
use super::*;
|
2021-05-19 19:03:36 +00:00
|
|
|
use crate as bevy_reflect;
|
|
|
|
use crate::serde::{ReflectDeserializer, ReflectSerializer};
|
2021-02-22 08:42:19 +00:00
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
#[test]
|
|
|
|
fn reflect_struct() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
b: f32,
|
|
|
|
c: Bar,
|
|
|
|
}
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut foo = Foo {
|
|
|
|
a: 42,
|
|
|
|
b: 3.14,
|
|
|
|
c: Bar { x: 1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
let a = *foo.get_field::<u32>("a").unwrap();
|
|
|
|
assert_eq!(a, 42);
|
|
|
|
|
|
|
|
*foo.get_field_mut::<u32>("a").unwrap() += 1;
|
|
|
|
assert_eq!(foo.a, 43);
|
|
|
|
|
|
|
|
let bar = foo.get_field::<Bar>("c").unwrap();
|
|
|
|
assert_eq!(bar.x, 1);
|
|
|
|
|
|
|
|
// nested retrieval
|
|
|
|
let c = foo.field("c").unwrap();
|
|
|
|
if let ReflectRef::Struct(value) = c.reflect_ref() {
|
|
|
|
assert_eq!(*value.get_field::<u32>("x").unwrap(), 1);
|
|
|
|
} else {
|
2020-12-02 19:31:16 +00:00
|
|
|
panic!("Expected a struct.");
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// patch Foo with a dynamic struct
|
|
|
|
let mut dynamic_struct = DynamicStruct::default();
|
|
|
|
dynamic_struct.insert("a", 123u32);
|
|
|
|
dynamic_struct.insert("should_be_ignored", 456);
|
|
|
|
|
|
|
|
foo.apply(&dynamic_struct);
|
|
|
|
assert_eq!(foo.a, 123);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_map() {
|
|
|
|
#[derive(Reflect, Hash)]
|
|
|
|
#[reflect(Hash)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
b: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
let key_a = Foo {
|
|
|
|
a: 1,
|
|
|
|
b: "k1".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let key_b = Foo {
|
|
|
|
a: 1,
|
|
|
|
b: "k1".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let key_c = Foo {
|
|
|
|
a: 3,
|
|
|
|
b: "k3".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(key_a, 10u32);
|
|
|
|
assert_eq!(10, *map.get(&key_b).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert!(map.get(&key_c).is_none());
|
|
|
|
*map.get_mut(&key_b).unwrap().downcast_mut::<u32>().unwrap() = 20;
|
|
|
|
assert_eq!(20, *map.get(&key_b).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-02-22 08:42:19 +00:00
|
|
|
#[allow(clippy::blacklisted_name)]
|
2020-11-28 00:39:59 +00:00
|
|
|
fn reflect_unit_struct() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo(u32, u64);
|
|
|
|
|
|
|
|
let mut foo = Foo(1, 2);
|
|
|
|
assert_eq!(1, *foo.get_field::<u32>(0).unwrap());
|
|
|
|
assert_eq!(2, *foo.get_field::<u64>(1).unwrap());
|
|
|
|
|
|
|
|
let mut patch = DynamicTupleStruct::default();
|
|
|
|
patch.insert(3u32);
|
|
|
|
patch.insert(4u64);
|
|
|
|
assert_eq!(3, *patch.field(0).unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert_eq!(4, *patch.field(1).unwrap().downcast_ref::<u64>().unwrap());
|
|
|
|
|
|
|
|
foo.apply(&patch);
|
|
|
|
assert_eq!(3, foo.0);
|
|
|
|
assert_eq!(4, foo.1);
|
|
|
|
|
|
|
|
let mut iter = patch.iter_fields();
|
|
|
|
assert_eq!(3, *iter.next().unwrap().downcast_ref::<u32>().unwrap());
|
|
|
|
assert_eq!(4, *iter.next().unwrap().downcast_ref::<u64>().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "the given key does not support hashing")]
|
|
|
|
fn reflect_map_no_hash() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let foo = Foo { a: 1 };
|
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(foo, 10u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_ignore() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let foo = Foo { a: 1, _b: 2 };
|
|
|
|
|
|
|
|
let values: Vec<u32> = foo
|
|
|
|
.iter_fields()
|
|
|
|
.map(|value| *value.downcast_ref::<u32>().unwrap())
|
|
|
|
.collect();
|
|
|
|
assert_eq!(values, vec![1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_complex_patch() {
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
c: Vec<isize>,
|
|
|
|
d: HashMap<usize, i8>,
|
|
|
|
e: Bar,
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (i32, Vec<isize>, Bar),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: Vec<(Baz, HashMap<usize, Bar>)>,
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Clone, Debug, FromReflect)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
|
|
|
|
struct Baz(String);
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 2);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let mut hash_map_baz = HashMap::default();
|
|
|
|
hash_map_baz.insert(1, Bar { x: 0 });
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let mut foo = Foo {
|
|
|
|
a: 1,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![1, 2],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 1 },
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (1, vec![1, 2], Bar { x: 1 }),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: vec![(Baz("string".to_string()), hash_map_baz)],
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut foo_patch = DynamicStruct::default();
|
|
|
|
foo_patch.insert("a", 2u32);
|
|
|
|
foo_patch.insert("b", 2u32); // this should be ignored
|
|
|
|
|
|
|
|
let mut list = DynamicList::default();
|
|
|
|
list.push(3isize);
|
|
|
|
list.push(4isize);
|
|
|
|
list.push(5isize);
|
2021-01-08 03:50:09 +00:00
|
|
|
foo_patch.insert("c", list.clone_dynamic());
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
let mut map = DynamicMap::default();
|
|
|
|
map.insert(2usize, 3i8);
|
|
|
|
foo_patch.insert("d", map);
|
|
|
|
|
|
|
|
let mut bar_patch = DynamicStruct::default();
|
|
|
|
bar_patch.insert("x", 2u32);
|
2021-01-08 03:50:09 +00:00
|
|
|
foo_patch.insert("e", bar_patch.clone_dynamic());
|
|
|
|
|
|
|
|
let mut tuple = DynamicTuple::default();
|
|
|
|
tuple.insert(2i32);
|
|
|
|
tuple.insert(list);
|
|
|
|
tuple.insert(bar_patch);
|
|
|
|
foo_patch.insert("f", tuple);
|
2020-11-28 00:39:59 +00:00
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
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);
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
foo.apply(&foo_patch);
|
|
|
|
|
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 3);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
let mut hash_map_baz = HashMap::default();
|
|
|
|
hash_map_baz.insert(1, Bar { x: 7 });
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
let expected_foo = Foo {
|
|
|
|
a: 2,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![3, 4, 5],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 2 },
|
2021-01-08 03:50:09 +00:00
|
|
|
f: (2, vec![3, 4, 5], Bar { x: 2 }),
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
g: vec![(Baz("new_string".to_string()), hash_map_baz.clone())],
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(foo, expected_foo);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
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);
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_serialize() {
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Foo {
|
|
|
|
a: u32,
|
|
|
|
#[reflect(ignore)]
|
|
|
|
_b: u32,
|
|
|
|
c: Vec<isize>,
|
|
|
|
d: HashMap<usize, i8>,
|
|
|
|
e: Bar,
|
|
|
|
f: String,
|
2021-01-08 03:50:09 +00:00
|
|
|
g: (i32, Vec<isize>, Bar),
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut hash_map = HashMap::default();
|
|
|
|
hash_map.insert(1, 1);
|
|
|
|
hash_map.insert(2, 2);
|
|
|
|
let foo = Foo {
|
|
|
|
a: 1,
|
|
|
|
_b: 1,
|
|
|
|
c: vec![1, 2],
|
|
|
|
d: hash_map,
|
|
|
|
e: Bar { x: 1 },
|
|
|
|
f: "hi".to_string(),
|
2021-01-08 03:50:09 +00:00
|
|
|
g: (1, vec![1, 2], Bar { x: 1 }),
|
2020-11-28 00:39:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
|
|
registry.register::<u32>();
|
|
|
|
registry.register::<isize>();
|
|
|
|
registry.register::<usize>();
|
|
|
|
registry.register::<Bar>();
|
|
|
|
registry.register::<String>();
|
|
|
|
registry.register::<i8>();
|
2021-01-08 03:50:09 +00:00
|
|
|
registry.register::<i32>();
|
2020-11-28 00:39:59 +00:00
|
|
|
|
|
|
|
let serializer = ReflectSerializer::new(&foo, ®istry);
|
|
|
|
let serialized = to_string_pretty(&serializer, PrettyConfig::default()).unwrap();
|
|
|
|
|
|
|
|
let mut deserializer = Deserializer::from_str(&serialized).unwrap();
|
|
|
|
let reflect_deserializer = ReflectDeserializer::new(®istry);
|
|
|
|
let value = reflect_deserializer.deserialize(&mut deserializer).unwrap();
|
|
|
|
let dynamic_struct = value.take::<DynamicStruct>().unwrap();
|
|
|
|
|
2020-12-01 19:15:07 +00:00
|
|
|
assert!(foo.reflect_partial_eq(&dynamic_struct).unwrap());
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reflect_take() {
|
|
|
|
#[derive(Reflect, Debug, PartialEq)]
|
Reflection cleanup (#1536)
This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed.
Supersedes #1533 and resolves #1528.
---
I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here):
- Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive.
- Same as above for `struct` and `#[reflect(...)]`, respectively.
- If a `struct` is used as a component, it should also have `#[reflect(Component)]`
- All reflected types should be registered in their plugins
I treated the following as components (added `#[reflect(Component)]` if necessary):
- `bevy_render`
- `struct RenderLayers`
- `bevy_transform`
- `struct GlobalTransform`
- `struct Parent`
- `struct Transform`
- `bevy_ui`
- `struct Style`
Not treated as components:
- `bevy_math`
- `struct Size<T>`
- `struct Rect<T>`
- Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin.
- `bevy_render`
- `struct Color`
- `struct PipelineSpecialization`
- `struct ShaderSpecialization`
- `enum PrimitiveTopology`
- `enum IndexFormat`
Not Addressed:
- I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR.
- I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
|
|
|
#[reflect(PartialEq)]
|
2020-11-28 00:39:59 +00:00
|
|
|
struct Bar {
|
|
|
|
x: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
let x: Box<dyn Reflect> = Box::new(Bar { x: 2 });
|
|
|
|
let y = x.take::<Bar>().unwrap();
|
|
|
|
assert_eq!(y, Bar { x: 2 });
|
|
|
|
}
|
2021-02-02 21:57:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn dynamic_names() {
|
|
|
|
let list = Vec::<usize>::new();
|
|
|
|
let dyn_list = list.clone_dynamic();
|
|
|
|
assert_eq!(dyn_list.type_name(), std::any::type_name::<Vec<usize>>());
|
|
|
|
|
|
|
|
let map = HashMap::<usize, String>::default();
|
|
|
|
let dyn_map = map.clone_dynamic();
|
|
|
|
assert_eq!(
|
|
|
|
dyn_map.type_name(),
|
|
|
|
std::any::type_name::<HashMap<usize, String>>()
|
|
|
|
);
|
|
|
|
|
|
|
|
let tuple = (0usize, "1".to_string(), 2.0f32);
|
|
|
|
let mut dyn_tuple = tuple.clone_dynamic();
|
|
|
|
dyn_tuple.insert::<usize>(3);
|
|
|
|
assert_eq!(
|
|
|
|
dyn_tuple.type_name(),
|
|
|
|
std::any::type_name::<(usize, String, f32, usize)>()
|
|
|
|
);
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestStruct {
|
|
|
|
a: usize,
|
|
|
|
}
|
|
|
|
let struct_ = TestStruct { a: 0 };
|
|
|
|
let dyn_struct = struct_.clone_dynamic();
|
|
|
|
assert_eq!(dyn_struct.type_name(), std::any::type_name::<TestStruct>());
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestTupleStruct(usize);
|
|
|
|
let tuple_struct = TestTupleStruct(0);
|
|
|
|
let dyn_tuple_struct = tuple_struct.clone_dynamic();
|
|
|
|
assert_eq!(
|
|
|
|
dyn_tuple_struct.type_name(),
|
|
|
|
std::any::type_name::<TestTupleStruct>()
|
|
|
|
);
|
|
|
|
}
|
2022-04-25 13:54:48 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn as_reflect() {
|
|
|
|
trait TestTrait: Reflect {}
|
|
|
|
|
|
|
|
#[derive(Reflect)]
|
|
|
|
struct TestStruct;
|
|
|
|
|
|
|
|
impl TestTrait for TestStruct {}
|
|
|
|
|
|
|
|
let trait_object: Box<dyn TestTrait> = Box::new(TestStruct);
|
|
|
|
|
|
|
|
// Should compile:
|
|
|
|
let _ = trait_object.as_reflect();
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|