mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 20:53:53 +00:00
Add macro to implement reflect for struct types and migrate glam types (#4540)
# Objective Relevant issue: #4474 Currently glam types implement Reflect as a value, which is problematic for reflection, making scripting/editor work much more difficult. This PR re-implements them as structs. ## Solution Added a new proc macro, `impl_reflect_struct`, which replaces `impl_reflect_value` and `impl_from_reflect_value` for glam types. This macro could also be used for other types, but I don't know of any that would require it. It's specifically useful for foreign types that cannot derive Reflect normally. --- ## Changelog ### Added - `impl_reflect_struct` proc macro ### Changed - Glam reflect impls have been replaced with `impl_reflect_struct` - from_reflect's `impl_struct` altered to take an optional custom constructor, allowing non-default non-constructible foreign types to use it - Calls to `impl_struct` altered to conform to new signature - Altered glam types (All vec/mat combinations) have a different serialization structure, as they are reflected differently now. ## Migration Guide This will break altered glam types serialized to RON scenes, as they will expect to be serialized/deserialized as structs rather than values now. A future PR to add custom serialization for non-value types is likely on the way to restore previous behavior. Additionally, calls to `impl_struct` must add a `None` parameter to the end of the call to restore previous behavior. Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
This commit is contained in:
parent
38a940dbbe
commit
aced6aff04
4 changed files with 470 additions and 50 deletions
|
@ -8,6 +8,7 @@ pub fn impl_struct(
|
|||
bevy_reflect_path: &Path,
|
||||
active_fields: &[(&Field, usize)],
|
||||
ignored_fields: &[(&Field, usize)],
|
||||
custom_constructor: Option<proc_macro2::TokenStream>,
|
||||
) -> TokenStream {
|
||||
let field_names = active_fields
|
||||
.iter()
|
||||
|
@ -60,20 +61,35 @@ pub fn impl_struct(
|
|||
#(#field_types: #bevy_reflect_path::FromReflect,)*
|
||||
});
|
||||
|
||||
let constructor = if let Some(constructor) = custom_constructor {
|
||||
quote!(
|
||||
let mut value: Self = #constructor;
|
||||
#(
|
||||
value.#field_idents = {
|
||||
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)?
|
||||
};
|
||||
)*
|
||||
Some(value)
|
||||
)
|
||||
} else {
|
||||
quote!(
|
||||
Some(
|
||||
Self {
|
||||
#(#field_idents: {
|
||||
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)?
|
||||
},)*
|
||||
#(#ignored_field_idents: Default::default(),)*
|
||||
}
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
TokenStream::from(quote! {
|
||||
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
|
||||
{
|
||||
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
|
||||
use #bevy_reflect_path::Struct;
|
||||
if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() {
|
||||
Some(
|
||||
Self{
|
||||
#(#field_idents: {
|
||||
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_struct.field(#field_names)?)?
|
||||
},)*
|
||||
#(#ignored_field_idents: Default::default(),)*
|
||||
}
|
||||
)
|
||||
#constructor
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -287,8 +287,7 @@ fn impl_struct(
|
|||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn #bevy_reflect_path::Reflect> {
|
||||
use #bevy_reflect_path::Struct;
|
||||
Box::new(self.clone_dynamic())
|
||||
Box::new(#bevy_reflect_path::Struct::clone_dynamic(self))
|
||||
}
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||
|
@ -298,11 +297,10 @@ fn impl_struct(
|
|||
|
||||
#[inline]
|
||||
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
|
||||
use #bevy_reflect_path::Struct;
|
||||
if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() {
|
||||
for (i, value) in struct_value.iter_fields().enumerate() {
|
||||
let name = struct_value.name_at(i).unwrap();
|
||||
self.field_mut(name).map(|v| v.apply(value));
|
||||
#bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value));
|
||||
}
|
||||
} else {
|
||||
panic!("Attempted to apply non-struct type to struct type.");
|
||||
|
@ -607,6 +605,204 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
|
|||
)
|
||||
}
|
||||
|
||||
/// Represents the information needed to implement a type as a Reflect Struct.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// impl_reflect_struct!(
|
||||
/// // attrs
|
||||
/// // |----------------------------------------|
|
||||
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
/// // type_name generics
|
||||
/// // |-------------------||----------|
|
||||
/// struct ThingThatImReflecting<T1, T2, T3> {
|
||||
/// x: T1, // |
|
||||
/// y: T2, // |- fields
|
||||
/// z: T3 // |
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
struct ReflectStructDef {
|
||||
type_name: Ident,
|
||||
generics: Generics,
|
||||
attrs: ReflectAttrs,
|
||||
fields: Fields,
|
||||
}
|
||||
|
||||
impl Parse for ReflectStructDef {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let ast = input.parse::<DeriveInput>()?;
|
||||
|
||||
let type_name = ast.ident;
|
||||
let generics = ast.generics;
|
||||
let fields = match ast.data {
|
||||
Data::Struct(data) => data.fields,
|
||||
Data::Enum(data) => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
data.enum_token,
|
||||
"Enums are not currently supported for reflection",
|
||||
))
|
||||
}
|
||||
Data::Union(data) => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
data.union_token,
|
||||
"Unions are not supported for reflection",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let mut attrs = ReflectAttrs::default();
|
||||
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
|
||||
let meta_list = if let Meta::List(meta_list) = attribute {
|
||||
meta_list
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(ident) = meta_list.path.get_ident() {
|
||||
if ident == REFLECT_ATTRIBUTE_NAME || ident == REFLECT_VALUE_ATTRIBUTE_NAME {
|
||||
attrs = ReflectAttrs::from_nested_metas(&meta_list.nested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
type_name,
|
||||
generics,
|
||||
attrs,
|
||||
fields,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A replacement for `#[derive(Reflect)]` to be used with foreign types which
|
||||
/// the definitions of cannot be altered.
|
||||
///
|
||||
/// This macro is an alternative to [`impl_reflect_value!`] and [`impl_from_reflect_value!`]
|
||||
/// which implement foreign types as Value types. Note that there is no `impl_from_reflect_struct`,
|
||||
/// as this macro will do the job of both. This macro implements them as `Struct` types,
|
||||
/// which have greater functionality. The type being reflected must be in scope, as you cannot
|
||||
/// qualify it in the macro as e.g. `bevy::prelude::Vec3`.
|
||||
///
|
||||
/// It may be necessary to add `#[reflect(Default)]` for some types, specifically non-constructible
|
||||
/// foreign types. Without `Default` reflected for such types, you will usually get an arcane
|
||||
/// error message and fail to compile. If the type does not implement `Default`, it may not
|
||||
/// be possible to reflect without extending the macro.
|
||||
///
|
||||
/// # Example
|
||||
/// Implementing `Reflect` for `bevy::prelude::Vec3` as a struct type:
|
||||
/// ```ignore
|
||||
/// use bevy::prelude::Vec3;
|
||||
///
|
||||
/// impl_reflect_struct!(
|
||||
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
/// struct Vec3 {
|
||||
/// x: f32,
|
||||
/// y: f32,
|
||||
/// z: f32
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
|
||||
let ReflectStructDef {
|
||||
type_name,
|
||||
generics,
|
||||
attrs,
|
||||
fields,
|
||||
} = parse_macro_input!(input as ReflectStructDef);
|
||||
|
||||
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
|
||||
|
||||
let fields_and_args = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, f)| {
|
||||
(
|
||||
f,
|
||||
f.attrs
|
||||
.iter()
|
||||
.find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME)
|
||||
.map(|a| {
|
||||
syn::custom_keyword!(ignore);
|
||||
let mut attribute_args = PropAttributeArgs { ignore: None };
|
||||
a.parse_args_with(|input: ParseStream| {
|
||||
if input.parse::<Option<ignore>>()?.is_some() {
|
||||
attribute_args.ignore = Some(true);
|
||||
return Ok(());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.expect("Invalid 'property' attribute format.");
|
||||
|
||||
attribute_args
|
||||
}),
|
||||
i,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(&Field, Option<PropAttributeArgs>, usize)>>();
|
||||
let active_fields = fields_and_args
|
||||
.iter()
|
||||
.filter(|(_field, attrs, _i)| {
|
||||
attrs.is_none()
|
||||
|| match attrs.as_ref().unwrap().ignore {
|
||||
Some(ignore) => !ignore,
|
||||
None => true,
|
||||
}
|
||||
})
|
||||
.map(|(f, _attr, i)| (*f, *i))
|
||||
.collect::<Vec<(&Field, usize)>>();
|
||||
let ignored_fields = fields_and_args
|
||||
.iter()
|
||||
.filter(|(_field, attrs, _i)| {
|
||||
attrs
|
||||
.as_ref()
|
||||
.map(|attrs| attrs.ignore.unwrap_or(false))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.map(|(f, _attr, i)| (*f, *i))
|
||||
.collect::<Vec<(&Field, usize)>>();
|
||||
|
||||
let constructor = if attrs
|
||||
.data
|
||||
.contains(&Ident::new("ReflectDefault", Span::call_site()))
|
||||
{
|
||||
Some(quote! { Default::default() })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let registration_data = &attrs.data;
|
||||
let get_type_registration_impl =
|
||||
impl_get_type_registration(&type_name, &bevy_reflect_path, registration_data, &generics);
|
||||
|
||||
let impl_struct: proc_macro2::TokenStream = impl_struct(
|
||||
&type_name,
|
||||
&generics,
|
||||
&get_type_registration_impl,
|
||||
&bevy_reflect_path,
|
||||
&attrs,
|
||||
&active_fields,
|
||||
)
|
||||
.into();
|
||||
|
||||
let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(
|
||||
&type_name,
|
||||
&generics,
|
||||
&bevy_reflect_path,
|
||||
&active_fields,
|
||||
&ignored_fields,
|
||||
constructor,
|
||||
)
|
||||
.into();
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#impl_struct
|
||||
|
||||
#impl_from_struct
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ReflectAttrs {
|
||||
reflect_hash: TraitImpl,
|
||||
|
@ -862,6 +1058,7 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
|
|||
&bevy_reflect_path,
|
||||
&active_fields,
|
||||
&ignored_fields,
|
||||
None,
|
||||
),
|
||||
DeriveType::TupleStruct => from_reflect::impl_tuple_struct(
|
||||
type_name,
|
||||
|
|
|
@ -1,44 +1,160 @@
|
|||
use crate as bevy_reflect;
|
||||
use crate::prelude::ReflectDefault;
|
||||
use crate::reflect::Reflect;
|
||||
use crate::ReflectDeserialize;
|
||||
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
|
||||
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_struct, impl_reflect_value};
|
||||
use glam::*;
|
||||
|
||||
impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(IVec3(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(IVec4(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(UVec2(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(UVec3(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(UVec4(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(Vec2(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(Vec3(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(Vec3A(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(DVec2(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(DVec3(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(DVec4(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(DMat3(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(DMat4(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_value!(DQuat(PartialEq, Serialize, Deserialize));
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct IVec2 {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct IVec3 {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct IVec4 {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
w: i32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct UVec2 {
|
||||
x: u32,
|
||||
y: u32,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct UVec3 {
|
||||
x: u32,
|
||||
y: u32,
|
||||
z: u32,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct UVec4 {
|
||||
x: u32,
|
||||
y: u32,
|
||||
z: u32,
|
||||
w: u32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct Vec2 {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct Vec3 {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct Vec3A {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct Vec4 {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
w: f32,
|
||||
}
|
||||
);
|
||||
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct DVec2 {
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct DVec3 {
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct DVec4 {
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
w: f64,
|
||||
}
|
||||
);
|
||||
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct Mat3 {
|
||||
x_axis: Vec3,
|
||||
y_axis: Vec3,
|
||||
z_axis: Vec3,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct Mat4 {
|
||||
x_axis: Vec4,
|
||||
y_axis: Vec4,
|
||||
z_axis: Vec4,
|
||||
w_axis: Vec4,
|
||||
}
|
||||
);
|
||||
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct DMat3 {
|
||||
x_axis: DVec3,
|
||||
y_axis: DVec3,
|
||||
z_axis: DVec3,
|
||||
}
|
||||
);
|
||||
impl_reflect_struct!(
|
||||
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
struct DMat4 {
|
||||
x_axis: DVec4,
|
||||
y_axis: DVec4,
|
||||
z_axis: DVec4,
|
||||
w_axis: DVec4,
|
||||
}
|
||||
);
|
||||
|
||||
// Quat fields are read-only (as of now), and reflection is currently missing
|
||||
// mechanisms for read-only fields. I doubt those mechanisms would be added,
|
||||
// so for now quaternions will remain as values. They are represented identically
|
||||
// to Vec4 and DVec4, so you may use those instead and convert between.
|
||||
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize, Default));
|
||||
impl_reflect_value!(DQuat(PartialEq, Serialize, Deserialize, Default));
|
||||
|
||||
impl_from_reflect_value!(IVec2);
|
||||
impl_from_reflect_value!(IVec3);
|
||||
impl_from_reflect_value!(IVec4);
|
||||
impl_from_reflect_value!(UVec2);
|
||||
impl_from_reflect_value!(UVec3);
|
||||
impl_from_reflect_value!(UVec4);
|
||||
impl_from_reflect_value!(Vec2);
|
||||
impl_from_reflect_value!(Vec3);
|
||||
impl_from_reflect_value!(Vec4);
|
||||
impl_from_reflect_value!(Vec3A);
|
||||
impl_from_reflect_value!(DVec2);
|
||||
impl_from_reflect_value!(DVec3);
|
||||
impl_from_reflect_value!(DVec4);
|
||||
impl_from_reflect_value!(Mat3);
|
||||
impl_from_reflect_value!(Mat4);
|
||||
impl_from_reflect_value!(Quat);
|
||||
impl_from_reflect_value!(DMat3);
|
||||
impl_from_reflect_value!(DMat4);
|
||||
impl_from_reflect_value!(DQuat);
|
||||
|
|
|
@ -82,7 +82,10 @@ pub mod __macro_exports {
|
|||
#[cfg(test)]
|
||||
#[allow(clippy::blacklisted_name, clippy::approx_constant)]
|
||||
mod tests {
|
||||
#[cfg(feature = "glam")]
|
||||
use ::glam::{vec3, Vec3};
|
||||
use ::serde::de::DeserializeSeed;
|
||||
use ::serde::Serialize;
|
||||
use bevy_utils::HashMap;
|
||||
use ron::{
|
||||
ser::{to_string_pretty, PrettyConfig},
|
||||
|
@ -471,4 +474,92 @@ mod tests {
|
|||
// Should compile:
|
||||
let _ = trait_object.as_reflect();
|
||||
}
|
||||
|
||||
#[cfg(feature = "glam")]
|
||||
mod glam {
|
||||
use super::*;
|
||||
|
||||
#[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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue