From d2d02f63f6ee8c10aec17b81a07e412a415f3efa Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 26 May 2020 19:47:33 -0700 Subject: [PATCH] props: "Seq" properties --- .vscode/launch.json | 6 +- .vscode/settings.json | 3 +- crates/bevy_asset/src/handle.rs | 25 +- crates/bevy_core/src/lib.rs | 13 +- .../bevy_property_derive/src/lib.rs | 35 +- .../bevy_property/src/dynamic_properties.rs | 442 +++++++++++++----- .../bevy_property/src/impl_property_glam.rs | 1 - .../bevy_property/src/impl_property_legion.rs | 1 - .../src/impl_property_smallvec.rs | 5 + crates/bevy_property/src/impl_property_std.rs | 150 +++++- crates/bevy_property/src/properties.rs | 36 +- crates/bevy_property/src/property.rs | 75 ++- crates/bevy_property/src/type_registry.rs | 20 +- crates/bevy_render/src/lib.rs | 3 +- crates/ron/src/de/mod.rs | 9 +- examples/scene/properties.rs | 45 +- 16 files changed, 656 insertions(+), 213 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 32dc4faa6c..59f5d46f6b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1104,15 +1104,15 @@ { "type": "lldb", "request": "launch", - "name": "Debug example 'scene'", + "name": "Debug example '3d_scene'", "cargo": { "args": [ "build", - "--example=scene", + "--example=3d_scene", "--package=bevy" ], "filter": { - "name": "scene", + "name": "3d_scene", "kind": "example" } }, diff --git a/.vscode/settings.json b/.vscode/settings.json index ebc81e8919..e2635a6f34 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,5 +16,6 @@ "rspirv", "rustc", "spirv" - ] + ], + "rust-analyzer.checkOnSave.extraArgs": ["--target-dir", "target/rust-analyzer"], } \ No newline at end of file diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index f042ea92e5..2766073b38 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::{any::TypeId, marker::PhantomData}; use uuid::Uuid; -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, Property)] pub struct HandleId(pub Uuid); pub const DEFAULT_HANDLE_ID: HandleId = HandleId(Uuid::from_u128(240940089166493627844978703213080810552)); @@ -19,29 +19,6 @@ impl HandleId { } } -impl Property for HandleId { - fn any(&self) -> &dyn std::any::Any { - self - } - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - fn clone_prop(&self) -> Box { - Box::new(self.clone()) - } - fn set(&mut self, value: &dyn Property) { - let value = value.any(); - if let Some(prop) = value.downcast_ref::() { - *self = *prop; - } else { - panic!("prop value is not {}", std::any::type_name::()); - } - } - fn apply(&mut self, value: &dyn Property) { - self.set(value); - } -} - #[derive(Properties)] pub struct Handle where diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index a04c6c6d7f..dd4eb1513d 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -4,7 +4,13 @@ pub mod transform; use bevy_app::{stage, AppBuilder, AppPlugin}; use bevy_component_registry::RegisterComponent; -use bevy_transform::{transform_system_bundle, components::{Children, LocalToParent, LocalToWorld, Translation, Rotation, Scale, NonUniformScale}}; +use bevy_transform::{ + components::{ + Children, LocalToParent, LocalToWorld, NonUniformScale, Rotation, Scale, Translation, + }, + transform_system_bundle, +}; +use glam::{Mat3, Mat4, Quat, Vec2, Vec3}; use legion::prelude::IntoSystem; use time::{start_timer_system, stop_timer_system, Time}; @@ -25,6 +31,11 @@ impl AppPlugin for CorePlugin { .register_component::() .register_component::() .register_component::() + .register_property_type::() + .register_property_type::() + .register_property_type::() + .register_property_type::() + .register_property_type::() .add_system_to_stage(stage::FIRST, start_timer_system.system()) .add_system_to_stage(stage::LAST, stop_timer_system.system()); } diff --git a/crates/bevy_property/bevy_property_derive/src/lib.rs b/crates/bevy_property/bevy_property_derive/src/lib.rs index 445cf920ad..f78f787b06 100644 --- a/crates/bevy_property/bevy_property_derive/src/lib.rs +++ b/crates/bevy_property/bevy_property_derive/src/lib.rs @@ -99,9 +99,6 @@ pub fn derive_properties(input: TokenStream) -> TokenStream { TokenStream::from(quote! { impl #impl_generics #bevy_property_path::Properties for #struct_name#ty_generics { - fn type_name(&self) -> &str { - std::any::type_name::() - } fn prop(&self, name: &str) -> Option<&dyn #bevy_property_path::Property> { match name { #(#field_names => Some(&self.#field_idents),)* @@ -144,6 +141,10 @@ pub fn derive_properties(input: TokenStream) -> TokenStream { fn iter_props(&self) -> #bevy_property_path::PropertyIter { #bevy_property_path::PropertyIter::new(self) } + + fn properties_type(&self) -> #bevy_property_path::PropertiesType { + #bevy_property_path::PropertiesType::Map + } } impl #impl_generics #bevy_property_path::serde::ser::Serialize for #struct_name#ty_generics { @@ -152,16 +153,17 @@ pub fn derive_properties(input: TokenStream) -> TokenStream { S: #bevy_property_path::serde::ser::Serializer, { use #bevy_property_path::serde::ser::SerializeMap; - let mut state = serializer.serialize_map(Some(self.prop_len()))?; - state.serialize_entry("type", self.type_name())?; - for (name, prop) in self.iter_props() { - state.serialize_entry(name, prop)?; - } - state.end() + use #bevy_property_path::serde::Serialize; + #bevy_property_path::MapSerializer::new(self).serialize(serializer) } } impl #impl_generics #bevy_property_path::Property for #struct_name#ty_generics { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn std::any::Any { self @@ -183,7 +185,15 @@ pub fn derive_properties(input: TokenStream) -> TokenStream { #[inline] fn apply(&mut self, value: &dyn #bevy_property_path::Property) { if let Some(properties) = value.as_properties() { - for (name, prop) in properties.iter_props() { + if properties.properties_type() != self.properties_type() { + panic!( + "Properties type mismatch. This type is {:?} but the applied type is {:?}", + self.properties_type(), + properties.properties_type() + ); + } + for (i, prop) in properties.iter_props().enumerate() { + let name = properties.prop_name(i).unwrap(); self.prop_mut(name).map(|p| p.apply(prop)); } } else { @@ -212,6 +222,11 @@ pub fn derive_property(input: TokenStream) -> TokenStream { TokenStream::from(quote! { impl #impl_generics #bevy_property_path::Property for #struct_name#ty_generics { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn std::any::Any { self diff --git a/crates/bevy_property/src/dynamic_properties.rs b/crates/bevy_property/src/dynamic_properties.rs index 6d7e940018..daa50f8306 100644 --- a/crates/bevy_property/src/dynamic_properties.rs +++ b/crates/bevy_property/src/dynamic_properties.rs @@ -1,52 +1,76 @@ use crate::{ - Properties, Property, PropertyIter, PropertyTypeRegistration, - PropertyTypeRegistry, PropertyVal, + Properties, PropertiesType, Property, PropertyIter, PropertyTypeRegistration, + PropertyTypeRegistry, }; +use de::SeqAccess; use serde::{ de::{self, DeserializeSeed, MapAccess, Visitor}, - ser::SerializeMap, + ser::{SerializeMap, SerializeSeq}, Serialize, }; use std::{any::Any, borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc}; -#[derive(Default)] pub struct DynamicProperties { pub type_name: String, - pub props: Vec<(Cow<'static, str>, Box)>, + pub props: Vec>, + pub prop_names: Vec>, pub prop_indices: HashMap, usize>, + pub properties_type: PropertiesType, } impl DynamicProperties { - fn push(&mut self, name: &str, prop: Box) { - let name: Cow<'static, str> = Cow::Owned(name.to_string()); - self.props.push((name.clone(), prop)); - self.prop_indices.insert(name, self.props.len()); + pub fn map() -> Self { + DynamicProperties { + type_name: Default::default(), + props: Default::default(), + prop_names: Default::default(), + prop_indices: Default::default(), + properties_type: PropertiesType::Map, + } + } + + pub fn seq() -> Self { + DynamicProperties { + type_name: Default::default(), + props: Default::default(), + prop_names: Default::default(), + prop_indices: Default::default(), + properties_type: PropertiesType::Seq, + } + } + + pub fn push(&mut self, prop: Box, name: Option<&str>) { + // TODO: validate map / seq operations + self.props.push(prop); + if let Some(name) = name { + let cow_name: Cow<'static, str> = Cow::Owned(name.to_string()); // moo + self.prop_names.push(cow_name.clone()); + self.prop_indices.insert(cow_name, self.props.len() - 1); + } } pub fn set(&mut self, name: &str, prop: T) { + // TODO: validate map / seq operations if let Some(index) = self.prop_indices.get(name) { - self.props[*index].1 = Box::new(prop); + self.props[*index] = Box::new(prop); } else { - self.push(name, Box::new(prop)); + self.push(Box::new(prop), Some(name)); } } pub fn set_box(&mut self, name: &str, prop: Box) { + // TODO: validate map / seq operations if let Some(index) = self.prop_indices.get(name) { - self.props[*index].1 = prop; + self.props[*index] = prop; } else { - self.push(name, prop); + self.push(prop, Some(name)); } } } impl Properties for DynamicProperties { - #[inline] - fn type_name(&self) -> &str { - &self.type_name - } #[inline] fn prop(&self, name: &str) -> Option<&dyn Property> { if let Some(index) = self.prop_indices.get(name) { - Some(&*self.props[*index].1) + Some(&*self.props[*index]) } else { None } @@ -55,7 +79,7 @@ impl Properties for DynamicProperties { #[inline] fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Property> { if let Some(index) = self.prop_indices.get(name) { - Some(&mut *self.props[*index].1) + Some(&mut *self.props[*index]) } else { None } @@ -63,17 +87,20 @@ impl Properties for DynamicProperties { #[inline] fn prop_with_index(&self, index: usize) -> Option<&dyn Property> { - self.props.get(index).map(|(_i, prop)| &**prop) + self.props.get(index).map(|prop| &**prop) } #[inline] fn prop_with_index_mut(&mut self, index: usize) -> Option<&mut dyn Property> { - self.props.get_mut(index).map(|(_i, prop)| &mut **prop) + self.props.get_mut(index).map(|prop| &mut **prop) } #[inline] fn prop_name(&self, index: usize) -> Option<&str> { - self.props.get(index).map(|(name, _)| name.as_ref()) + match self.properties_type { + PropertiesType::Seq => None, + PropertiesType::Map => self.prop_names.get(index).map(|name| name.as_ref()), + } } #[inline] @@ -87,6 +114,75 @@ impl Properties for DynamicProperties { index: 0, } } + + #[inline] + fn properties_type(&self) -> PropertiesType { + self.properties_type + } +} + +impl Property for DynamicProperties { + #[inline] + fn type_name(&self) -> &str { + &self.type_name + } + + #[inline] + fn any(&self) -> &dyn Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn Any { + self + } + #[inline] + fn clone_prop(&self) -> Box { + Box::new(self.to_dynamic()) + } + #[inline] + fn set(&mut self, value: &dyn Property) { + if let Some(properties) = value.as_properties() { + *self = properties.to_dynamic(); + } else { + panic!("attempted to apply non-Properties type to Properties type"); + } + } + + #[inline] + fn apply(&mut self, value: &dyn Property) { + if let Some(properties) = value.as_properties() { + if properties.properties_type() != self.properties_type { + panic!( + "Properties type mismatch. This type is {:?} but the applied type is {:?}", + self.properties_type, + properties.properties_type() + ); + } + match self.properties_type { + PropertiesType::Map => { + for (i, prop) in properties.iter_props().enumerate() { + let name = properties.prop_name(i).unwrap(); + self.prop_mut(name).map(|p| p.apply(prop)); + } + } + PropertiesType::Seq => { + for (i, prop) in properties.iter_props().enumerate() { + self.prop_with_index_mut(i).map(|p| p.apply(prop)); + } + } + } + } else { + panic!("attempted to apply non-Properties type to Properties type"); + } + } + + fn as_properties(&self) -> Option<&dyn Properties> { + Some(self) + } + + fn is_sequence(&self) -> bool { + self.properties_type == PropertiesType::Seq + } } impl Serialize for DynamicProperties { @@ -94,10 +190,82 @@ impl Serialize for DynamicProperties { where S: serde::Serializer, { - let mut state = serializer.serialize_map(Some(self.props.len()))?; - state.serialize_entry("type", self.type_name())?; - for (name, prop) in self.iter_props() { - state.serialize_entry(name, prop)?; + match self.properties_type { + PropertiesType::Map => MapSerializer::new(self).serialize(serializer), + PropertiesType::Seq => SeqSerializer::new(self).serialize(serializer), + } + } +} + +pub struct MapSerializer<'a> { + pub properties: &'a dyn Properties, +} + +impl<'a> MapSerializer<'a> { + pub fn new(properties: &'a dyn Properties) -> Self { + MapSerializer { properties } + } +} + +impl<'a> Serialize for MapSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_map(Some(self.properties.prop_len()))?; + state.serialize_entry("type", self.properties.type_name())?; + for (index, property) in self.properties.iter_props().enumerate() { + let name = self.properties.prop_name(index).unwrap(); + if property.is_sequence() { + state.serialize_entry(name, &SeqSerializer { property })?; + } else { + state.serialize_entry(name, property)?; + } + } + state.end() + } +} + +// TODO: maybe you can return this as a type erased serializer as Prop::get_serializer()? This would remove the need for explicit Serialize impls +pub struct SeqSerializer<'a> { + pub property: &'a dyn Property, +} + +impl<'a> SeqSerializer<'a> { + pub fn new(property: &'a dyn Property) -> Self { + SeqSerializer { property } + } +} + +impl<'a> Serialize for SeqSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_map(Some(2))?; + if let Some(properties) = self.property.as_properties() { + state.serialize_entry("seq_type", self.property.type_name())?; + state.serialize_entry("data", &PropertiesSeqSerializer { properties })?; + } else { + state.serialize_entry("seq_value_type", self.property.type_name())?; + state.serialize_entry("data", self.property)?; + } + state.end() + } +} + +pub struct PropertiesSeqSerializer<'a> { + pub properties: &'a dyn Properties, +} + +impl<'a> Serialize for PropertiesSeqSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_seq(Some(self.properties.prop_len()))?; + for prop in self.properties.iter_props() { + state.serialize_element(prop)?; } state.end() } @@ -114,49 +282,90 @@ impl<'a, 'de> DeserializeSeed<'de> for DynamicPropertiesDeserializer<'a> { where D: serde::Deserializer<'de>, { - let mut dynamic_properties = DynamicProperties::default(); - deserializer.deserialize_map(PropMapVisiter { - dynamic_properties: &mut dynamic_properties, + deserializer.deserialize_map(DynamicPropertyMapVisiter { property_type_registry: self.property_type_registry, current_type_name: self.current_type_name, - })?; - - Ok(dynamic_properties) + }) } } -struct PropMapVisiter<'a> { - dynamic_properties: &'a mut DynamicProperties, +pub struct DynamicPropertyMapVisiter<'a> { property_type_registry: &'a PropertyTypeRegistry, current_type_name: Rc>>, } -impl<'a, 'de> Visitor<'de> for PropMapVisiter<'a> { - type Value = (); +impl<'a, 'de> Visitor<'de> for DynamicPropertyMapVisiter<'a> { + type Value = DynamicProperties; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("map of properties") + formatter.write_str("properties map") } - fn visit_map(self, mut map: V) -> Result<(), V::Error> + fn visit_map(self, map: V) -> Result where V: MapAccess<'de>, { - let mut type_name: Option = None; - while let Some(key) = map.next_key::()? { - if &key == "type" { - type_name = Some(map.next_value()?); - } else { - let prop = map.next_value_seed(MapValueDeserializer { - property_type_registry: self.property_type_registry, - current_type_name: self.current_type_name.clone(), - })?; - self.dynamic_properties.set_box(&key, prop); - } - } + visit_map(map, self.property_type_registry, self.current_type_name) + } +} +pub struct PropertyDeserializer<'a> { + pub property_type_registry: &'a PropertyTypeRegistry, + pub current_type_name: Rc>>, +} - let type_name = type_name.ok_or_else(|| de::Error::missing_field("type"))?; - self.dynamic_properties.type_name = type_name.to_string(); - Ok(()) +impl<'a, 'de> DeserializeSeed<'de> for PropertyDeserializer<'a> { + type Value = Box; + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(AnyPropVisiter { + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name, + }) + } +} + +pub struct PropSeqDeserializer<'a> { + property_type_registry: &'a PropertyTypeRegistry, + current_type_name: Rc>>, +} + +impl<'a, 'de> DeserializeSeed<'de> for PropSeqDeserializer<'a> { + type Value = DynamicProperties; + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(PropSeqVisiter { + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name.clone(), + }) + } +} + +pub struct PropSeqVisiter<'a> { + property_type_registry: &'a PropertyTypeRegistry, + current_type_name: Rc>>, +} + +impl<'a, 'de> Visitor<'de> for PropSeqVisiter<'a> { + type Value = DynamicProperties; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("property value") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: SeqAccess<'de>, + { + let mut dynamic_properties = DynamicProperties::seq(); + while let Some(prop) = seq.next_element_seed(PropertyDeserializer { + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name.clone(), + })? { + dynamic_properties.push(prop, None); + } + Ok(dynamic_properties) } } @@ -173,23 +382,18 @@ impl<'a, 'de> DeserializeSeed<'de> for MapValueDeserializer<'a> { { if self.current_type_name.borrow().is_some() { let registration = { - let current_type_name= self.current_type_name.borrow(); + let current_type_name = self.current_type_name.borrow(); let type_name = current_type_name.as_ref().unwrap(); - self - .property_type_registry - .get(type_name) + self.property_type_registry + .get_short(type_name) .ok_or_else(|| { - de::Error::custom(format!( - "TypeRegistration is missing for {}", - type_name - )) + de::Error::custom(format!("TypeRegistration is missing for {}", type_name)) })? }; let mut erased = erased_serde::Deserializer::erase(deserializer); - let res = (registration.deserialize)(&mut erased) - .map_err(<>::Error as serde::de::Error>::custom); - res - } else { + (registration.deserialize)(&mut erased) + .map_err(<>::Error as serde::de::Error>::custom) + } else { deserializer.deserialize_any(AnyPropVisiter { property_type_registry: self.property_type_registry, current_type_name: self.current_type_name, @@ -216,6 +420,13 @@ impl<'a, 'de> Visitor<'de> for AnyPropVisiter<'a> { Ok(Box::new(v)) } + fn visit_bool(self, v: bool) -> Result + where + E: de::Error, + { + Ok(Box::new(v)) + } + fn visit_u16(self, v: u16) -> Result where E: de::Error, @@ -293,30 +504,57 @@ impl<'a, 'de> Visitor<'de> for AnyPropVisiter<'a> { Ok(Box::new(v.to_string())) } - fn visit_map(self, mut map: V) -> Result + fn visit_map(self, map: V) -> Result where V: MapAccess<'de>, { - let mut dynamic_properties = DynamicProperties::default(); - while let Some(key) = map.next_key()? { - let prop = map.next_value_seed(MapValueDeserializer { - property_type_registry: self.property_type_registry, - current_type_name: self.current_type_name.clone(), - })?; - if key == "type" { - dynamic_properties.type_name = prop - .val::() - .map(|s| s.clone()) - .ok_or_else(|| de::Error::custom("type must be a string"))?; - } else { - dynamic_properties.set_box(key, prop); - } - } - - Ok(Box::new(dynamic_properties)) + Ok(Box::new(visit_map(map, self.property_type_registry, self.current_type_name)?)) } } +fn visit_map<'a, 'de, V>( + mut map: V, + property_type_registry: &'a PropertyTypeRegistry, + current_type_name: Rc>>, +) -> Result +where + V: MapAccess<'de>, +{ + let mut dynamic_properties = DynamicProperties::map(); + let mut type_name: Option = None; + let mut is_seq = false; + // TODO: support seq_value_type + while let Some(key) = map.next_key::()? { + if key == "type" { + type_name = Some(map.next_value()?); + } else if key == "seq_type" { + type_name = Some(map.next_value()?); + is_seq = true; + } else if is_seq { + if key != "data" { + return Err(de::Error::custom( + "seq_type must be immediately followed by a data field", + )); + } + dynamic_properties = map.next_value_seed(PropSeqDeserializer { + property_type_registry: property_type_registry, + current_type_name: current_type_name.clone(), + })?; + break; + } else { + let prop = map.next_value_seed(MapValueDeserializer { + property_type_registry: property_type_registry, + current_type_name: current_type_name.clone(), + })?; + dynamic_properties.set_box(&key, prop); + } + } + + let type_name = type_name.ok_or_else(|| de::Error::missing_field("type"))?; + dynamic_properties.type_name = type_name.to_string(); + Ok(dynamic_properties) +} + struct PropertyTypeDeserializer<'a> { registration: &'a PropertyTypeRegistration, } @@ -332,41 +570,3 @@ impl<'a, 'de> DeserializeSeed<'de> for PropertyTypeDeserializer<'a> { .map_err(<>::Error as serde::de::Error>::custom) } } - -impl Property for DynamicProperties { - #[inline] - fn any(&self) -> &dyn Any { - self - } - #[inline] - fn any_mut(&mut self) -> &mut dyn Any { - self - } - #[inline] - fn clone_prop(&self) -> Box { - Box::new(self.to_dynamic()) - } - #[inline] - fn set(&mut self, value: &dyn Property) { - if let Some(properties) = value.as_properties() { - *self = properties.to_dynamic(); - } else { - panic!("attempted to apply non-Properties type to Properties type"); - } - } - - #[inline] - fn apply(&mut self, value: &dyn Property) { - if let Some(properties) = value.as_properties() { - for (name, prop) in properties.iter_props() { - self.prop_mut(name).map(|p| p.apply(prop)); - } - } else { - panic!("attempted to apply non-Properties type to Properties type"); - } - } - - fn as_properties(&self) -> Option<&dyn Properties> { - Some(self) - } -} \ No newline at end of file diff --git a/crates/bevy_property/src/impl_property_glam.rs b/crates/bevy_property/src/impl_property_glam.rs index 9d61c025e7..0981e7fbac 100644 --- a/crates/bevy_property/src/impl_property_glam.rs +++ b/crates/bevy_property/src/impl_property_glam.rs @@ -1,6 +1,5 @@ use crate::{impl_property, Property}; use glam::{Mat3, Mat4, Quat, Vec2, Vec3}; -use std::any::Any; impl_property!(Vec2); impl_property!(Vec3); diff --git a/crates/bevy_property/src/impl_property_legion.rs b/crates/bevy_property/src/impl_property_legion.rs index e3cc32165c..c97a320393 100644 --- a/crates/bevy_property/src/impl_property_legion.rs +++ b/crates/bevy_property/src/impl_property_legion.rs @@ -1,5 +1,4 @@ use crate::{impl_property, Property}; use legion::prelude::Entity; -use std::any::Any; impl_property!(Entity); diff --git a/crates/bevy_property/src/impl_property_smallvec.rs b/crates/bevy_property/src/impl_property_smallvec.rs index 9fb65bee0d..87d66e33d6 100644 --- a/crates/bevy_property/src/impl_property_smallvec.rs +++ b/crates/bevy_property/src/impl_property_smallvec.rs @@ -8,6 +8,11 @@ where T: Clone + Send + Sync + Serialize + 'static + Array, I: Send + Sync + Clone + Serialize + 'static, { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self diff --git a/crates/bevy_property/src/impl_property_std.rs b/crates/bevy_property/src/impl_property_std.rs index ef2245903a..c15ccf89e5 100644 --- a/crates/bevy_property/src/impl_property_std.rs +++ b/crates/bevy_property/src/impl_property_std.rs @@ -1,15 +1,91 @@ -use crate::{Property, impl_property}; +use crate::{impl_property, Properties, PropertiesType, Property, PropertyIter}; use serde::Serialize; use std::{ any::Any, collections::{BTreeMap, HashMap, HashSet, VecDeque}, - hash::Hash, ops::Range, + hash::Hash, + ops::Range, }; +impl Properties for Vec +where + T: Property + Clone + Serialize, +{ + fn prop(&self, _name: &str) -> Option<&dyn Property> { + None + } + fn prop_mut(&mut self, _name: &str) -> Option<&mut dyn Property> { + None + } + fn prop_with_index(&self, index: usize) -> Option<&dyn Property> { + Some(&self[index]) + } + fn prop_with_index_mut(&mut self, index: usize) -> Option<&mut dyn Property> { + Some(&mut self[index]) + } + fn prop_name(&self, _index: usize) -> Option<&str> { + None + } + fn prop_len(&self) -> usize { + self.len() + } + fn iter_props(&self) -> PropertyIter { + PropertyIter::new(self) + } + fn properties_type(&self) -> PropertiesType { + PropertiesType::Seq + } +} + +impl Property for Vec +where + T: Property + Clone + Serialize, +{ + fn type_name(&self) -> &str { + std::any::type_name::() + } + fn any(&self) -> &dyn Any { + self + } + fn any_mut(&mut self) -> &mut dyn Any { + self + } + + fn clone_prop(&self) -> Box { + Box::new(self.clone()) + } + fn set(&mut self, value: &dyn Property) { + if let Some(properties) = value.as_properties() { + if properties.properties_type() != self.properties_type() { + panic!( + "Properties type mismatch. This type is {:?} but the applied type is {:?}", + self.properties_type(), + properties.properties_type() + ); + } + for (i, prop) in properties.iter_props().enumerate() { + self.prop_with_index_mut(i).map(|p| p.apply(prop)); + } + } else { + panic!("attempted to apply non-Properties type to Properties type"); + } + } + fn apply(&mut self, value: &dyn Property) { + self.set(value); + } + + fn as_properties(&self) -> Option<&dyn Properties> { + Some(self) + } + + fn is_sequence(&self) -> bool { + true + } +} + impl_property!(String); impl_property!(bool); -impl_property!(Vec where T: Clone + Send + Sync + Serialize + 'static); -impl_property!(VecDeque where T: Clone + Send + Sync + Serialize + 'static); +impl_property!(SEQUENCE, VecDeque where T: Clone + Send + Sync + Serialize + 'static); impl_property!(HashSet where T: Clone + Eq + Send + Sync + Hash + Serialize + 'static); impl_property!(HashMap where K: Clone + Eq + Send + Sync + Hash + Serialize + 'static, @@ -19,7 +95,15 @@ impl_property!(BTreeMap where V: Clone + Send + Sync + Serialize + 'static); impl_property!(Range where T: Clone + Send + Sync + Serialize + 'static); + +// TODO: Implement lossless primitive types in RON and remove all of these primitive "cast checks" + impl Property for usize { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -69,6 +153,11 @@ impl Property for usize { } impl Property for u64 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -118,6 +207,11 @@ impl Property for u64 { } impl Property for u32 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -167,6 +261,11 @@ impl Property for u32 { } impl Property for u16 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -216,6 +315,11 @@ impl Property for u16 { } impl Property for u8 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -265,6 +369,11 @@ impl Property for u8 { } impl Property for isize { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -314,6 +423,11 @@ impl Property for isize { } impl Property for i64 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -363,6 +477,11 @@ impl Property for i64 { } impl Property for i32 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -411,8 +530,12 @@ impl Property for i32 { } } - impl Property for i16 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -462,6 +585,11 @@ impl Property for i16 { } impl Property for i8 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -511,6 +639,11 @@ impl Property for i8 { } impl Property for f32 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -544,6 +677,11 @@ impl Property for f32 { } impl Property for f64 { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + #[inline] fn any(&self) -> &dyn Any { self @@ -574,4 +712,4 @@ impl Property for f64 { panic!("prop value is not {}", std::any::type_name::()); } } -} \ No newline at end of file +} diff --git a/crates/bevy_property/src/properties.rs b/crates/bevy_property/src/properties.rs index c3cf8c7227..6e4ed020c9 100644 --- a/crates/bevy_property/src/properties.rs +++ b/crates/bevy_property/src/properties.rs @@ -1,7 +1,12 @@ use crate::{DynamicProperties, Property, PropertyVal}; +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] +pub enum PropertiesType { + Map, + Seq, +} + pub trait Properties: Property { - fn type_name(&self) -> &str; fn prop(&self, name: &str) -> Option<&dyn Property>; fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Property>; fn prop_with_index(&self, index: usize) -> Option<&dyn Property>; @@ -9,6 +14,7 @@ pub trait Properties: Property { fn prop_name(&self, index: usize) -> Option<&str>; fn prop_len(&self) -> usize; fn iter_props(&self) -> PropertyIter; + fn properties_type(&self) -> PropertiesType; fn set_prop(&mut self, name: &str, value: &dyn Property) { if let Some(prop) = self.prop_mut(name) { prop.set(value); @@ -18,12 +24,25 @@ pub trait Properties: Property { } fn to_dynamic(&self) -> DynamicProperties { - let mut dynamic_props = DynamicProperties::default(); - for (name, prop) in self.iter_props() { - dynamic_props.set_box(name, prop.clone_prop()); - } + let mut dynamic_props = match self.properties_type() { + PropertiesType::Map => { + let mut dynamic_props = DynamicProperties::map(); + for (i, prop) in self.iter_props().enumerate() { + let name = self.prop_name(i).expect("All properties in maps should have a name"); + dynamic_props.set_box(name, prop.clone_prop()); + } + dynamic_props + }, + PropertiesType::Seq => { + let mut dynamic_props = DynamicProperties::seq(); + for prop in self.iter_props() { + dynamic_props.push(prop.clone_prop(), None); + } + dynamic_props + } + } ; - dynamic_props.type_name = std::any::type_name::().to_string(); + dynamic_props.type_name = self.type_name().to_string(); dynamic_props } } @@ -40,13 +59,12 @@ impl<'a> PropertyIter<'a> { } impl<'a> Iterator for PropertyIter<'a> { - type Item = (&'a str, &'a dyn Property); + type Item = &'a dyn Property; fn next(&mut self) -> Option { if self.index < self.props.prop_len() { let prop = self.props.prop_with_index(self.index).unwrap(); - let name = self.props.prop_name(self.index).unwrap(); self.index += 1; - Some((name, prop)) + Some(prop) } else { None } diff --git a/crates/bevy_property/src/property.rs b/crates/bevy_property/src/property.rs index 9f691a4b99..309e92aba4 100644 --- a/crates/bevy_property/src/property.rs +++ b/crates/bevy_property/src/property.rs @@ -2,6 +2,7 @@ use crate::Properties; use std::any::Any; pub trait Property: erased_serde::Serialize + Send + Sync + Any + 'static { + fn type_name(&self) -> &str; fn any(&self) -> &dyn Any; fn any_mut(&mut self) -> &mut dyn Any; fn clone_prop(&self) -> Box; @@ -10,6 +11,9 @@ pub trait Property: erased_serde::Serialize + Send + Sync + Any + 'static { fn as_properties(&self) -> Option<&dyn Properties> { None } + fn is_sequence(&self) -> bool { + false + } } erased_serde::serialize_trait_object!(Property); @@ -48,12 +52,17 @@ macro_rules! impl_property { ($ty:ident) => { impl Property for $ty { #[inline] - fn any(&self) -> &dyn Any { + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { self } #[inline] - fn any_mut(&mut self) -> &mut dyn Any { + fn any_mut(&mut self) -> &mut dyn std::any::Any { self } @@ -77,18 +86,68 @@ macro_rules! impl_property { } } }; + (SEQUENCE, @$trait_:ident [$($args:ident,)*] where [$($preds:tt)+]) => { + impl_property! { + @as_item + impl<$($args),*> Property for $trait_<$($args),*> where $($args: ::std::any::Any + 'static,)* + $($preds)* { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn clone_prop(&self) -> Box { + Box::new(self.clone()) + } + + #[inline] + fn apply(&mut self, value: &dyn Property) { + self.set(value); + } + + fn set(&mut self, value: &dyn Property) { + let value = value.any(); + if let Some(prop) = value.downcast_ref::() { + *self = prop.clone(); + } else { + panic!("prop value is not {}", std::any::type_name::()); + } + } + + fn is_sequence(&self) -> bool { + true + } + } + } + }; (@$trait_:ident [$($args:ident,)*] where [$($preds:tt)+]) => { impl_property! { @as_item impl<$($args),*> Property for $trait_<$($args),*> where $($args: ::std::any::Any + 'static,)* $($preds)* { #[inline] - fn any(&self) -> &dyn Any { + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { self } #[inline] - fn any_mut(&mut self) -> &mut dyn Any { + fn any_mut(&mut self) -> &mut dyn std::any::Any { self } @@ -115,10 +174,16 @@ macro_rules! impl_property { }; (@as_item $i:item) => { $i }; + ( + SEQUENCE, $trait_:ident < $($args:ident),* $(,)* > + where $($preds:tt)+ + ) => { + impl_property! {SEQUENCE, @$trait_ [$($args,)*] where [$($preds)*] } + }; ( $trait_:ident < $($args:ident),* $(,)* > where $($preds:tt)+ ) => { impl_property! { @$trait_ [$($args,)*] where [$($preds)*] } }; -} +} \ No newline at end of file diff --git a/crates/bevy_property/src/type_registry.rs b/crates/bevy_property/src/type_registry.rs index ec8f2ed832..30571294c3 100644 --- a/crates/bevy_property/src/type_registry.rs +++ b/crates/bevy_property/src/type_registry.rs @@ -5,6 +5,7 @@ use std::{any::TypeId, collections::HashMap}; #[derive(Default)] pub struct PropertyTypeRegistry { pub registrations: HashMap, + pub short_names: HashMap, } impl PropertyTypeRegistry { @@ -13,19 +14,31 @@ impl PropertyTypeRegistry { T: Property + for<'de> Deserialize<'de>, { let registration = PropertyTypeRegistration::of::(); - self.registrations.insert(registration.short_name.to_string(), registration); + self.short_names + .insert(registration.short_name.to_string(), registration.name.to_string()); + self.registrations + .insert(registration.name.to_string(), registration); } pub fn get(&self, type_name: &str) -> Option<&PropertyTypeRegistration> { self.registrations.get(type_name) } + + pub fn get_short(&self, short_type_name: &str) -> Option<&PropertyTypeRegistration> { + self.short_names + .get(short_type_name) + .and_then(|name| self.registrations.get(name)) + } } #[derive(Clone)] pub struct PropertyTypeRegistration { pub ty: TypeId, - pub deserialize: fn(deserializer: &mut dyn erased_serde::Deserializer) -> Result, erased_serde::Error>, + pub deserialize: fn( + deserializer: &mut dyn erased_serde::Deserializer, + ) -> Result, erased_serde::Error>, pub short_name: &'static str, + pub name: &'static str, } impl PropertyTypeRegistration { @@ -37,7 +50,8 @@ impl PropertyTypeRegistration { let property = ::deserialize(deserializer)?; Ok(Box::new(property)) }, + name: std::any::type_name::(), short_name: std::any::type_name::().split("::").last().unwrap(), } } -} \ No newline at end of file +} diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 48b91c1473..fa367b168f 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -41,8 +41,8 @@ use self::{ use base_render_graph::{BaseRenderGraphBuilder, BaseRenderGraphConfig}; use bevy_app::{stage, AppBuilder, AppPlugin}; -use bevy_component_registry::RegisterComponent; use bevy_asset::AddAsset; +use bevy_component_registry::RegisterComponent; use legion::prelude::IntoSystem; use mesh::mesh_resource_provider_system; use render_graph::RenderGraph; @@ -78,6 +78,7 @@ impl AppPlugin for RenderPlugin { .register_component::() .register_component::() .register_component::() + .register_property_type::() .init_resource::() .init_resource::() .init_resource::() diff --git a/crates/ron/src/de/mod.rs b/crates/ron/src/de/mod.rs index c7d5047232..7024f2873f 100644 --- a/crates/ron/src/de/mod.rs +++ b/crates/ron/src/de/mod.rs @@ -595,7 +595,14 @@ impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { fast_forward_bytes.skip_ws()?; fast_forward_bytes.consume(":"); fast_forward_bytes.skip_ws()?; - callback(&fast_forward_bytes.identifier().ok()); + let identifier = &fast_forward_bytes.identifier().ok(); + fast_forward_bytes.skip_ws()?; + // only run callback from structs + if fast_forward_bytes.consume("(") { + callback(identifier); + } else { + callback(&None); + } } result } else { diff --git a/examples/scene/properties.rs b/examples/scene/properties.rs index 839426b61e..e00621273f 100644 --- a/examples/scene/properties.rs +++ b/examples/scene/properties.rs @@ -4,6 +4,7 @@ use bevy::{ property::{ron::deserialize_dynamic_properties}, }; use serde::{Deserialize, Serialize}; +use bevy_property::{PropertiesSeqSerializer, SeqSerializer}; fn main() { App::build() @@ -26,34 +27,11 @@ pub struct Nested { b: usize, } -#[derive(Serialize, Deserialize, Default, Clone)] +#[derive(Serialize, Deserialize, Default, Clone, Property)] pub struct CustomProperty { a: usize, } -impl Property for CustomProperty { - fn any(&self) -> &dyn std::any::Any { - self - } - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - fn clone_prop(&self) -> Box { - Box::new(self.clone()) - } - fn set(&mut self, value: &dyn Property) { - let value = value.any(); - if let Some(prop) = value.downcast_ref::() { - *self = prop.clone(); - } else { - panic!("prop value is not {}", std::any::type_name::()); - } - } - fn apply(&mut self, value: &dyn Property) { - self.set(value); - } -} - fn setup(property_type_registry: Res) { let mut test = Test { a: 1, @@ -71,7 +49,7 @@ fn setup(property_type_registry: Res) { assert_eq!(test.a, 3); // DynamicProperties also implements the Properties trait. - let mut patch = DynamicProperties::default(); + let mut patch = DynamicProperties::map(); patch.set::("a", 4); // You can "apply" Properties on top of other Properties. This will only set properties with the same name and type. @@ -80,7 +58,6 @@ fn setup(property_type_registry: Res) { assert_eq!(test.a, 4); // Properties implement the serde Serialize trait. You don't need to derive it yourself! - let ron_string = serialize_ron(&test).unwrap(); println!("{}\n", ron_string); @@ -95,6 +72,22 @@ fn setup(property_type_registry: Res) { // This means you can patch Properties with dynamic properties deserialized from a string test.apply(&dynamic_properties); + + // Properties can also be sequences. Std sequences (Vec, VecDeque) already implement the Properties trait + let mut seq = vec![1u32, 2u32]; + let mut patch = DynamicProperties::seq(); + patch.push(Box::new(3u32), None); + seq.apply(&patch); + assert_eq!(seq[0], 3); + + let ron_string = serialize_ron(&SeqSerializer { property: &patch} ).unwrap(); + println!("{}\n", ron_string); + let dynamic_properties = + deserialize_dynamic_properties(&ron_string, &property_type_registry.value.read().unwrap()) + .unwrap(); + let round_tripped = serialize_ron(&dynamic_properties).unwrap(); + println!("{}", round_tripped); + assert_eq!(ron_string, round_tripped); } fn serialize_ron(properties: &T) -> Result