#![cfg_attr(
any(docsrs, docsrs_dep),
expect(
internal_features,
reason = "rustdoc_internals is needed for fake_variadic"
)
)]
#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
#![doc(
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
#![no_std]
#[cfg(feature = "std")]
extern crate std;
extern crate alloc;
extern crate self as bevy_reflect;
mod array;
mod error;
mod fields;
mod from_reflect;
#[cfg(feature = "functions")]
pub mod func;
mod kind;
mod list;
mod map;
mod path;
mod reflect;
mod reflectable;
mod remote;
mod set;
mod struct_trait;
mod tuple;
mod tuple_struct;
mod type_info;
mod type_path;
mod type_registry;
mod impls {
mod alloc;
mod bevy_platform;
mod core;
mod foldhash;
#[cfg(feature = "hashbrown")]
mod hashbrown;
mod macros;
#[cfg(feature = "std")]
mod std;
#[cfg(feature = "glam")]
mod glam;
#[cfg(feature = "petgraph")]
mod petgraph;
#[cfg(feature = "smallvec")]
mod smallvec;
#[cfg(feature = "smol_str")]
mod smol_str;
#[cfg(feature = "uuid")]
mod uuid;
#[cfg(feature = "wgpu-types")]
mod wgpu_types;
}
pub mod attributes;
mod enums;
mod generics;
pub mod serde;
pub mod std_traits;
#[cfg(feature = "debug_stack")]
mod type_info_stack;
pub mod utility;
pub mod prelude {
pub use crate::std_traits::*;
#[doc(hidden)]
pub use crate::{
reflect_trait, FromReflect, GetField, GetPath, GetTupleStructField, PartialReflect,
Reflect, ReflectDeserialize, ReflectFromReflect, ReflectPath, ReflectSerialize, Struct,
TupleStruct, TypePath,
};
#[cfg(feature = "functions")]
pub use crate::func::{Function, IntoFunction, IntoFunctionMut};
}
pub use array::*;
pub use enums::*;
pub use error::*;
pub use fields::*;
pub use from_reflect::*;
pub use generics::*;
pub use kind::*;
pub use list::*;
pub use map::*;
pub use path::*;
pub use reflect::*;
pub use reflectable::*;
pub use remote::*;
pub use set::*;
pub use struct_trait::*;
pub use tuple::*;
pub use tuple_struct::*;
pub use type_info::*;
pub use type_path::*;
pub use type_registry::*;
pub use bevy_reflect_derive::*;
pub use erased_serde;
#[doc(hidden)]
pub mod __macro_exports {
use crate::{
DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple,
DynamicTupleStruct, GetTypeRegistration, TypeRegistry,
};
pub mod alloc_utils {
pub use ::alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
string::ToString,
};
}
#[diagnostic::on_unimplemented(
message = "`{Self}` does not implement `GetTypeRegistration` so cannot be registered for reflection",
note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
)]
pub trait RegisterForReflection {
#[expect(
unused_variables,
reason = "The parameters here are intentionally unused by the default implementation; however, putting underscores here will result in the underscores being copied by rust-analyzer's tab completion."
)]
fn __register(registry: &mut TypeRegistry) {}
}
impl<T: GetTypeRegistration> RegisterForReflection for T {
fn __register(registry: &mut TypeRegistry) {
registry.register::<T>();
}
}
impl RegisterForReflection for DynamicEnum {}
impl RegisterForReflection for DynamicTupleStruct {}
impl RegisterForReflection for DynamicStruct {}
impl RegisterForReflection for DynamicMap {}
impl RegisterForReflection for DynamicList {}
impl RegisterForReflection for DynamicArray {}
impl RegisterForReflection for DynamicTuple {}
#[cfg(feature = "auto_register")]
pub mod auto_register {
pub use super::*;
#[cfg(all(
not(feature = "auto_register_inventory"),
not(feature = "auto_register_static")
))]
compile_error!(
"Choosing a backend is required for automatic reflect registration. Please enable either the \"auto_register_inventory\" or the \"auto_register_static\" feature."
);
#[cfg(all(
not(feature = "auto_register_static"),
feature = "auto_register_inventory"
))]
mod __automatic_type_registration_impl {
use super::*;
pub use inventory;
pub struct AutomaticReflectRegistrations(pub fn(&mut TypeRegistry));
pub fn register_types(registry: &mut TypeRegistry) {
#[cfg(target_family = "wasm")]
wasm_support::init();
for registration_fn in inventory::iter::<AutomaticReflectRegistrations> {
registration_fn.0(registry);
}
}
inventory::collect!(AutomaticReflectRegistrations);
#[cfg(target_family = "wasm")]
mod wasm_support {
use bevy_platform::sync::atomic::{AtomicBool, Ordering};
static INIT_DONE: AtomicBool = AtomicBool::new(false);
#[expect(unsafe_code, reason = "This function is generated by linker.")]
unsafe extern "C" {
fn __wasm_call_ctors();
}
pub fn init() {
if INIT_DONE.swap(true, Ordering::Relaxed) {
return;
};
#[expect(
unsafe_code,
reason = "This function must be called to use inventory on wasm."
)]
unsafe {
__wasm_call_ctors();
}
}
}
}
#[cfg(feature = "auto_register_static")]
mod __automatic_type_registration_impl {
use super::*;
use alloc::vec::Vec;
use bevy_platform::sync::Mutex;
static REGISTRATION_FNS: Mutex<Vec<fn(&mut TypeRegistry)>> = Mutex::new(Vec::new());
pub fn push_registration_fn(registration_fn: fn(&mut TypeRegistry)) {
REGISTRATION_FNS.lock().unwrap().push(registration_fn);
}
pub fn register_types(registry: &mut TypeRegistry) {
for func in REGISTRATION_FNS.lock().unwrap().iter() {
(func)(registry);
}
}
}
#[cfg(any(feature = "auto_register_static", feature = "auto_register_inventory"))]
pub use __automatic_type_registration_impl::*;
}
}
#[cfg(test)]
#[expect(
clippy::approx_constant,
reason = "We don't need the exact value of Pi here."
)]
mod tests {
use ::serde::{de::DeserializeSeed, Deserialize, Serialize};
use alloc::{
borrow::Cow,
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use bevy_platform::collections::HashMap;
use core::{
any::TypeId,
fmt::{Debug, Formatter},
hash::Hash,
marker::PhantomData,
};
use disqualified::ShortName;
use ron::{
ser::{to_string_pretty, PrettyConfig},
Deserializer,
};
use static_assertions::{assert_impl_all, assert_not_impl_all};
use super::{prelude::*, *};
use crate::{
serde::{ReflectDeserializer, ReflectSerializer},
utility::GenericTypePathCell,
};
#[test]
fn try_apply_should_detect_kinds() {
#[derive(Reflect, Debug)]
struct Struct {
a: u32,
b: f32,
}
#[derive(Reflect, Debug)]
enum Enum {
A,
B(u32),
}
let mut struct_target = Struct {
a: 0xDEADBEEF,
b: 3.14,
};
let mut enum_target = Enum::A;
let array_src = [8, 0, 8];
let result = struct_target.try_apply(&enum_target);
assert!(
matches!(
result,
Err(ApplyError::MismatchedKinds {
from_kind: ReflectKind::Enum,
to_kind: ReflectKind::Struct
})
),
"result was {result:?}"
);
let result = enum_target.try_apply(&array_src);
assert!(
matches!(
result,
Err(ApplyError::MismatchedKinds {
from_kind: ReflectKind::Array,
to_kind: ReflectKind::Enum
})
),
"result was {result:?}"
);
}
#[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);
let c = foo.field("c").unwrap();
let value = c.reflect_ref().as_struct().unwrap();
assert_eq!(*value.get_field::<u32>("x").unwrap(), 1);
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().try_downcast_ref::<u32>().unwrap()
);
assert!(map.get(&key_c).is_none());
*map.get_mut(&key_b)
.unwrap()
.try_downcast_mut::<u32>()
.unwrap() = 20;
assert_eq!(
20,
*map.get(&key_b).unwrap().try_downcast_ref::<u32>().unwrap()
);
}
#[test]
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().try_downcast_ref::<u32>().unwrap()
);
assert_eq!(
4,
*patch.field(1).unwrap().try_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().try_downcast_ref::<u32>().unwrap());
assert_eq!(4, *iter.next().unwrap().try_downcast_ref::<u64>().unwrap());
}
#[test]
#[should_panic(
expected = "the given key of type `bevy_reflect::tests::Foo` does not support hashing"
)]
fn reflect_map_no_hash() {
#[derive(Reflect)]
struct Foo {
a: u32,
}
let foo = Foo { a: 1 };
assert!(foo.reflect_hash().is_none());
let mut map = DynamicMap::default();
map.insert(foo, 10u32);
}
#[test]
#[should_panic(
expected = "the dynamic type `bevy_reflect::DynamicStruct` (representing `bevy_reflect::tests::Foo`) does not support hashing"
)]
fn reflect_map_no_hash_dynamic_representing() {
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo {
a: u32,
}
let foo = Foo { a: 1 };
assert!(foo.reflect_hash().is_some());
let dynamic = foo.to_dynamic_struct();
let mut map = DynamicMap::default();
map.insert(dynamic, 11u32);
}
#[test]
#[should_panic(
expected = "the dynamic type `bevy_reflect::DynamicStruct` does not support hashing"
)]
fn reflect_map_no_hash_dynamic() {
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo {
a: u32,
}
let mut dynamic = DynamicStruct::default();
dynamic.insert("a", 4u32);
assert!(dynamic.reflect_hash().is_none());
let mut map = DynamicMap::default();
map.insert(dynamic, 11u32);
}
#[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.try_downcast_ref::<u32>().unwrap())
.collect();
assert_eq!(values, vec![1]);
}
#[test]
fn should_reflect_generic() {
struct FakeString {}
impl core::ops::Add<FakeString> for String {
type Output = Self;
fn add(self, _rhs: FakeString) -> Self::Output {
unreachable!()
}
}
#[derive(Reflect)]
struct Foo<A>(A);
#[derive(Reflect)]
struct Bar<A, B>(A, B);
#[derive(Reflect)]
struct Baz<A, B, C>(A, B, C);
}
#[test]
fn should_reflect_clone() {
#[derive(Reflect, Debug, PartialEq)]
struct Foo(usize);
let value = Foo(123);
let clone = value.reflect_clone().expect("should reflect_clone struct");
assert_eq!(value, clone.take::<Foo>().unwrap());
let foo = (123, 4.56);
let clone = foo.reflect_clone().expect("should reflect_clone tuple");
assert_eq!(foo, clone.take::<(u32, f32)>().unwrap());
}
#[test]
fn should_reflect_clone_generic_type() {
#[derive(Reflect, Debug, PartialEq)]
struct Foo<T, U>(T, #[reflect(ignore, clone)] PhantomData<U>);
#[derive(TypePath, Debug, PartialEq)]
struct Bar;
let value = Foo::<usize, Bar>(123, PhantomData);
let clone = value
.reflect_clone()
.expect("should reflect_clone generic struct");
assert_eq!(value, clone.take::<Foo<usize, Bar>>().unwrap());
}
#[test]
fn should_reflect_clone_with_clone() {
#[expect(
dead_code,
reason = "if things are working correctly, this function should never be called"
)]
fn custom_clone(_value: &usize) -> usize {
panic!("should not be called");
}
#[derive(Reflect, Clone, Debug, PartialEq)]
#[reflect(Clone)]
struct Foo(#[reflect(clone = "custom_clone")] usize);
let value = Foo(123);
let clone = value
.reflect_clone()
.expect("should reflect_clone tuple struct");
assert_eq!(value, clone.take::<Foo>().unwrap());
#[derive(Reflect, Clone, Debug, PartialEq)]
#[reflect(Clone)]
struct Bar {
#[reflect(clone = "custom_clone")]
value: usize,
}
let value = Bar { value: 123 };
let clone = value.reflect_clone().expect("should reflect_clone struct");
assert_eq!(value, clone.take::<Bar>().unwrap());
#[derive(Reflect, Clone, Debug, PartialEq)]
#[reflect(Clone)]
enum Baz {
Unit,
Tuple(#[reflect(clone = "custom_clone")] usize),
Struct {
#[reflect(clone = "custom_clone")]
value: usize,
},
}
let value = Baz::Unit;
let clone = value
.reflect_clone()
.expect("should reflect_clone unit variant");
assert_eq!(value, clone.take::<Baz>().unwrap());
let value = Baz::Tuple(123);
let clone = value
.reflect_clone()
.expect("should reflect_clone tuple variant");
assert_eq!(value, clone.take::<Baz>().unwrap());
let value = Baz::Struct { value: 123 };
let clone = value
.reflect_clone()
.expect("should reflect_clone struct variant");
assert_eq!(value, clone.take::<Baz>().unwrap());
}
#[test]
fn should_custom_reflect_clone() {
#[derive(Reflect, Debug, PartialEq)]
#[reflect(Clone(clone_foo))]
struct Foo(usize);
fn clone_foo(foo: &Foo) -> Foo {
Foo(foo.0 + 198)
}
let foo = Foo(123);
let clone = foo.reflect_clone().unwrap();
assert_eq!(Foo(321), clone.take::<Foo>().unwrap());
}
#[test]
fn should_not_clone_ignored_fields() {
#[derive(Reflect, Clone, Debug, PartialEq)]
struct Foo(#[reflect(ignore)] usize);
let foo = Foo(123);
let clone = foo.reflect_clone();
assert_eq!(
clone.unwrap_err(),
ReflectCloneError::FieldNotCloneable {
field: FieldId::Unnamed(0),
variant: None,
container_type_path: Cow::Borrowed(Foo::type_path()),
}
);
#[derive(Reflect, Clone, Debug, PartialEq)]
struct Bar {
#[reflect(ignore)]
value: usize,
}
let bar = Bar { value: 123 };
let clone = bar.reflect_clone();
assert_eq!(
clone.unwrap_err(),
ReflectCloneError::FieldNotCloneable {
field: FieldId::Named(Cow::Borrowed("value")),
variant: None,
container_type_path: Cow::Borrowed(Bar::type_path()),
}
);
#[derive(Reflect, Clone, Debug, PartialEq)]
enum Baz {
Tuple(#[reflect(ignore)] usize),
Struct {
#[reflect(ignore)]
value: usize,
},
}
let baz = Baz::Tuple(123);
let clone = baz.reflect_clone();
assert_eq!(
clone.unwrap_err(),
ReflectCloneError::FieldNotCloneable {
field: FieldId::Unnamed(0),
variant: Some(Cow::Borrowed("Tuple")),
container_type_path: Cow::Borrowed(Baz::type_path()),
}
);
let baz = Baz::Struct { value: 123 };
let clone = baz.reflect_clone();
assert_eq!(
clone.unwrap_err(),
ReflectCloneError::FieldNotCloneable {
field: FieldId::Named(Cow::Borrowed("value")),
variant: Some(Cow::Borrowed("Struct")),
container_type_path: Cow::Borrowed(Baz::type_path()),
}
);
}
#[test]
fn should_clone_ignored_fields_with_clone_attributes() {
#[derive(Reflect, Clone, Debug, PartialEq)]
struct Foo(#[reflect(ignore, clone)] usize);
let foo = Foo(123);
let clone = foo.reflect_clone().unwrap();
assert_eq!(Foo(123), clone.take::<Foo>().unwrap());
#[derive(Reflect, Clone, Debug, PartialEq)]
struct Bar(#[reflect(ignore, clone = "clone_usize")] usize);
fn clone_usize(this: &usize) -> usize {
*this + 198
}
let bar = Bar(123);
let clone = bar.reflect_clone().unwrap();
assert_eq!(Bar(321), clone.take::<Bar>().unwrap());
}
#[test]
fn should_composite_reflect_clone() {
#[derive(Reflect, Debug, PartialEq)]
enum MyEnum {
Unit,
Tuple(
Foo,
#[reflect(ignore, clone)] Bar,
#[reflect(clone = "clone_baz")] Baz,
),
Struct {
foo: Foo,
#[reflect(ignore, clone)]
bar: Bar,
#[reflect(clone = "clone_baz")]
baz: Baz,
},
}
#[derive(Reflect, Debug, PartialEq)]
struct Foo {
#[reflect(clone = "clone_bar")]
bar: Bar,
baz: Baz,
}
#[derive(Reflect, Default, Clone, Debug, PartialEq)]
#[reflect(Clone)]
struct Bar(String);
#[derive(Reflect, Debug, PartialEq)]
struct Baz(String);
fn clone_bar(bar: &Bar) -> Bar {
Bar(format!("{}!", bar.0))
}
fn clone_baz(baz: &Baz) -> Baz {
Baz(format!("{}!", baz.0))
}
let my_enum = MyEnum::Unit;
let clone = my_enum.reflect_clone().unwrap();
assert_eq!(MyEnum::Unit, clone.take::<MyEnum>().unwrap());
let my_enum = MyEnum::Tuple(
Foo {
bar: Bar("bar".to_string()),
baz: Baz("baz".to_string()),
},
Bar("bar".to_string()),
Baz("baz".to_string()),
);
let clone = my_enum.reflect_clone().unwrap();
assert_eq!(
MyEnum::Tuple(
Foo {
bar: Bar("bar!".to_string()),
baz: Baz("baz".to_string()),
},
Bar("bar".to_string()),
Baz("baz!".to_string()),
),
clone.take::<MyEnum>().unwrap()
);
let my_enum = MyEnum::Struct {
foo: Foo {
bar: Bar("bar".to_string()),
baz: Baz("baz".to_string()),
},
bar: Bar("bar".to_string()),
baz: Baz("baz".to_string()),
};
let clone = my_enum.reflect_clone().unwrap();
assert_eq!(
MyEnum::Struct {
foo: Foo {
bar: Bar("bar!".to_string()),
baz: Baz("baz".to_string()),
},
bar: Bar("bar".to_string()),
baz: Baz("baz!".to_string()),
},
clone.take::<MyEnum>().unwrap()
);
}
#[test]
fn should_call_from_reflect_dynamically() {
#[derive(Reflect)]
struct MyStruct {
foo: usize,
}
let mut registry = TypeRegistry::default();
registry.register::<MyStruct>();
let type_id = TypeId::of::<MyStruct>();
let rfr = registry
.get_type_data::<ReflectFromReflect>(type_id)
.expect("the FromReflect trait should be registered");
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");
let expected = MyStruct { foo: 123 };
assert!(expected
.reflect_partial_eq(reflected.as_partial_reflect())
.unwrap_or_default());
let not_expected = MyStruct { foo: 321 };
assert!(!not_expected
.reflect_partial_eq(reflected.as_partial_reflect())
.unwrap_or_default());
}
#[test]
fn from_reflect_should_allow_ignored_unnamed_fields() {
#[derive(Reflect, Eq, PartialEq, Debug)]
struct MyTupleStruct(i8, #[reflect(ignore)] i16, i32);
let expected = MyTupleStruct(1, 0, 3);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1_i8);
dyn_tuple_struct.insert(3_i32);
let my_tuple_struct = <MyTupleStruct as FromReflect>::from_reflect(&dyn_tuple_struct);
assert_eq!(Some(expected), my_tuple_struct);
#[derive(Reflect, Eq, PartialEq, Debug)]
enum MyEnum {
Tuple(i8, #[reflect(ignore)] i16, i32),
}
let expected = MyEnum::Tuple(1, 0, 3);
let mut dyn_tuple = DynamicTuple::default();
dyn_tuple.insert(1_i8);
dyn_tuple.insert(3_i32);
let mut dyn_enum = DynamicEnum::default();
dyn_enum.set_variant("Tuple", dyn_tuple);
let my_enum = <MyEnum as FromReflect>::from_reflect(&dyn_enum);
assert_eq!(Some(expected), my_enum);
}
#[test]
fn from_reflect_should_use_default_field_attributes() {
#[derive(Reflect, Eq, PartialEq, Debug)]
struct MyStruct {
#[reflect(default)]
foo: String,
#[reflect(ignore)]
#[reflect(default = "get_bar_default")]
bar: NotReflect,
#[reflect(ignore, default = "get_bar_default")]
baz: NotReflect,
}
#[derive(Eq, PartialEq, Debug)]
struct NotReflect(usize);
fn get_bar_default() -> NotReflect {
NotReflect(123)
}
let expected = MyStruct {
foo: String::default(),
bar: NotReflect(123),
baz: NotReflect(123),
};
let dyn_struct = DynamicStruct::default();
let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct);
assert_eq!(Some(expected), my_struct);
}
#[test]
fn from_reflect_should_use_default_variant_field_attributes() {
#[derive(Reflect, Eq, PartialEq, Debug)]
enum MyEnum {
Foo(#[reflect(default)] String),
Bar {
#[reflect(default = "get_baz_default")]
#[reflect(ignore)]
baz: usize,
},
}
fn get_baz_default() -> usize {
123
}
let expected = MyEnum::Foo(String::default());
let dyn_enum = DynamicEnum::new("Foo", DynamicTuple::default());
let my_enum = <MyEnum as FromReflect>::from_reflect(&dyn_enum);
assert_eq!(Some(expected), my_enum);
let expected = MyEnum::Bar {
baz: get_baz_default(),
};
let dyn_enum = DynamicEnum::new("Bar", DynamicStruct::default());
let my_enum = <MyEnum as FromReflect>::from_reflect(&dyn_enum);
assert_eq!(Some(expected), my_enum);
}
#[test]
fn from_reflect_should_use_default_container_attribute() {
#[derive(Reflect, Eq, PartialEq, Debug)]
#[reflect(Default)]
struct MyStruct {
foo: String,
#[reflect(ignore)]
bar: usize,
}
impl Default for MyStruct {
fn default() -> Self {
Self {
foo: String::from("Hello"),
bar: 123,
}
}
}
let expected = MyStruct {
foo: String::from("Hello"),
bar: 123,
};
let dyn_struct = DynamicStruct::default();
let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct);
assert_eq!(Some(expected), my_struct);
}
#[test]
fn reflect_complex_patch() {
#[derive(Reflect, Eq, PartialEq, Debug)]
#[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)]
#[reflect(PartialEq)]
struct Bar {
x: u32,
}
#[derive(Reflect, Eq, PartialEq, Debug)]
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);
let mut list = DynamicList::default();
list.push(3isize);
list.push(4isize);
list.push(5isize);
foo_patch.insert("c", list.to_dynamic_list());
let mut map = DynamicMap::default();
map.insert(2usize, 3i8);
map.insert(3usize, 4i8);
foo_patch.insert("d", map);
let mut bar_patch = DynamicStruct::default();
bar_patch.insert("x", 2u32);
foo_patch.insert("e", bar_patch.to_dynamic_struct());
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_iter([2u32, 2u32]);
foo_patch.insert("h", array);
foo.apply(&foo_patch);
let mut hash_map = <HashMap<_, _>>::default();
hash_map.insert(2, 3);
hash_map.insert(3, 4);
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);
hash_map.insert(3, 4);
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 should_auto_register_fields() {
#[derive(Reflect)]
struct Foo {
bar: Bar,
}
#[derive(Reflect)]
enum Bar {
Variant(Baz),
}
#[derive(Reflect)]
struct Baz(usize);
let mut registry = TypeRegistry::empty();
registry.register::<Foo>();
assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `Foo`"
);
let mut registry = TypeRegistry::empty();
registry.register::<Option<Foo>>();
assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `Option<Foo>`"
);
let mut registry = TypeRegistry::empty();
registry.register::<(Foo, Foo)>();
assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `(Foo, Foo)`"
);
let mut registry = TypeRegistry::empty();
registry.register::<[Foo; 3]>();
assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `[Foo; 3]`"
);
let mut registry = TypeRegistry::empty();
registry.register::<Vec<Foo>>();
assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `Vec<Foo>`"
);
let mut registry = TypeRegistry::empty();
registry.register::<HashMap<i32, Foo>>();
assert!(
registry.contains(TypeId::of::<Bar>()),
"registry should contain auto-registered `Bar` from `HashMap<i32, Foo>`"
);
}
#[test]
fn should_allow_dynamic_fields() {
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct MyStruct(
DynamicEnum,
DynamicTupleStruct,
DynamicStruct,
DynamicMap,
DynamicList,
DynamicArray,
DynamicTuple,
i32,
);
assert_impl_all!(MyStruct: Reflect, GetTypeRegistration);
let mut registry = TypeRegistry::empty();
registry.register::<MyStruct>();
assert_eq!(2, registry.iter().count());
assert!(registry.contains(TypeId::of::<MyStruct>()));
assert!(registry.contains(TypeId::of::<i32>()));
}
#[test]
fn should_not_auto_register_existing_types() {
#[derive(Reflect)]
struct Foo {
bar: Bar,
}
#[derive(Reflect, Default)]
struct Bar(usize);
let mut registry = TypeRegistry::empty();
registry.register::<Bar>();
registry.register_type_data::<Bar, ReflectDefault>();
registry.register::<Foo>();
assert!(
registry
.get_type_data::<ReflectDefault>(TypeId::of::<Bar>())
.is_some(),
"registry should contain existing registration for `Bar`"
);
}
#[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, Serialize, Deserialize)]
#[reflect(Serialize, Deserialize)]
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::<i8>();
registry.register::<i32>();
registry.register::<usize>();
registry.register::<isize>();
registry.register::<Foo>();
registry.register::<Bar>();
registry.register::<String>();
registry.register::<Vec<isize>>();
registry.register::<HashMap<usize, i8>>();
registry.register::<(i32, Vec<isize>, Bar)>();
registry.register::<[u32; 2]>();
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 roundtrip_foo = Foo::from_reflect(value.as_partial_reflect()).unwrap();
assert!(foo.reflect_partial_eq(&roundtrip_foo).unwrap());
}
#[test]
fn reflect_downcast() {
#[derive(Reflect, Clone, Debug, PartialEq)]
struct Bar {
y: u8,
}
#[derive(Reflect, Clone, Debug, PartialEq)]
struct Foo {
x: i32,
s: String,
b: Bar,
u: usize,
t: ([f32; 3], String),
v: Cow<'static, str>,
w: Cow<'static, [u8]>,
}
let foo = Foo {
x: 123,
s: "String".to_string(),
b: Bar { y: 255 },
u: 1111111111111,
t: ([3.0, 2.0, 1.0], "Tuple String".to_string()),
v: Cow::Owned("Cow String".to_string()),
w: Cow::Owned(vec![1, 2, 3]),
};
let foo2: Box<dyn Reflect> = Box::new(foo.clone());
assert_eq!(foo, *foo2.downcast::<Foo>().unwrap());
}
#[test]
fn should_drain_fields() {
let array_value: Box<dyn Array> = Box::new([123_i32, 321_i32]);
let fields = array_value.drain();
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
let mut list_value: Box<dyn List> = Box::new(vec![123_i32, 321_i32]);
let fields = list_value.drain();
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
let tuple_value: Box<dyn Tuple> = Box::new((123_i32, 321_i32));
let fields = tuple_value.drain();
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
let mut map_value: Box<dyn Map> =
Box::new([(123_i32, 321_i32)].into_iter().collect::<HashMap<_, _>>());
let fields = map_value.drain();
assert!(fields[0].0.reflect_partial_eq(&123_i32).unwrap_or_default());
assert!(fields[0].1.reflect_partial_eq(&321_i32).unwrap_or_default());
}
#[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 not_dynamic_names() {
let list = Vec::<usize>::new();
let dyn_list = list.to_dynamic_list();
assert_ne!(dyn_list.reflect_type_path(), Vec::<usize>::type_path());
let array = [b'0'; 4];
let dyn_array = array.to_dynamic_array();
assert_ne!(dyn_array.reflect_type_path(), <[u8; 4]>::type_path());
let map = HashMap::<usize, String>::default();
let dyn_map = map.to_dynamic_map();
assert_ne!(
dyn_map.reflect_type_path(),
HashMap::<usize, String>::type_path()
);
let tuple = (0usize, "1".to_string(), 2.0f32);
let mut dyn_tuple = tuple.to_dynamic_tuple();
dyn_tuple.insert::<usize>(3);
assert_ne!(
dyn_tuple.reflect_type_path(),
<(usize, String, f32, usize)>::type_path()
);
#[derive(Reflect)]
struct TestStruct {
a: usize,
}
let struct_ = TestStruct { a: 0 };
let dyn_struct = struct_.to_dynamic_struct();
assert_ne!(dyn_struct.reflect_type_path(), TestStruct::type_path());
#[derive(Reflect)]
struct TestTupleStruct(usize);
let tuple_struct = TestTupleStruct(0);
let dyn_tuple_struct = tuple_struct.to_dynamic_tuple_struct();
assert_ne!(
dyn_tuple_struct.reflect_type_path(),
TestTupleStruct::type_path()
);
}
macro_rules! assert_type_paths {
($($ty:ty => $long:literal, $short:literal,)*) => {
$(
assert_eq!(<$ty as TypePath>::type_path(), $long);
assert_eq!(<$ty as TypePath>::short_type_path(), $short);
)*
};
}
#[test]
fn reflect_type_path() {
#[derive(TypePath)]
struct Param;
#[derive(TypePath)]
struct Derive;
#[derive(TypePath)]
#[type_path = "my_alias"]
struct DerivePath;
#[derive(TypePath)]
#[type_path = "my_alias"]
#[type_name = "MyDerivePathName"]
struct DerivePathName;
#[derive(TypePath)]
struct DeriveG<T>(PhantomData<T>);
#[derive(TypePath)]
#[type_path = "my_alias"]
struct DerivePathG<T, const N: usize>(PhantomData<T>);
#[derive(TypePath)]
#[type_path = "my_alias"]
#[type_name = "MyDerivePathNameG"]
struct DerivePathNameG<T>(PhantomData<T>);
struct Macro;
impl_type_path!((in my_alias) Macro);
struct MacroName;
impl_type_path!((in my_alias as MyMacroName) MacroName);
struct MacroG<T, const N: usize>(PhantomData<T>);
impl_type_path!((in my_alias) MacroG<T, const N: usize>);
struct MacroNameG<T>(PhantomData<T>);
impl_type_path!((in my_alias as MyMacroNameG) MacroNameG<T>);
assert_type_paths! {
Derive => "bevy_reflect::tests::Derive", "Derive",
DerivePath => "my_alias::DerivePath", "DerivePath",
DerivePathName => "my_alias::MyDerivePathName", "MyDerivePathName",
DeriveG<Param> => "bevy_reflect::tests::DeriveG<bevy_reflect::tests::Param>", "DeriveG<Param>",
DerivePathG<Param, 10> => "my_alias::DerivePathG<bevy_reflect::tests::Param, 10>", "DerivePathG<Param, 10>",
DerivePathNameG<Param> => "my_alias::MyDerivePathNameG<bevy_reflect::tests::Param>", "MyDerivePathNameG<Param>",
Macro => "my_alias::Macro", "Macro",
MacroName => "my_alias::MyMacroName", "MyMacroName",
MacroG<Param, 10> => "my_alias::MacroG<bevy_reflect::tests::Param, 10>", "MacroG<Param, 10>",
MacroNameG<Param> => "my_alias::MyMacroNameG<bevy_reflect::tests::Param>", "MyMacroNameG<Param>",
}
}
#[test]
fn std_type_paths() {
#[derive(Clone)]
struct Type;
impl TypePath for Type {
fn type_path() -> &'static str {
"Long"
}
fn short_type_path() -> &'static str {
"Short"
}
}
assert_type_paths! {
u8 => "u8", "u8",
Type => "Long", "Short",
&Type => "&Long", "&Short",
[Type] => "[Long]", "[Short]",
&[Type] => "&[Long]", "&[Short]",
[Type; 0] => "[Long; 0]", "[Short; 0]",
[Type; 100] => "[Long; 100]", "[Short; 100]",
() => "()", "()",
(Type,) => "(Long,)", "(Short,)",
(Type, Type) => "(Long, Long)", "(Short, Short)",
(Type, Type, Type) => "(Long, Long, Long)", "(Short, Short, Short)",
Cow<'static, Type> => "alloc::borrow::Cow<Long>", "Cow<Short>",
}
}
#[test]
fn reflect_type_info() {
let info = i32::type_info();
assert_eq!(i32::type_path(), info.type_path());
assert_eq!(TypeId::of::<i32>(), info.type_id());
assert_eq!(
TypeId::of::<dyn Reflect>(),
<dyn Reflect as Typed>::type_info().type_id()
);
let value: &dyn Reflect = &123_i32;
let info = value.reflect_type_info();
assert!(info.is::<i32>());
#[derive(Reflect)]
struct MyStruct {
foo: i32,
bar: usize,
}
let info = MyStruct::type_info().as_struct().unwrap();
assert!(info.is::<MyStruct>());
assert_eq!(MyStruct::type_path(), info.type_path());
assert_eq!(i32::type_path(), info.field("foo").unwrap().type_path());
assert_eq!(TypeId::of::<i32>(), info.field("foo").unwrap().type_id());
assert!(info.field("foo").unwrap().type_info().unwrap().is::<i32>());
assert!(info.field("foo").unwrap().is::<i32>());
assert_eq!("foo", info.field("foo").unwrap().name());
assert_eq!(usize::type_path(), info.field_at(1).unwrap().type_path());
let value: &dyn Reflect = &MyStruct { foo: 123, bar: 321 };
let info = value.reflect_type_info();
assert!(info.is::<MyStruct>());
#[derive(Reflect)]
struct MyGenericStruct<T> {
foo: T,
bar: usize,
}
let info = <MyGenericStruct<i32>>::type_info().as_struct().unwrap();
assert!(info.is::<MyGenericStruct<i32>>());
assert_eq!(MyGenericStruct::<i32>::type_path(), info.type_path());
assert_eq!(i32::type_path(), info.field("foo").unwrap().type_path());
assert_eq!("foo", info.field("foo").unwrap().name());
assert!(info.field("foo").unwrap().type_info().unwrap().is::<i32>());
assert_eq!(usize::type_path(), info.field_at(1).unwrap().type_path());
let value: &dyn Reflect = &MyGenericStruct {
foo: String::from("Hello!"),
bar: 321,
};
let info = value.reflect_type_info();
assert!(info.is::<MyGenericStruct<String>>());
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct MyDynamicStruct {
foo: DynamicStruct,
bar: usize,
}
let info = MyDynamicStruct::type_info();
if let TypeInfo::Struct(info) = info {
assert!(info.is::<MyDynamicStruct>());
assert_eq!(MyDynamicStruct::type_path(), info.type_path());
assert_eq!(
DynamicStruct::type_path(),
info.field("foo").unwrap().type_path()
);
assert_eq!("foo", info.field("foo").unwrap().name());
assert!(info.field("foo").unwrap().type_info().is_none());
assert_eq!(usize::type_path(), info.field_at(1).unwrap().type_path());
} else {
panic!("Expected `TypeInfo::Struct`");
}
let value: &dyn Reflect = &MyDynamicStruct {
foo: DynamicStruct::default(),
bar: 321,
};
let info = value.reflect_type_info();
assert!(info.is::<MyDynamicStruct>());
#[derive(Reflect)]
struct MyTupleStruct(usize, i32, MyStruct);
let info = MyTupleStruct::type_info().as_tuple_struct().unwrap();
assert!(info.is::<MyTupleStruct>());
assert_eq!(MyTupleStruct::type_path(), info.type_path());
assert_eq!(i32::type_path(), info.field_at(1).unwrap().type_path());
assert!(info.field_at(1).unwrap().type_info().unwrap().is::<i32>());
assert!(info.field_at(1).unwrap().is::<i32>());
type MyTuple = (u32, f32, String);
let info = MyTuple::type_info().as_tuple().unwrap();
assert!(info.is::<MyTuple>());
assert_eq!(MyTuple::type_path(), info.type_path());
assert_eq!(f32::type_path(), info.field_at(1).unwrap().type_path());
assert!(info.field_at(1).unwrap().type_info().unwrap().is::<f32>());
let value: &dyn Reflect = &(123_u32, 1.23_f32, String::from("Hello!"));
let info = value.reflect_type_info();
assert!(info.is::<MyTuple>());
type MyList = Vec<usize>;
let info = MyList::type_info().as_list().unwrap();
assert!(info.is::<MyList>());
assert!(info.item_ty().is::<usize>());
assert!(info.item_info().unwrap().is::<usize>());
assert_eq!(MyList::type_path(), info.type_path());
assert_eq!(usize::type_path(), info.item_ty().path());
let value: &dyn Reflect = &vec![123_usize];
let info = value.reflect_type_info();
assert!(info.is::<MyList>());
#[cfg(feature = "smallvec")]
{
type MySmallVec = smallvec::SmallVec<[String; 2]>;
let info = MySmallVec::type_info().as_list().unwrap();
assert!(info.is::<MySmallVec>());
assert!(info.item_ty().is::<String>());
assert!(info.item_info().unwrap().is::<String>());
assert_eq!(MySmallVec::type_path(), info.type_path());
assert_eq!(String::type_path(), info.item_ty().path());
let value: MySmallVec = smallvec::smallvec![String::default(); 2];
let value: &dyn Reflect = &value;
let info = value.reflect_type_info();
assert!(info.is::<MySmallVec>());
}
type MyArray = [usize; 3];
let info = MyArray::type_info().as_array().unwrap();
assert!(info.is::<MyArray>());
assert!(info.item_ty().is::<usize>());
assert!(info.item_info().unwrap().is::<usize>());
assert_eq!(MyArray::type_path(), info.type_path());
assert_eq!(usize::type_path(), info.item_ty().path());
assert_eq!(3, info.capacity());
let value: &dyn Reflect = &[1usize, 2usize, 3usize];
let info = value.reflect_type_info();
assert!(info.is::<MyArray>());
type MyCowStr = Cow<'static, str>;
let info = MyCowStr::type_info().as_opaque().unwrap();
assert!(info.is::<MyCowStr>());
assert_eq!(core::any::type_name::<MyCowStr>(), info.type_path());
let value: &dyn Reflect = &Cow::<'static, str>::Owned("Hello!".to_string());
let info = value.reflect_type_info();
assert!(info.is::<MyCowStr>());
type MyCowSlice = Cow<'static, [u8]>;
let info = MyCowSlice::type_info().as_list().unwrap();
assert!(info.is::<MyCowSlice>());
assert!(info.item_ty().is::<u8>());
assert!(info.item_info().unwrap().is::<u8>());
assert_eq!(core::any::type_name::<MyCowSlice>(), info.type_path());
assert_eq!(core::any::type_name::<u8>(), info.item_ty().path());
let value: &dyn Reflect = &Cow::<'static, [u8]>::Owned(vec![0, 1, 2, 3]);
let info = value.reflect_type_info();
assert!(info.is::<MyCowSlice>());
type MyMap = HashMap<usize, f32>;
let info = MyMap::type_info().as_map().unwrap();
assert!(info.is::<MyMap>());
assert!(info.key_ty().is::<usize>());
assert!(info.value_ty().is::<f32>());
assert!(info.key_info().unwrap().is::<usize>());
assert!(info.value_info().unwrap().is::<f32>());
assert_eq!(MyMap::type_path(), info.type_path());
assert_eq!(usize::type_path(), info.key_ty().path());
assert_eq!(f32::type_path(), info.value_ty().path());
let value: &dyn Reflect = &MyMap::default();
let info = value.reflect_type_info();
assert!(info.is::<MyMap>());
type MyValue = String;
let info = MyValue::type_info().as_opaque().unwrap();
assert!(info.is::<MyValue>());
assert_eq!(MyValue::type_path(), info.type_path());
let value: &dyn Reflect = &String::from("Hello!");
let info = value.reflect_type_info();
assert!(info.is::<MyValue>());
}
#[test]
fn get_represented_kind_info() {
#[derive(Reflect)]
struct SomeStruct;
#[derive(Reflect)]
struct SomeTupleStruct(f32);
#[derive(Reflect)]
enum SomeEnum {
Foo,
Bar,
}
let dyn_struct: &dyn Struct = &SomeStruct;
let _: &StructInfo = dyn_struct.get_represented_struct_info().unwrap();
let dyn_map: &dyn Map = &HashMap::<(), ()>::default();
let _: &MapInfo = dyn_map.get_represented_map_info().unwrap();
let dyn_array: &dyn Array = &[1, 2, 3];
let _: &ArrayInfo = dyn_array.get_represented_array_info().unwrap();
let dyn_list: &dyn List = &vec![1, 2, 3];
let _: &ListInfo = dyn_list.get_represented_list_info().unwrap();
let dyn_tuple_struct: &dyn TupleStruct = &SomeTupleStruct(5.0);
let _: &TupleStructInfo = dyn_tuple_struct
.get_represented_tuple_struct_info()
.unwrap();
let dyn_enum: &dyn Enum = &SomeEnum::Foo;
let _: &EnumInfo = dyn_enum.get_represented_enum_info().unwrap();
}
#[test]
fn should_permit_higher_ranked_lifetimes() {
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct TestStruct {
#[reflect(ignore)]
_hrl: for<'a> fn(&'a str) -> &'a str,
}
impl Default for TestStruct {
fn default() -> Self {
TestStruct {
_hrl: |input| input,
}
}
}
fn get_type_registration<T: GetTypeRegistration>() {}
get_type_registration::<TestStruct>();
}
#[test]
fn should_permit_valid_represented_type_for_dynamic() {
let type_info = <[i32; 2] as Typed>::type_info();
let mut dynamic_array = [123; 2].to_dynamic_array();
dynamic_array.set_represented_type(Some(type_info));
}
#[test]
#[should_panic(expected = "expected TypeInfo::Array but received")]
fn should_prohibit_invalid_represented_type_for_dynamic() {
let type_info = <(i32, i32) as Typed>::type_info();
let mut dynamic_array = [123; 2].to_dynamic_array();
dynamic_array.set_represented_type(Some(type_info));
}
#[cfg(feature = "documentation")]
mod docstrings {
use super::*;
#[test]
fn should_not_contain_docs() {
#[derive(Reflect)]
struct SomeStruct;
let info = <SomeStruct as Typed>::type_info();
assert_eq!(None, info.docs());
#[derive(Reflect)]
struct SomeOtherStruct;
let info = <SomeOtherStruct as Typed>::type_info();
assert_eq!(None, info.docs());
}
#[test]
fn should_contain_docs() {
#[derive(Reflect)]
struct SomeStruct;
let info = <SomeStruct as Typed>::type_info();
assert_eq!(
Some(" Some struct.\n\n # Example\n\n ```ignore (This is only used for a unit test, no need to doc test)\n let some_struct = SomeStruct;\n ```"),
info.docs()
);
#[doc = "The compiler automatically converts `///`-style comments into `#[doc]` attributes."]
#[doc = "Of course, you _could_ use the attribute directly if you wanted to."]
#[doc = "Both will be reflected."]
#[derive(Reflect)]
struct SomeOtherStruct;
let info = <SomeOtherStruct as Typed>::type_info();
assert_eq!(
Some("The compiler automatically converts `///`-style comments into `#[doc]` attributes.\nOf course, you _could_ use the attribute directly if you wanted to.\nBoth will be reflected."),
info.docs()
);
#[derive(Reflect)]
struct SomeTupleStruct(usize);
let info = <SomeTupleStruct as Typed>::type_info();
assert_eq!(Some(" Some tuple struct."), info.docs());
#[derive(Reflect)]
enum SomeEnum {
Foo,
}
let info = <SomeEnum as Typed>::type_info();
assert_eq!(Some(" Some enum."), info.docs());
#[derive(Clone)]
struct SomePrimitive;
impl_reflect_opaque!(
(in bevy_reflect::tests) SomePrimitive
);
let info = <SomePrimitive as Typed>::type_info();
assert_eq!(
Some(" Some primitive for which we have attributed custom documentation."),
info.docs()
);
}
#[test]
fn fields_should_contain_docs() {
#[derive(Reflect)]
struct SomeStruct {
name: String,
index: usize,
data: Vec<i32>,
}
let info = <SomeStruct as Typed>::type_info().as_struct().unwrap();
let mut fields = info.iter();
assert_eq!(Some(" The name"), fields.next().unwrap().docs());
assert_eq!(Some(" The index"), fields.next().unwrap().docs());
assert_eq!(None, fields.next().unwrap().docs());
}
#[test]
fn variants_should_contain_docs() {
#[derive(Reflect)]
enum SomeEnum {
Nothing,
A(
usize,
),
B {
name: String,
},
}
let info = <SomeEnum as Typed>::type_info().as_enum().unwrap();
let mut variants = info.iter();
assert_eq!(None, variants.next().unwrap().docs());
let variant = variants.next().unwrap().as_tuple_variant().unwrap();
assert_eq!(Some(" Option A"), variant.docs());
let field = variant.field_at(0).unwrap();
assert_eq!(Some(" Index"), field.docs());
let variant = variants.next().unwrap().as_struct_variant().unwrap();
assert_eq!(Some(" Option B"), variant.docs());
let field = variant.field_at(0).unwrap();
assert_eq!(Some(" Name"), field.docs());
}
}
#[test]
fn into_reflect() {
trait TestTrait: Reflect {}
#[derive(Reflect)]
struct TestStruct;
impl TestTrait for TestStruct {}
let trait_object: Box<dyn TestTrait> = Box::new(TestStruct);
let _ = trait_object.into_reflect();
}
#[test]
fn as_reflect() {
trait TestTrait: Reflect {}
#[derive(Reflect)]
struct TestStruct;
impl TestTrait for TestStruct {}
let trait_object: Box<dyn TestTrait> = Box::new(TestStruct);
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,
enum_unit: SomeEnum,
enum_tuple: SomeEnum,
enum_struct: SomeEnum,
custom: CustomDebug,
#[reflect(ignore)]
#[expect(dead_code, reason = "This value is intended to not be reflected.")]
ignored: isize,
}
#[derive(Reflect)]
struct SomeStruct {
foo: String,
}
#[derive(Reflect)]
enum SomeEnum {
A,
B(usize),
C { value: i32 },
}
#[derive(Reflect)]
struct SomeTupleStruct(String);
#[derive(Reflect)]
#[reflect(Debug)]
struct CustomDebug;
impl Debug for CustomDebug {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str("Cool debug!")
}
}
let mut map = <HashMap<_, _>>::default();
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!")),
enum_unit: SomeEnum::A,
enum_tuple: SomeEnum::B(123),
enum_struct: SomeEnum::C { value: 321 },
custom: CustomDebug,
ignored: 321,
};
let reflected: &dyn Reflect = &test;
let expected = r#"
bevy_reflect::tests::Test {
value: 123,
list: [
"A",
"B",
"C",
],
array: [
1.0,
2.0,
3.0,
],
map: {
123: 1.23,
},
a_struct: bevy_reflect::tests::SomeStruct {
foo: "A Struct!",
},
a_tuple_struct: bevy_reflect::tests::SomeTupleStruct(
"A Tuple Struct!",
),
enum_unit: A,
enum_tuple: B(
123,
),
enum_struct: C {
value: 321,
},
custom: Cool debug!,
}"#;
assert_eq!(expected, format!("\n{reflected:#?}"));
}
#[test]
fn multiple_reflect_lists() {
#[derive(Hash, PartialEq, Reflect)]
#[reflect(Debug, Hash)]
#[reflect(PartialEq)]
struct Foo(i32);
impl Debug for Foo {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "Foo")
}
}
let foo = Foo(123);
let foo: &dyn PartialReflect = &foo;
assert!(foo.reflect_hash().is_some());
assert_eq!(Some(true), foo.reflect_partial_eq(foo));
assert_eq!("Foo".to_string(), format!("{foo:?}"));
}
#[test]
fn custom_debug_function() {
#[derive(Reflect)]
#[reflect(Debug(custom_debug))]
struct Foo {
a: u32,
}
fn custom_debug(_x: &Foo, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "123")
}
let foo = Foo { a: 1 };
let foo: &dyn Reflect = &foo;
assert_eq!("123", format!("{foo:?}"));
}
#[test]
fn should_allow_custom_where() {
#[derive(Reflect)]
#[reflect(where T: Default)]
struct Foo<T>(String, #[reflect(ignore)] PhantomData<T>);
#[derive(Default, TypePath)]
struct Bar;
#[derive(TypePath)]
struct Baz;
assert_impl_all!(Foo<Bar>: Reflect);
assert_not_impl_all!(Foo<Baz>: Reflect);
}
#[test]
fn should_allow_empty_custom_where() {
#[derive(Reflect)]
#[reflect(where)]
struct Foo<T>(String, #[reflect(ignore)] PhantomData<T>);
#[derive(TypePath)]
struct Bar;
assert_impl_all!(Foo<Bar>: Reflect);
}
#[test]
fn should_allow_multiple_custom_where() {
#[derive(Reflect)]
#[reflect(where T: Default)]
#[reflect(where U: core::ops::Add<T>)]
struct Foo<T, U>(T, U);
#[derive(Reflect)]
struct Baz {
a: Foo<i32, i32>,
b: Foo<u32, u32>,
}
assert_impl_all!(Foo<i32, i32>: Reflect);
assert_not_impl_all!(Foo<i32, usize>: Reflect);
}
#[test]
fn should_allow_custom_where_with_assoc_type() {
trait Trait {
type Assoc;
}
#[derive(Reflect)]
#[reflect(where T::Assoc: core::fmt::Display)]
struct Foo<T: Trait>(T::Assoc);
#[derive(TypePath)]
struct Bar;
impl Trait for Bar {
type Assoc = usize;
}
#[derive(TypePath)]
struct Baz;
impl Trait for Baz {
type Assoc = (f32, f32);
}
assert_impl_all!(Foo<Bar>: Reflect);
assert_not_impl_all!(Foo<Baz>: Reflect);
}
#[test]
fn should_allow_empty_enums() {
#[derive(Reflect)]
enum Empty {}
assert_impl_all!(Empty: Reflect);
}
#[test]
fn recursive_typed_storage_does_not_hang() {
#[derive(Reflect)]
struct Recurse<T>(T);
let _ = <Recurse<Recurse<()>> as Typed>::type_info();
let _ = <Recurse<Recurse<()>> as TypePath>::type_path();
#[derive(Reflect)]
#[reflect(no_field_bounds)]
struct SelfRecurse {
recurse: Vec<SelfRecurse>,
}
let _ = <SelfRecurse as Typed>::type_info();
let _ = <SelfRecurse as TypePath>::type_path();
#[derive(Reflect)]
#[reflect(no_field_bounds)]
enum RecurseA {
Recurse(RecurseB),
}
#[derive(Reflect)]
struct RecurseB {
vector: Vec<RecurseA>,
}
let _ = <RecurseA as Typed>::type_info();
let _ = <RecurseA as TypePath>::type_path();
let _ = <RecurseB as Typed>::type_info();
let _ = <RecurseB as TypePath>::type_path();
}
#[test]
fn recursive_registration_does_not_hang() {
#[derive(Reflect)]
struct Recurse<T>(T);
let mut registry = TypeRegistry::empty();
registry.register::<Recurse<Recurse<()>>>();
#[derive(Reflect)]
#[reflect(no_field_bounds)]
struct SelfRecurse {
recurse: Vec<SelfRecurse>,
}
registry.register::<SelfRecurse>();
#[derive(Reflect)]
#[reflect(no_field_bounds)]
enum RecurseA {
Recurse(RecurseB),
}
#[derive(Reflect)]
struct RecurseB {
vector: Vec<RecurseA>,
}
registry.register::<RecurseA>();
assert!(registry.contains(TypeId::of::<RecurseA>()));
assert!(registry.contains(TypeId::of::<RecurseB>()));
}
#[test]
fn can_opt_out_type_path() {
#[derive(Reflect)]
#[reflect(type_path = false)]
struct Foo<T> {
#[reflect(ignore)]
_marker: PhantomData<T>,
}
struct NotTypePath;
impl<T: 'static> TypePath for Foo<T> {
fn type_path() -> &'static str {
core::any::type_name::<Self>()
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| ShortName::of::<Self>().to_string())
}
fn type_ident() -> Option<&'static str> {
Some("Foo")
}
fn crate_name() -> Option<&'static str> {
Some("bevy_reflect")
}
fn module_path() -> Option<&'static str> {
Some("bevy_reflect::tests")
}
}
let path = <Foo<NotTypePath> as TypePath>::type_path();
assert_eq!("bevy_reflect::tests::can_opt_out_type_path::Foo<bevy_reflect::tests::can_opt_out_type_path::NotTypePath>", path);
let mut registry = TypeRegistry::default();
registry.register::<Foo<NotTypePath>>();
let registration = registry.get(TypeId::of::<Foo<NotTypePath>>()).unwrap();
assert_eq!(
"Foo<NotTypePath>",
registration.type_info().type_path_table().short_path()
);
}
#[test]
fn dynamic_types_debug_format() {
#[derive(Debug, Reflect)]
struct TestTupleStruct(u32);
#[derive(Debug, Reflect)]
enum TestEnum {
A(u32),
B,
}
#[derive(Debug, Reflect)]
struct TestStruct {
tuple: (u32, u32),
tuple_struct: TestTupleStruct,
list: Vec<u32>,
array: [u32; 3],
e: TestEnum,
map: HashMap<u32, u32>,
value: u32,
}
let mut map = <HashMap<_, _>>::default();
map.insert(9, 10);
let mut test_struct: DynamicStruct = TestStruct {
tuple: (0, 1),
list: vec![2, 3, 4],
array: [5, 6, 7],
tuple_struct: TestTupleStruct(8),
e: TestEnum::A(11),
map,
value: 12,
}
.to_dynamic_struct();
let mut test_unknown_struct = DynamicStruct::default();
test_unknown_struct.insert("a", 13);
test_struct.insert("unknown_struct", test_unknown_struct);
let mut test_unknown_tuple_struct = DynamicTupleStruct::default();
test_unknown_tuple_struct.insert(14);
test_struct.insert("unknown_tuplestruct", test_unknown_tuple_struct);
assert_eq!(
format!("{test_struct:?}"),
"DynamicStruct(bevy_reflect::tests::TestStruct { \
tuple: DynamicTuple((0, 1)), \
tuple_struct: DynamicTupleStruct(bevy_reflect::tests::TestTupleStruct(8)), \
list: DynamicList([2, 3, 4]), \
array: DynamicArray([5, 6, 7]), \
e: DynamicEnum(A(11)), \
map: DynamicMap({9: 10}), \
value: 12, \
unknown_struct: DynamicStruct(_ { a: 13 }), \
unknown_tuplestruct: DynamicTupleStruct(_(14)) \
})"
);
}
#[test]
fn assert_impl_reflect_macro_on_all() {
struct Struct {
foo: (),
}
struct TupleStruct(());
enum Enum {
Foo { foo: () },
Bar(()),
}
impl_reflect!(
#[type_path = "my_crate::foo"]
struct Struct {
foo: (),
}
);
impl_reflect!(
#[type_path = "my_crate::foo"]
struct TupleStruct(());
);
impl_reflect!(
#[type_path = "my_crate::foo"]
enum Enum {
Foo { foo: () },
Bar(()),
}
);
assert_impl_all!(Struct: Reflect);
assert_impl_all!(TupleStruct: Reflect);
assert_impl_all!(Enum: Reflect);
}
#[test]
fn should_reflect_remote_type() {
mod external_crate {
use alloc::string::String;
#[derive(Debug, Default)]
pub struct TheirType {
pub value: String,
}
}
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug, Default)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
}
let mut patch = DynamicStruct::default();
patch.set_represented_type(Some(MyType::type_info()));
patch.insert("value", "Goodbye".to_string());
let mut data = MyType(external_crate::TheirType {
value: "Hello".to_string(),
});
assert_eq!("Hello", data.0.value);
data.apply(&patch);
assert_eq!("Goodbye", data.0.value);
#[derive(Reflect, Debug)]
#[reflect(from_reflect = false)]
struct ContainerStruct {
#[reflect(remote = MyType)]
their_type: external_crate::TheirType,
}
let mut patch = DynamicStruct::default();
patch.set_represented_type(Some(ContainerStruct::type_info()));
patch.insert(
"their_type",
MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
}),
);
let mut data = ContainerStruct {
their_type: external_crate::TheirType {
value: "Hello".to_string(),
},
};
assert_eq!("Hello", data.their_type.value);
data.apply(&patch);
assert_eq!("Goodbye", data.their_type.value);
#[derive(Reflect, Debug)]
struct ContainerTupleStruct(#[reflect(remote = MyType)] external_crate::TheirType);
let mut patch = DynamicTupleStruct::default();
patch.set_represented_type(Some(ContainerTupleStruct::type_info()));
patch.insert(MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
}));
let mut data = ContainerTupleStruct(external_crate::TheirType {
value: "Hello".to_string(),
});
assert_eq!("Hello", data.0.value);
data.apply(&patch);
assert_eq!("Goodbye", data.0.value);
}
#[test]
fn should_reflect_remote_value_type() {
mod external_crate {
use alloc::string::String;
#[derive(Clone, Debug, Default)]
pub struct TheirType {
pub value: String,
}
}
#[reflect_remote(external_crate::TheirType)]
#[derive(Clone, Debug, Default)]
#[reflect(opaque)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
}
let mut data = MyType(external_crate::TheirType {
value: "Hello".to_string(),
});
let patch = MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
});
assert_eq!("Hello", data.0.value);
data.apply(&patch);
assert_eq!("Goodbye", data.0.value);
#[derive(Reflect, Debug)]
#[reflect(from_reflect = false)]
struct ContainerStruct {
#[reflect(remote = MyType)]
their_type: external_crate::TheirType,
}
let mut patch = DynamicStruct::default();
patch.set_represented_type(Some(ContainerStruct::type_info()));
patch.insert(
"their_type",
MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
}),
);
let mut data = ContainerStruct {
their_type: external_crate::TheirType {
value: "Hello".to_string(),
},
};
assert_eq!("Hello", data.their_type.value);
data.apply(&patch);
assert_eq!("Goodbye", data.their_type.value);
#[derive(Reflect, Debug)]
struct ContainerTupleStruct(#[reflect(remote = MyType)] external_crate::TheirType);
let mut patch = DynamicTupleStruct::default();
patch.set_represented_type(Some(ContainerTupleStruct::type_info()));
patch.insert(MyType(external_crate::TheirType {
value: "Goodbye".to_string(),
}));
let mut data = ContainerTupleStruct(external_crate::TheirType {
value: "Hello".to_string(),
});
assert_eq!("Hello", data.0.value);
data.apply(&patch);
assert_eq!("Goodbye", data.0.value);
}
#[test]
fn should_reflect_remote_type_from_module() {
mod wrapper {
use super::*;
pub mod external_crate {
use alloc::string::String;
pub struct TheirType {
pub value: String,
}
}
#[reflect_remote(external_crate::TheirType)]
pub struct MyType {
pub value: String,
}
}
#[derive(Reflect)]
struct ContainerStruct {
#[reflect(remote = wrapper::MyType)]
their_type: wrapper::external_crate::TheirType,
}
}
#[test]
fn should_reflect_remote_enum() {
mod external_crate {
use alloc::string::String;
#[derive(Debug, PartialEq, Eq)]
pub enum TheirType {
Unit,
Tuple(usize),
Struct { value: String },
}
}
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug)]
#[reflect(Debug)]
enum MyType {
Unit,
Tuple(usize),
Struct { value: String },
}
let mut patch = DynamicEnum::from(MyType(external_crate::TheirType::Tuple(123)));
let mut data = MyType(external_crate::TheirType::Unit);
assert_eq!(external_crate::TheirType::Unit, data.0);
data.apply(&patch);
assert_eq!(external_crate::TheirType::Tuple(123), data.0);
patch = DynamicEnum::from(MyType(external_crate::TheirType::Struct {
value: "Hello world!".to_string(),
}));
data.apply(&patch);
assert_eq!(
external_crate::TheirType::Struct {
value: "Hello world!".to_string()
},
data.0
);
#[derive(Reflect, Debug, PartialEq)]
enum ContainerEnum {
Foo,
Bar {
#[reflect(remote = MyType)]
their_type: external_crate::TheirType,
},
}
let patch = DynamicEnum::from(ContainerEnum::Bar {
their_type: external_crate::TheirType::Tuple(123),
});
let mut data = ContainerEnum::Foo;
assert_eq!(ContainerEnum::Foo, data);
data.apply(&patch);
assert_eq!(
ContainerEnum::Bar {
their_type: external_crate::TheirType::Tuple(123)
},
data
);
}
#[test]
fn should_reflect_nested_remote_type() {
mod external_crate {
pub struct TheirOuter<T> {
pub a: TheirInner<T>,
pub b: TheirInner<bool>,
}
pub struct TheirInner<T>(pub T);
}
#[reflect_remote(external_crate::TheirOuter<T>)]
struct MyOuter<T: FromReflect + Reflectable> {
#[reflect(remote = MyInner<T>)]
pub a: external_crate::TheirInner<T>,
#[reflect(remote = MyInner<bool>)]
pub b: external_crate::TheirInner<bool>,
}
#[reflect_remote(external_crate::TheirInner<T>)]
struct MyInner<T: FromReflect>(pub T);
let mut patch = DynamicStruct::default();
patch.set_represented_type(Some(MyOuter::<i32>::type_info()));
patch.insert("a", MyInner(external_crate::TheirInner(321_i32)));
patch.insert("b", MyInner(external_crate::TheirInner(true)));
let mut data = MyOuter(external_crate::TheirOuter {
a: external_crate::TheirInner(123_i32),
b: external_crate::TheirInner(false),
});
assert_eq!(123, data.0.a.0);
assert!(!data.0.b.0);
data.apply(&patch);
assert_eq!(321, data.0.a.0);
assert!(data.0.b.0);
}
#[test]
fn should_reflect_nested_remote_enum() {
mod external_crate {
use core::fmt::Debug;
#[derive(Debug)]
pub enum TheirOuter<T: Debug> {
Unit,
Tuple(TheirInner<T>),
Struct { value: TheirInner<T> },
}
#[derive(Debug)]
pub enum TheirInner<T: Debug> {
Unit,
Tuple(T),
Struct { value: T },
}
}
#[reflect_remote(external_crate::TheirOuter<T>)]
#[derive(Debug)]
enum MyOuter<T: FromReflect + Reflectable + Debug> {
Unit,
Tuple(#[reflect(remote = MyInner<T>)] external_crate::TheirInner<T>),
Struct {
#[reflect(remote = MyInner<T>)]
value: external_crate::TheirInner<T>,
},
}
#[reflect_remote(external_crate::TheirInner<T>)]
#[derive(Debug)]
enum MyInner<T: FromReflect + Debug> {
Unit,
Tuple(T),
Struct { value: T },
}
let mut patch = DynamicEnum::default();
let mut value = DynamicStruct::default();
value.insert("value", MyInner(external_crate::TheirInner::Tuple(123)));
patch.set_variant("Struct", value);
let mut data = MyOuter(external_crate::TheirOuter::<i32>::Unit);
assert!(matches!(
data,
MyOuter(external_crate::TheirOuter::<i32>::Unit)
));
data.apply(&patch);
assert!(matches!(
data,
MyOuter(external_crate::TheirOuter::Struct {
value: external_crate::TheirInner::Tuple(123)
})
));
}
#[test]
fn should_take_remote_type() {
mod external_crate {
use alloc::string::String;
#[derive(Debug, Default, PartialEq, Eq)]
pub struct TheirType {
pub value: String,
}
}
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug, Default)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
}
let input: Box<dyn Reflect> = Box::new(MyType(external_crate::TheirType {
value: "Hello".to_string(),
}));
let output: external_crate::TheirType = input
.take()
.expect("should downcast to `external_crate::TheirType`");
assert_eq!(
external_crate::TheirType {
value: "Hello".to_string(),
},
output
);
}
#[test]
fn should_try_take_remote_type() {
mod external_crate {
use alloc::string::String;
#[derive(Debug, Default, PartialEq, Eq)]
pub struct TheirType {
pub value: String,
}
}
#[reflect_remote(external_crate::TheirType)]
#[derive(Debug, Default)]
#[reflect(Debug, Default)]
struct MyType {
pub value: String,
}
let input: Box<dyn PartialReflect> = Box::new(MyType(external_crate::TheirType {
value: "Hello".to_string(),
}));
let output: external_crate::TheirType = input
.try_take()
.expect("should downcast to `external_crate::TheirType`");
assert_eq!(
external_crate::TheirType {
value: "Hello".to_string(),
},
output,
);
}
#[test]
fn should_take_nested_remote_type() {
mod external_crate {
#[derive(PartialEq, Eq, Debug)]
pub struct TheirOuter<T> {
pub inner: TheirInner<T>,
}
#[derive(PartialEq, Eq, Debug)]
pub struct TheirInner<T>(pub T);
}
#[reflect_remote(external_crate::TheirOuter<T>)]
struct MyOuter<T: FromReflect + Reflectable> {
#[reflect(remote = MyInner<T>)]
pub inner: external_crate::TheirInner<T>,
}
#[reflect_remote(external_crate::TheirInner<T>)]
struct MyInner<T: FromReflect>(pub T);
let input: Box<dyn Reflect> = Box::new(MyOuter(external_crate::TheirOuter {
inner: external_crate::TheirInner(123),
}));
let output: external_crate::TheirOuter<i32> = input
.take()
.expect("should downcast to `external_crate::TheirOuter`");
assert_eq!(
external_crate::TheirOuter {
inner: external_crate::TheirInner(123),
},
output
);
}
#[test]
fn should_serialize_opaque_remote_type() {
mod external_crate {
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Vector2<T>(pub [T; 2]);
}
#[reflect_remote(external_crate::Vector2<i32>)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[reflect(Serialize, Deserialize)]
#[reflect(opaque)]
struct Vector2Wrapper([i32; 2]);
#[derive(Reflect, Debug, PartialEq)]
struct Point(#[reflect(remote = Vector2Wrapper)] external_crate::Vector2<i32>);
let point = Point(external_crate::Vector2([1, 2]));
let mut registry = TypeRegistry::new();
registry.register::<Point>();
registry.register::<Vector2Wrapper>();
let serializer = ReflectSerializer::new(&point, ®istry);
let serialized = ron::to_string(&serializer).unwrap();
assert_eq!(serialized, r#"{"bevy_reflect::tests::Point":((((1,2))))}"#);
let mut deserializer = Deserializer::from_str(&serialized).unwrap();
let reflect_deserializer = ReflectDeserializer::new(®istry);
let deserialized = reflect_deserializer.deserialize(&mut deserializer).unwrap();
let point = <Point as FromReflect>::from_reflect(&*deserialized).unwrap();
assert_eq!(point, Point(external_crate::Vector2([1, 2])));
}
#[cfg(feature = "auto_register")]
mod auto_register_reflect {
use super::*;
#[test]
fn should_ignore_auto_reflect_registration() {
#[derive(Reflect)]
#[reflect(no_auto_register)]
struct NoAutomaticStruct {
a: usize,
}
let mut registry = TypeRegistry::default();
registry.register_derived_types();
assert!(!registry.contains(TypeId::of::<NoAutomaticStruct>()));
}
#[test]
fn should_auto_register_reflect_for_all_supported_types() {
#[derive(Reflect)]
struct StructReflect {
a: usize,
}
#[derive(Reflect)]
struct ZSTStructReflect;
#[derive(Reflect)]
struct TupleStructReflect(pub u32);
#[derive(Reflect)]
enum EnumReflect {
A,
B,
}
#[derive(Reflect)]
enum ZSTEnumReflect {}
#[derive(Reflect, Clone)]
#[reflect(opaque)]
struct OpaqueStructReflect {
_a: usize,
}
#[derive(Reflect, Clone)]
#[reflect(opaque)]
struct ZSTOpaqueStructReflect;
let mut registry = TypeRegistry::default();
registry.register_derived_types();
assert!(registry.contains(TypeId::of::<StructReflect>()));
assert!(registry.contains(TypeId::of::<ZSTStructReflect>()));
assert!(registry.contains(TypeId::of::<TupleStructReflect>()));
assert!(registry.contains(TypeId::of::<EnumReflect>()));
assert!(registry.contains(TypeId::of::<ZSTEnumReflect>()));
assert!(registry.contains(TypeId::of::<OpaqueStructReflect>()));
assert!(registry.contains(TypeId::of::<ZSTOpaqueStructReflect>()));
}
}
#[cfg(feature = "glam")]
mod glam {
use super::*;
use ::glam::{quat, vec3, Quat, Vec3};
#[test]
fn quat_serialization() {
let q = quat(1.0, 2.0, 3.0, 4.0);
let mut registry = TypeRegistry::default();
registry.register::<f32>();
registry.register::<Quat>();
let ser = ReflectSerializer::new(&q, ®istry);
let config = PrettyConfig::default()
.new_line(String::from("\n"))
.indentor(String::from(" "));
let output = to_string_pretty(&ser, config).unwrap();
let expected = r#"
{
"glam::Quat": (1.0, 2.0, 3.0, 4.0),
}"#;
assert_eq!(expected, format!("\n{output}"));
}
#[test]
fn quat_deserialization() {
let data = r#"
{
"glam::Quat": (1.0, 2.0, 3.0, 4.0),
}"#;
let mut registry = TypeRegistry::default();
registry.register::<Quat>();
registry.register::<f32>();
let de = ReflectDeserializer::new(®istry);
let mut deserializer =
Deserializer::from_str(data).expect("Failed to acquire deserializer");
let dynamic_struct = de
.deserialize(&mut deserializer)
.expect("Failed to deserialize");
let mut result = Quat::default();
result.apply(dynamic_struct.as_partial_reflect());
assert_eq!(result, quat(1.0, 2.0, 3.0, 4.0));
}
#[test]
fn vec3_serialization() {
let v = vec3(12.0, 3.0, -6.9);
let mut registry = TypeRegistry::default();
registry.register::<f32>();
registry.register::<Vec3>();
let ser = ReflectSerializer::new(&v, ®istry);
let config = PrettyConfig::default()
.new_line(String::from("\n"))
.indentor(String::from(" "));
let output = to_string_pretty(&ser, config).unwrap();
let expected = r#"
{
"glam::Vec3": (12.0, 3.0, -6.9),
}"#;
assert_eq!(expected, format!("\n{output}"));
}
#[test]
fn vec3_deserialization() {
let data = r#"
{
"glam::Vec3": (12.0, 3.0, -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 =
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.as_partial_reflect());
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.reflect_path("x")
.unwrap()
.try_downcast_ref::<f32>()
.unwrap(),
1.0
);
*v.reflect_path_mut("y")
.unwrap()
.try_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));
}
}
}