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
					
				| @ -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,)* | ||||
|     }); | ||||
| 
 | ||||
|     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() { | ||||
|     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(ref_struct.field(#field_names)?)? | ||||
|                         <#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> { | ||||
|                 if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() { | ||||
|                     #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 New Issue
	
	Block a user
	 PROMETHIA-27
						PROMETHIA-27