diff --git a/assets/scene/load_scene_example.scn b/assets/scene/load_scene_example.scn index 1d3c695c67..3285aaa4c8 100644 --- a/assets/scene/load_scene_example.scn +++ b/assets/scene/load_scene_example.scn @@ -1,6 +1,6 @@ [ ( - entity: 3072676124, + entity: 1279729879, components: [ { "type": "load_scene::Test", @@ -10,7 +10,7 @@ ], ), ( - entity: 3949176536, + entity: 1639302665, components: [ { "type": "load_scene::Test", diff --git a/crates/bevy_property/Cargo.toml b/crates/bevy_property/Cargo.toml index 499ea184cc..89db925dc9 100644 --- a/crates/bevy_property/Cargo.toml +++ b/crates/bevy_property/Cargo.toml @@ -9,4 +9,5 @@ edition = "2018" [dependencies] serde = "1" erased-serde = "0.3" -bevy_property_derive = { path = "bevy_property_derive" } \ No newline at end of file +bevy_property_derive = { path = "bevy_property_derive" } +ron = { path = "../ron" } \ No newline at end of file diff --git a/crates/bevy_property/src/dynamic_properties.rs b/crates/bevy_property/src/dynamic_properties.rs index 7d93fa07b2..dac3a44ed2 100644 --- a/crates/bevy_property/src/dynamic_properties.rs +++ b/crates/bevy_property/src/dynamic_properties.rs @@ -1,10 +1,13 @@ -use crate::{AsProperties, Properties, Property, PropertyIter, PropertyVal}; -use serde::{ - de::{self, MapAccess, Visitor}, - ser::SerializeMap, - Deserialize, Serialize, +use crate::{ + AsProperties, Properties, Property, PropertyIter, PropertyTypeRegistration, + PropertyTypeRegistry, PropertyVal, }; -use std::{any::Any, borrow::Cow, collections::HashMap}; +use serde::{ + de::{self, DeserializeSeed, MapAccess, Visitor}, + ser::SerializeMap, + Serialize, +}; +use std::{any::Any, borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc}; #[derive(Default)] pub struct DynamicProperties { @@ -100,14 +103,22 @@ impl Serialize for DynamicProperties { } } -impl<'de> Deserialize<'de> for DynamicProperties { - fn deserialize(deserializer: D) -> Result +pub struct DynamicPropertiesDeserializer<'a> { + pub property_type_registry: &'a PropertyTypeRegistry, + pub current_type_name: Rc>>, +} + +impl<'a, 'de> DeserializeSeed<'de> for DynamicPropertiesDeserializer<'a> { + type Value = DynamicProperties; + fn deserialize(self, deserializer: D) -> Result where D: serde::Deserializer<'de>, { let mut dynamic_properties = DynamicProperties::default(); deserializer.deserialize_map(PropMapVisiter { dynamic_properties: &mut dynamic_properties, + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name, })?; Ok(dynamic_properties) @@ -116,6 +127,8 @@ impl<'de> Deserialize<'de> for DynamicProperties { struct PropMapVisiter<'a> { dynamic_properties: &'a mut DynamicProperties, + property_type_registry: &'a PropertyTypeRegistry, + current_type_name: Rc>>, } impl<'a, 'de> Visitor<'de> for PropMapVisiter<'a> { @@ -133,7 +146,10 @@ impl<'a, 'de> Visitor<'de> for PropMapVisiter<'a> { if &key == "type" { type_name = Some(map.next_value()?); } else { - let prop = map.next_value()?; + 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); } } @@ -144,18 +160,50 @@ impl<'a, 'de> Visitor<'de> for PropMapVisiter<'a> { } } -impl<'de> Deserialize<'de> for Box { - fn deserialize(deserializer: D) -> Result +pub struct MapValueDeserializer<'a> { + property_type_registry: &'a PropertyTypeRegistry, + current_type_name: Rc>>, +} + +impl<'a, 'de> DeserializeSeed<'de> for MapValueDeserializer<'a> { + type Value = Box; + fn deserialize(self, deserializer: D) -> Result where D: serde::Deserializer<'de>, { - deserializer.deserialize_any(AnyPropVisiter) + if self.current_type_name.borrow().is_some() { + let registration = { + 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) + .ok_or_else(|| { + 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 { + deserializer.deserialize_any(AnyPropVisiter { + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name, + }) + } } } -struct AnyPropVisiter; +struct AnyPropVisiter<'a> { + property_type_registry: &'a PropertyTypeRegistry, + current_type_name: Rc>>, +} -impl<'de> Visitor<'de> for AnyPropVisiter { +impl<'a, 'de> Visitor<'de> for AnyPropVisiter<'a> { type Value = Box; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("property value") @@ -251,7 +299,10 @@ impl<'de> Visitor<'de> for AnyPropVisiter { { let mut dynamic_properties = DynamicProperties::default(); while let Some(key) = map.next_key()? { - let prop = map.next_value::>()?; + 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::() @@ -261,10 +312,27 @@ impl<'de> Visitor<'de> for AnyPropVisiter { dynamic_properties.set_box(key, prop); } } + Ok(Box::new(dynamic_properties)) } } +struct PropertyTypeDeserializer<'a> { + registration: &'a PropertyTypeRegistration, +} + +impl<'a, 'de> DeserializeSeed<'de> for PropertyTypeDeserializer<'a> { + type Value = Box; + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let mut erased = erased_serde::Deserializer::erase(deserializer); + (self.registration.deserialize)(&mut erased) + .map_err(<>::Error as serde::de::Error>::custom) + } +} + impl Property for DynamicProperties { #[inline] fn any(&self) -> &dyn Any { diff --git a/crates/bevy_property/src/lib.rs b/crates/bevy_property/src/lib.rs index 57a66a4e50..18f660181f 100644 --- a/crates/bevy_property/src/lib.rs +++ b/crates/bevy_property/src/lib.rs @@ -3,10 +3,13 @@ mod property; mod properties; mod dynamic_properties; +mod type_registry; +pub mod ron; pub use property::*; pub use properties::*; pub use dynamic_properties::*; +pub use type_registry::*; pub use bevy_property_derive::*; pub use serde; \ No newline at end of file diff --git a/crates/bevy_property/src/ron.rs b/crates/bevy_property/src/ron.rs new file mode 100644 index 0000000000..7b561dc19f --- /dev/null +++ b/crates/bevy_property/src/ron.rs @@ -0,0 +1,19 @@ +use ron::de::Deserializer; +use std::{cell::RefCell, rc::Rc}; +use crate::{DynamicPropertiesDeserializer, PropertyTypeRegistry, DynamicProperties}; +use serde::de::DeserializeSeed; + +pub fn deserialize_dynamic_properties(ron_string: &str, property_type_registry: &PropertyTypeRegistry) -> Result { + let mut deserializer = Deserializer::from_str(&ron_string).unwrap(); + let last_type_name = Rc::new(RefCell::new(None)); + let mut callback = |ident: &Option<&[u8]>| { + let mut last_type_name = last_type_name.borrow_mut(); + *last_type_name = ident.map(|i| String::from_utf8(i.to_vec()).unwrap()); + }; + deserializer.set_callback(&mut callback); + let dynamic_properties_deserializer = DynamicPropertiesDeserializer { + current_type_name: last_type_name.clone(), + property_type_registry: &property_type_registry, + }; + dynamic_properties_deserializer.deserialize(&mut deserializer) +} \ No newline at end of file diff --git a/crates/bevy_property/src/type_registry.rs b/crates/bevy_property/src/type_registry.rs new file mode 100644 index 0000000000..ec8f2ed832 --- /dev/null +++ b/crates/bevy_property/src/type_registry.rs @@ -0,0 +1,43 @@ +use crate::Property; +use serde::Deserialize; +use std::{any::TypeId, collections::HashMap}; + +#[derive(Default)] +pub struct PropertyTypeRegistry { + pub registrations: HashMap, +} + +impl PropertyTypeRegistry { + pub fn register(&mut self) + where + T: Property + for<'de> Deserialize<'de>, + { + let registration = PropertyTypeRegistration::of::(); + self.registrations.insert(registration.short_name.to_string(), registration); + } + + pub fn get(&self, type_name: &str) -> Option<&PropertyTypeRegistration> { + self.registrations.get(type_name) + } +} + +#[derive(Clone)] +pub struct PropertyTypeRegistration { + pub ty: TypeId, + pub deserialize: fn(deserializer: &mut dyn erased_serde::Deserializer) -> Result, erased_serde::Error>, + pub short_name: &'static str, +} + +impl PropertyTypeRegistration { + pub fn of Deserialize<'de>>() -> Self { + let ty = TypeId::of::(); + Self { + ty, + deserialize: |deserializer: &mut dyn erased_serde::Deserializer| { + let property = ::deserialize(deserializer)?; + Ok(Box::new(property)) + }, + short_name: std::any::type_name::().split("::").last().unwrap(), + } + } +} \ No newline at end of file diff --git a/crates/bevy_scene/src/component_registry.rs b/crates/bevy_scene/src/component_registry.rs index 43aad69c3f..6b777df304 100644 --- a/crates/bevy_scene/src/component_registry.rs +++ b/crates/bevy_scene/src/component_registry.rs @@ -1,14 +1,20 @@ use bevy_app::AppBuilder; -use bevy_property::{Properties, Property}; +use bevy_property::{Properties, Property, PropertyTypeRegistry}; use legion::{ prelude::{Entity, World}, - storage::{Component, ComponentTypeId, ComponentResourceSet}, + storage::{Component, ComponentResourceSet, ComponentTypeId}, }; +use serde::Deserialize; use std::{ collections::HashMap, sync::{Arc, RwLock}, }; +#[derive(Clone, Default)] +pub struct PropertyTypeRegistryContext { + pub value: Arc>, +} + #[derive(Default)] pub struct ComponentRegistryContext { pub value: Arc>, @@ -69,9 +75,10 @@ impl ComponentRegistration { component.apply(property); world.add_component(entity, component).unwrap(); }, - component_properties_fn: |component_resource_set: &ComponentResourceSet, index: usize| { + component_properties_fn: |component_resource_set: &ComponentResourceSet, + index: usize| { // the type has been looked up by the caller, so this is safe - unsafe { &component_resource_set.data_slice::()[index] } + unsafe { &component_resource_set.data_slice::()[index] } }, short_name: ty.0.split("::").last().unwrap(), } @@ -82,6 +89,9 @@ pub trait RegisterComponent { fn register_component(&mut self) -> &mut Self where T: Properties + Component + Default; + fn register_property_type(&mut self) -> &mut Self + where + T: Property + for<'de> Deserialize<'de>; } impl RegisterComponent for AppBuilder { @@ -98,4 +108,18 @@ impl RegisterComponent for AppBuilder { } self } + + fn register_property_type(&mut self) -> &mut Self + where + T: Property + for<'de> Deserialize<'de>, + { + { + let registry_context = self + .resources() + .get_mut::() + .unwrap(); + registry_context.value.write().unwrap().register::(); + } + self + } } diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index 1d4eb7c5d1..77551e3c35 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -11,7 +11,8 @@ pub struct ComponentRegistryPlugin; impl AppPlugin for ComponentRegistryPlugin { fn build(&self, app: &mut AppBuilder) { - app.init_resource::(); + app.init_resource::() + .init_resource::(); } } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index cfdf858b6b..5fe0019eb7 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,17 +1,28 @@ -use crate::ComponentRegistry; +use crate::{ComponentRegistry, PropertyTypeRegistryContext}; use anyhow::Result; +use bevy_app::FromResources; use bevy_asset::AssetLoader; -use bevy_property::DynamicProperties; -use legion::prelude::{Entity, World}; -use serde::{Deserialize, Serialize}; -use std::{num::Wrapping, path::Path}; +use bevy_property::{DynamicProperties, PropertyTypeRegistry, DynamicPropertiesDeserializer}; +use legion::prelude::{Entity, Resources, World}; +use serde::{ + de::{DeserializeSeed, SeqAccess, Visitor, MapAccess, Error}, + Serialize, + Deserialize +}; +use std::{cell::RefCell, num::Wrapping, path::Path, rc::Rc}; use thiserror::Error; -#[derive(Serialize, Deserialize, Default)] +#[derive(Default)] pub struct Scene { pub entities: Vec, } +#[derive(Serialize)] +pub struct SceneEntity { + pub entity: u32, + pub components: Vec, +} + #[derive(Error, Debug)] pub enum SceneAddError { #[error("Scene contains an unregistered component.")] @@ -85,20 +96,219 @@ impl Scene { } } -#[derive(Serialize, Deserialize)] -pub struct SceneEntity { - pub entity: u32, - pub components: Vec, +impl Serialize for Scene { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.entities.serialize(serializer) + } } -#[derive(Default)] -pub struct SceneLoader; +pub struct SceneDeserializer<'a> { + pub property_type_registry: &'a PropertyTypeRegistry, + pub current_type_name: Rc>>, +} + +impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> { + type Value = Scene; + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let mut scene = Scene::default(); + scene.entities = deserializer.deserialize_seq(SceneEntitySeqVisiter { + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name, + })?; + + Ok(scene) + } +} + +struct SceneEntitySeqVisiter<'a> { + pub property_type_registry: &'a PropertyTypeRegistry, + pub current_type_name: Rc>>, +} + +impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisiter<'a> { + type Value = Vec; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("list of entities") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut entities = Vec::new(); + while let Some(entity) = seq.next_element_seed(SceneEntityDeserializer { + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name.clone(), + })? { + entities.push(entity); + } + + Ok(entities) + } +} + +pub struct SceneEntityDeserializer<'a> { + pub property_type_registry: &'a PropertyTypeRegistry, + pub current_type_name: Rc>>, +} + +impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> { + type Value = SceneEntity; + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_struct("", &["entity", "components"], SceneEntityVisiter { + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name, + }) + } +} + + +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "lowercase")] +enum EntityField { + Entity, + Components, +} + +pub const ENTITY_FIELD_ENTITY: &str = "entity"; +pub const ENTITY_FIELD_COMPONENTS: &str = "components"; + +struct SceneEntityVisiter<'a> { + pub property_type_registry: &'a PropertyTypeRegistry, + pub current_type_name: Rc>>, +} + +impl<'a, 'de> Visitor<'de> for SceneEntityVisiter<'a> { + type Value = SceneEntity; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("entities") + } + + fn visit_map(self, mut map: A) -> Result + where A: MapAccess<'de> { + let mut entity = None; + let mut components = None; + while let Some(key) = map.next_key()? { + match key { + EntityField::Entity => { + if entity.is_some() { + return Err(Error::duplicate_field(ENTITY_FIELD_ENTITY)); + } + entity = Some(map.next_value::()?); + } + EntityField::Components => { + if components.is_some() { + return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS)); + } + + components = Some(map.next_value_seed(ComponentVecDeserializer { + current_type_name: self.current_type_name.clone(), + property_type_registry: self.property_type_registry + })?); + } + } + } + + let entity = entity + .as_ref() + .ok_or_else(|| Error::missing_field(ENTITY_FIELD_ENTITY))?; + + let components = components + .take() + .ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?; + Ok(SceneEntity { + entity: *entity, + components, + }) + } +} + +pub struct ComponentVecDeserializer<'a> { + pub property_type_registry: &'a PropertyTypeRegistry, + pub current_type_name: Rc>>, +} + +impl<'a, 'de> DeserializeSeed<'de> for ComponentVecDeserializer<'a> { + type Value = Vec; + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(ComponentSeqVisiter { + property_type_registry: self.property_type_registry, + current_type_name: self.current_type_name, + }) + } +} + + +struct ComponentSeqVisiter<'a> { + pub property_type_registry: &'a PropertyTypeRegistry, + pub current_type_name: Rc>>, +} + +impl<'a, 'de> Visitor<'de> for ComponentSeqVisiter<'a> { + type Value = Vec; + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("list of components") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut dynamic_properties = Vec::new(); + while let Some(entity) = seq.next_element_seed(DynamicPropertiesDeserializer { + current_type_name: self.current_type_name.clone(), + property_type_registry: self.property_type_registry + })? { + dynamic_properties.push(entity); + } + + Ok(dynamic_properties) + } +} + + +pub struct SceneLoader { + property_type_registry: PropertyTypeRegistryContext, +} +impl FromResources for SceneLoader { + fn from_resources(resources: &Resources) -> Self { + let property_type_registry = resources.get::().unwrap(); + SceneLoader { + property_type_registry: property_type_registry.clone(), + } + } +} impl AssetLoader for SceneLoader { fn from_bytes(&self, _asset_path: &Path, bytes: Vec) -> Result { + let registry = self.property_type_registry.value.read().unwrap(); let mut deserializer = ron::de::Deserializer::from_bytes(&bytes).unwrap(); - let entities = Vec::::deserialize(&mut deserializer).unwrap(); - Ok(Scene { entities }) + let current_type_name = Rc::new(RefCell::new(None)); + let scene_deserializer = SceneDeserializer { + property_type_registry: ®istry, + current_type_name: current_type_name.clone(), + }; + let mut callback = |ident: &Option<&[u8]>| { + let mut last_type_name = current_type_name.borrow_mut(); + *last_type_name = ident.map(|i| String::from_utf8(i.to_vec()).unwrap()); + }; + deserializer.set_callback(&mut callback); + + + let scene = scene_deserializer.deserialize(&mut deserializer).unwrap(); + Ok(scene) } fn extensions(&self) -> &[&str] { static EXTENSIONS: &[&str] = &["scn"]; diff --git a/crates/ron/src/de/mod.rs b/crates/ron/src/de/mod.rs index 8c01d42a98..c7d5047232 100644 --- a/crates/ron/src/de/mod.rs +++ b/crates/ron/src/de/mod.rs @@ -23,6 +23,7 @@ mod value; /// you can use the `from_str` convenience function. pub struct Deserializer<'de> { bytes: Bytes<'de>, + type_callback: Option<&'de mut dyn FnMut(&Option<&[u8]>)>, } impl<'de> Deserializer<'de> { @@ -35,12 +36,17 @@ impl<'de> Deserializer<'de> { pub fn from_bytes(input: &'de [u8]) -> Result { Ok(Deserializer { bytes: Bytes::new(input)?, + type_callback: None, }) } pub fn remainder(&self) -> Cow<'_, str> { String::from_utf8_lossy(&self.bytes.bytes()) } + + pub fn set_callback(&mut self, callback: &'de mut dyn FnMut(&Option<&[u8]>)) { + self.type_callback = Some(callback); + } } /// A convenience function for reading data from a reader @@ -578,12 +584,20 @@ impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { K: DeserializeSeed<'de>, { if self.has_element()? { - if self.terminator == b')' { + let result = if self.terminator == b')' { seed.deserialize(&mut IdDeserializer::new(&mut *self.de)) .map(Some) } else { seed.deserialize(&mut *self.de).map(Some) + }; + if let Some(ref mut callback) = self.de.type_callback { + let mut fast_forward_bytes = self.de.bytes.clone(); + fast_forward_bytes.skip_ws()?; + fast_forward_bytes.consume(":"); + fast_forward_bytes.skip_ws()?; + callback(&fast_forward_bytes.identifier().ok()); } + result } else { Ok(None) } diff --git a/crates/ron/src/parse.rs b/crates/ron/src/parse.rs index 9d4e25e883..810c7b461c 100644 --- a/crates/ron/src/parse.rs +++ b/crates/ron/src/parse.rs @@ -315,7 +315,7 @@ impl<'a> Bytes<'a> { self.test_for(ident) && !self.check_ident_char(ident.len()) } - fn check_ident_char(&self, index: usize) -> bool { + pub fn check_ident_char(&self, index: usize) -> bool { self.bytes .get(index) .map_or(false, |b| IDENT_CHAR.contains(b)) diff --git a/examples/scene/load_scene.rs b/examples/scene/load_scene.rs index fcf4baa540..5bdd64d7dc 100644 --- a/examples/scene/load_scene.rs +++ b/examples/scene/load_scene.rs @@ -69,5 +69,4 @@ fn serialize_scene(world: &mut World, resources: &mut Resources) { let pretty_config = ron::ser::PrettyConfig::default().with_decimal_floats(true); let ron_string = ron::ser::to_string_pretty(&scene, pretty_config).unwrap(); println!("{}", ron_string); - } \ No newline at end of file diff --git a/examples/scene/properties.rs b/examples/scene/properties.rs index 3b6276916f..b4840b9c79 100644 --- a/examples/scene/properties.rs +++ b/examples/scene/properties.rs @@ -1,8 +1,13 @@ use bevy::prelude::*; +use bevy_property::ron::deserialize_dynamic_properties; +use bevy_scene::PropertyTypeRegistryContext; +use serde::{Deserialize, Serialize}; fn main() { App::build() .add_default_plugins() + // If you need to deserialize custom property types, register them like this: + .register_property_type::() .add_startup_system(setup.system()) .run(); } @@ -10,6 +15,7 @@ fn main() { #[derive(Properties, Default)] pub struct Test { a: usize, + hi: CustomProperty, nested: Nested, } @@ -18,9 +24,15 @@ pub struct Nested { b: usize, } -fn setup() { +#[derive(Serialize, Deserialize, Default, Clone)] +pub struct CustomProperty { + a: usize, +} + +fn setup(property_type_registry: Res) { let mut test = Test { a: 1, + hi: CustomProperty { a: 10 }, nested: Nested { b: 8 }, }; @@ -43,17 +55,31 @@ fn setup() { assert_eq!(test.a, 4); // Properties implement the serde Serialize trait. You don't need to derive it yourself! - let pretty_config = ron::ser::PrettyConfig::default().with_decimal_floats(true); - let ron_string = ron::ser::to_string_pretty(&test, pretty_config.clone()).unwrap(); + let ron_string = serialize_ron(&test).unwrap(); println!("{}\n", ron_string); // Dynamic properties can be deserialized - let dynamic_properties = ron::from_str::(&ron_string).unwrap(); - let round_tripped = ron::ser::to_string_pretty(&dynamic_properties, pretty_config).unwrap(); + 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); // This means you can patch Properties with dynamic properties deserialized from a string test.apply(&dynamic_properties); } + +fn serialize_ron(properties: &T) -> Result +where + T: Serialize, +{ + let pretty_config = ron::ser::PrettyConfig::default().with_decimal_floats(true); + let mut buf = Vec::new(); + let mut serializer = ron::ser::Serializer::new(&mut buf, Some(pretty_config), true)?; + properties.serialize(&mut serializer)?; + let ron_string = String::from_utf8(buf).unwrap(); + Ok(ron_string) +}