mirror of
https://github.com/bevyengine/bevy
synced 2025-01-07 18:58:58 +00:00
2f5591ff8c
# Objective Debugging reflected types can be somewhat frustrating since all `dyn Reflect` trait objects return something like `Reflect(core::option::Option<alloc::string::String>)`. It would be much nicer to be able to see the actual value— or even use a custom `Debug` implementation. ## Solution Added `Reflect::debug` which allows users to customize the debug output. It sets defaults for all `ReflectRef` subtraits and falls back to `Reflect(type_name)` if no `Debug` implementation was registered. To register a custom `Debug` impl, users can add `#[reflect(Debug)]` like they can with other traits. ### Example Using the following structs: ```rust #[derive(Reflect)] pub struct Foo { a: usize, nested: Bar, #[reflect(ignore)] _ignored: NonReflectedValue, } #[derive(Reflect)] pub struct Bar { value: Vec2, tuple_value: (i32, String), list_value: Vec<usize>, // We can't determine debug formatting for Option<T> yet unknown_value: Option<String>, custom_debug: CustomDebug } #[derive(Reflect)] #[reflect(Debug)] struct CustomDebug; impl Debug for CustomDebug { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "This is a custom debug!") } } pub struct NonReflectedValue { _a: usize, } ``` We can do: ```rust let value = Foo { a: 1, _ignored: NonReflectedValue { _a: 10 }, nested: Bar { value: Vec2::new(1.23, 3.21), tuple_value: (123, String::from("Hello")), list_value: vec![1, 2, 3], unknown_value: Some(String::from("World")), custom_debug: CustomDebug }, }; let reflected_value: &dyn Reflect = &value; println!("{:#?}", reflected_value) ``` Which results in: ```rust Foo { a: 2, nested: Bar { value: Vec2( 1.23, 3.21, ), tuple_value: ( 123, "Hello", ), list_value: [ 1, 2, 3, ], unknown_value: Reflect(core::option::Option<alloc::string::String>), custom_debug: This is a custom debug!, }, } ``` Notice that neither `Foo` nor `Bar` implement `Debug`, yet we can still deduce it. This might be a concern if we're worried about leaking internal values. If it is, we might want to consider a way to exclude fields (possibly with a `#[reflect(hide)]` macro) or make it purely opt in (as opposed to the default implementation automatically handled by ReflectRef subtraits). Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
662 lines
18 KiB
Rust
662 lines
18 KiB
Rust
#![doc = include_str!("../README.md")]
|
|
|
|
mod array;
|
|
mod list;
|
|
mod map;
|
|
mod path;
|
|
mod reflect;
|
|
mod struct_trait;
|
|
mod tuple;
|
|
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 std_traits;
|
|
|
|
pub mod prelude {
|
|
pub use crate::std_traits::*;
|
|
#[doc(hidden)]
|
|
pub use crate::{
|
|
reflect_trait, GetField, GetTupleStructField, Reflect, ReflectDeserialize, Struct,
|
|
TupleStruct,
|
|
};
|
|
}
|
|
|
|
pub use array::*;
|
|
pub use impls::*;
|
|
pub use list::*;
|
|
pub use map::*;
|
|
pub use path::*;
|
|
pub use reflect::*;
|
|
pub use struct_trait::*;
|
|
pub use tuple::*;
|
|
pub use tuple_struct::*;
|
|
pub use type_registry::*;
|
|
pub use type_uuid::*;
|
|
|
|
pub use bevy_reflect_derive::*;
|
|
pub use erased_serde;
|
|
|
|
#[doc(hidden)]
|
|
pub mod __macro_exports {
|
|
use crate::Uuid;
|
|
|
|
/// Generates a new UUID from the given UUIDs `a` and `b`,
|
|
/// where the bytes are generated by a bitwise `a ^ b.rotate_right(1)`.
|
|
/// The generated UUID will be a `UUIDv4` (meaning that the bytes should be random, not e.g. derived from the system time).
|
|
#[allow(clippy::unusual_byte_groupings)] // unusual byte grouping is meant to signal the relevant bits
|
|
pub const fn generate_composite_uuid(a: Uuid, b: Uuid) -> Uuid {
|
|
let mut new = [0; 16];
|
|
let mut i = 0;
|
|
while i < new.len() {
|
|
// rotating ensures different uuids for A<B<C>> and B<A<C>> because: A ^ (B ^ C) = B ^ (A ^ C)
|
|
// notice that you have to rotate the second parameter: A.rr ^ (B.rr ^ C) = B.rr ^ (A.rr ^ C)
|
|
// Solution: A ^ (B ^ C.rr).rr != B ^ (A ^ C.rr).rr
|
|
new[i] = a.as_bytes()[i] ^ b.as_bytes()[i].rotate_right(1);
|
|
|
|
i += 1;
|
|
}
|
|
|
|
// Version: the most significant 4 bits in the 6th byte: 11110000
|
|
new[6] = new[6] & 0b0000_1111 | 0b0100_0000; // set version to v4
|
|
|
|
// Variant: the most significant 3 bits in the 8th byte: 11100000
|
|
new[8] = new[8] & 0b000_11111 | 0b100_00000; // set variant to rfc4122
|
|
|
|
Uuid::from_bytes(new)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[allow(clippy::blacklisted_name, clippy::approx_constant)]
|
|
mod tests {
|
|
#[cfg(feature = "glam")]
|
|
use ::glam::{vec3, Vec3};
|
|
use ::serde::de::DeserializeSeed;
|
|
use bevy_utils::HashMap;
|
|
use ron::{
|
|
ser::{to_string_pretty, PrettyConfig},
|
|
Deserializer,
|
|
};
|
|
use std::fmt::{Debug, Formatter};
|
|
|
|
use super::*;
|
|
use crate as bevy_reflect;
|
|
use crate::serde::{ReflectDeserializer, ReflectSerializer};
|
|
|
|
#[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 {
|
|
panic!("Expected a struct.");
|
|
}
|
|
|
|
// 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]
|
|
#[allow(clippy::blacklisted_name)]
|
|
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() {
|
|
#[derive(Reflect, Eq, PartialEq, Debug, FromReflect)]
|
|
#[reflect(PartialEq)]
|
|
struct Foo {
|
|
a: u32,
|
|
#[reflect(ignore)]
|
|
_b: u32,
|
|
c: Vec<isize>,
|
|
d: HashMap<usize, i8>,
|
|
e: Bar,
|
|
f: (i32, Vec<isize>, Bar),
|
|
g: Vec<(Baz, HashMap<usize, Bar>)>,
|
|
h: [u32; 2],
|
|
}
|
|
|
|
#[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,
|
|
c: vec![1, 2],
|
|
d: hash_map,
|
|
e: Bar { x: 1 },
|
|
f: (1, vec![1, 2], Bar { x: 1 }),
|
|
g: vec![(Baz("string".to_string()), hash_map_baz)],
|
|
h: [2; 2],
|
|
};
|
|
|
|
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);
|
|
foo_patch.insert("c", List::clone_dynamic(&list));
|
|
|
|
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);
|
|
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);
|
|
|
|
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);
|
|
|
|
let array = DynamicArray::from_vec(vec![2u32, 2u32]);
|
|
foo_patch.insert("h", array);
|
|
|
|
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,
|
|
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.clone())],
|
|
h: [2; 2],
|
|
};
|
|
|
|
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)],
|
|
h: [2; 2],
|
|
};
|
|
|
|
assert_eq!(new_foo, expected_new_foo);
|
|
}
|
|
|
|
#[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,
|
|
g: (i32, Vec<isize>, Bar),
|
|
h: [u32; 2],
|
|
}
|
|
|
|
#[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(),
|
|
g: (1, vec![1, 2], Bar { x: 1 }),
|
|
h: [2; 2],
|
|
};
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
registry.register::<u32>();
|
|
registry.register::<isize>();
|
|
registry.register::<usize>();
|
|
registry.register::<Bar>();
|
|
registry.register::<String>();
|
|
registry.register::<i8>();
|
|
registry.register::<i32>();
|
|
|
|
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();
|
|
|
|
assert!(foo.reflect_partial_eq(&dynamic_struct).unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn reflect_take() {
|
|
#[derive(Reflect, Debug, PartialEq)]
|
|
#[reflect(PartialEq)]
|
|
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 });
|
|
}
|
|
|
|
#[test]
|
|
fn dynamic_names() {
|
|
let list = Vec::<usize>::new();
|
|
let dyn_list = List::clone_dynamic(&list);
|
|
assert_eq!(dyn_list.type_name(), std::any::type_name::<Vec<usize>>());
|
|
|
|
let array = [b'0'; 4];
|
|
let dyn_array = Array::clone_dynamic(&array);
|
|
assert_eq!(dyn_array.type_name(), std::any::type_name::<[u8; 4]>());
|
|
|
|
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>()
|
|
);
|
|
}
|
|
|
|
#[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();
|
|
}
|
|
|
|
#[test]
|
|
fn should_reflect_debug() {
|
|
#[derive(Reflect)]
|
|
struct Test {
|
|
value: usize,
|
|
list: Vec<String>,
|
|
array: [f32; 3],
|
|
map: HashMap<i32, f32>,
|
|
a_struct: SomeStruct,
|
|
a_tuple_struct: SomeTupleStruct,
|
|
custom: CustomDebug,
|
|
unknown: Option<String>,
|
|
#[reflect(ignore)]
|
|
#[allow(dead_code)]
|
|
ignored: isize,
|
|
}
|
|
|
|
#[derive(Reflect)]
|
|
struct SomeStruct {
|
|
foo: String,
|
|
}
|
|
|
|
#[derive(Reflect)]
|
|
struct SomeTupleStruct(String);
|
|
|
|
#[derive(Reflect)]
|
|
#[reflect(Debug)]
|
|
struct CustomDebug;
|
|
impl Debug for CustomDebug {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str("Cool debug!")
|
|
}
|
|
}
|
|
|
|
let mut map = HashMap::new();
|
|
map.insert(123, 1.23);
|
|
|
|
let test = Test {
|
|
value: 123,
|
|
list: vec![String::from("A"), String::from("B"), String::from("C")],
|
|
array: [1.0, 2.0, 3.0],
|
|
map,
|
|
a_struct: SomeStruct {
|
|
foo: String::from("A Struct!"),
|
|
},
|
|
a_tuple_struct: SomeTupleStruct(String::from("A Tuple Struct!")),
|
|
custom: CustomDebug,
|
|
unknown: Some(String::from("Enums aren't supported yet :(")),
|
|
ignored: 321,
|
|
};
|
|
|
|
let reflected: &dyn Reflect = &test;
|
|
let expected = r#"
|
|
bevy_reflect::tests::should_reflect_debug::Test {
|
|
value: 123,
|
|
list: [
|
|
"A",
|
|
"B",
|
|
"C",
|
|
],
|
|
array: [
|
|
1.0,
|
|
2.0,
|
|
3.0,
|
|
],
|
|
map: {
|
|
123: 1.23,
|
|
},
|
|
a_struct: bevy_reflect::tests::should_reflect_debug::SomeStruct {
|
|
foo: "A Struct!",
|
|
},
|
|
a_tuple_struct: bevy_reflect::tests::should_reflect_debug::SomeTupleStruct(
|
|
"A Tuple Struct!",
|
|
),
|
|
custom: Cool debug!,
|
|
unknown: Reflect(core::option::Option<alloc::string::String>),
|
|
}"#;
|
|
|
|
assert_eq!(expected, format!("\n{:#?}", reflected));
|
|
}
|
|
|
|
#[cfg(feature = "glam")]
|
|
mod glam {
|
|
use super::*;
|
|
use ::serde::Serialize;
|
|
|
|
#[test]
|
|
fn vec3_serialization() {
|
|
let v = vec3(12.0, 3.0, -6.9);
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
registry.add_registration(Vec3::get_type_registration());
|
|
|
|
let ser = ReflectSerializer::new(&v, ®istry);
|
|
|
|
let mut dest = vec![];
|
|
let mut serializer = ron::ser::Serializer::new(&mut dest, None, false)
|
|
.expect("Failed to acquire serializer");
|
|
|
|
ser.serialize(&mut serializer).expect("Failed to serialize");
|
|
|
|
let result = String::from_utf8(dest).expect("Failed to convert to string");
|
|
|
|
assert_eq!(
|
|
result,
|
|
r#"{"type":"glam::vec3::Vec3","struct":{"x":{"type":"f32","value":12},"y":{"type":"f32","value":3},"z":{"type":"f32","value":-6.9}}}"#
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn vec3_deserialization() {
|
|
let data = r#"{"type":"glam::vec3::Vec3","struct":{"x":{"type":"f32","value":12},"y":{"type":"f32","value":3},"z":{"type":"f32","value":-6.9}}}"#;
|
|
|
|
let mut registry = TypeRegistry::default();
|
|
registry.add_registration(Vec3::get_type_registration());
|
|
registry.add_registration(f32::get_type_registration());
|
|
|
|
let de = ReflectDeserializer::new(®istry);
|
|
|
|
let mut deserializer =
|
|
ron::de::Deserializer::from_str(data).expect("Failed to acquire deserializer");
|
|
|
|
let dynamic_struct = de
|
|
.deserialize(&mut deserializer)
|
|
.expect("Failed to deserialize");
|
|
|
|
let mut result = Vec3::default();
|
|
|
|
result.apply(&*dynamic_struct);
|
|
|
|
assert_eq!(result, vec3(12.0, 3.0, -6.9));
|
|
}
|
|
|
|
#[test]
|
|
fn vec3_field_access() {
|
|
let mut v = vec3(1.0, 2.0, 3.0);
|
|
|
|
assert_eq!(*v.get_field::<f32>("x").unwrap(), 1.0);
|
|
|
|
*v.get_field_mut::<f32>("y").unwrap() = 6.0;
|
|
|
|
assert_eq!(v.y, 6.0);
|
|
}
|
|
|
|
#[test]
|
|
fn vec3_path_access() {
|
|
let mut v = vec3(1.0, 2.0, 3.0);
|
|
|
|
assert_eq!(*v.path("x").unwrap().downcast_ref::<f32>().unwrap(), 1.0);
|
|
|
|
*v.path_mut("y").unwrap().downcast_mut::<f32>().unwrap() = 6.0;
|
|
|
|
assert_eq!(v.y, 6.0);
|
|
}
|
|
|
|
#[test]
|
|
fn vec3_apply_dynamic() {
|
|
let mut v = vec3(3.0, 3.0, 3.0);
|
|
|
|
let mut d = DynamicStruct::default();
|
|
d.insert("x", 4.0f32);
|
|
d.insert("y", 2.0f32);
|
|
d.insert("z", 1.0f32);
|
|
|
|
v.apply(&d);
|
|
|
|
assert_eq!(v, vec3(4.0, 2.0, 1.0));
|
|
}
|
|
}
|
|
}
|