From c48d80541ac01c265d46d828ffc2f5463887a29a Mon Sep 17 00:00:00 2001 From: Clemens Winter Date: Wed, 27 Oct 2021 19:31:03 -0700 Subject: [PATCH 1/6] Move struct_names from Serializer to PrettyConfig --- src/error.rs | 4 +--- src/ser/mod.rs | 49 +++++++++++++++++++++++++++------------ tests/to_string_pretty.rs | 21 +++++++++++++++++ 3 files changed, 56 insertions(+), 18 deletions(-) create mode 100644 tests/to_string_pretty.rs diff --git a/src/error.rs b/src/error.rs index 2a5052159..6430a45f8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,6 +14,7 @@ pub struct Error { pub type Result = std::result::Result; #[derive(Clone, Debug, PartialEq)] +#[non_exhaustive] pub enum ErrorCode { Io(String), Message(String), @@ -54,9 +55,6 @@ pub enum ErrorCode { Utf8Error(Utf8Error), TrailingCharacters, - - #[doc(hidden)] - __Nonexhaustive, } impl fmt::Display for Error { diff --git a/src/ser/mod.rs b/src/ser/mod.rs index d8c4ce649..2143db283 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -17,7 +17,7 @@ where W: io::Write, T: ?Sized + Serialize, { - let mut s = Serializer::new(writer, None, false)?; + let mut s = Serializer::new(writer, None)?; value.serialize(&mut s) } @@ -27,7 +27,7 @@ where W: io::Write, T: ?Sized + Serialize, { - let mut s = Serializer::new(writer, Some(config), false)?; + let mut s = Serializer::new(writer, Some(config))?; value.serialize(&mut s) } @@ -40,7 +40,7 @@ where T: ?Sized + Serialize, { let buf = Vec::new(); - let mut s = Serializer::new(buf, None, false)?; + let mut s = Serializer::new(buf, None)?; value.serialize(&mut s)?; Ok(String::from_utf8(s.output).expect("Ron should be utf-8")) } @@ -51,7 +51,7 @@ where T: ?Sized + Serialize, { let buf = Vec::new(); - let mut s = Serializer::new(buf, Some(config), false)?; + let mut s = Serializer::new(buf, Some(config))?; value.serialize(&mut s)?; Ok(String::from_utf8(s.output).expect("Ron should be utf-8")) } @@ -75,6 +75,7 @@ struct Pretty { /// .indentor("\t".to_owned()); /// ``` #[derive(Clone, Debug, Serialize, Deserialize)] +#[non_exhaustive] pub struct PrettyConfig { /// Limit the pretty-ness up to the given depth. #[serde(default = "default_depth_limit")] @@ -85,6 +86,9 @@ pub struct PrettyConfig { /// Indentation string #[serde(default = "default_indentor")] pub indentor: String, + // Whether to emit struct names + #[serde(default = "default_struct_names")] + pub struct_names: bool, /// Separate tuple members with indentation #[serde(default = "default_separate_tuple_members")] pub separate_tuple_members: bool, @@ -96,9 +100,6 @@ pub struct PrettyConfig { pub decimal_floats: bool, /// Enable extensions. Only configures 'implicit_some' for now. pub extensions: Extensions, - /// Private field to ensure adding a field is non-breaking. - #[serde(skip)] - _future_proof: (), } impl PrettyConfig { @@ -137,6 +138,15 @@ impl PrettyConfig { self } + /// Configures whether to emit struct names. + /// + /// Default: `false` + pub fn struct_names(mut self, struct_names: bool) -> Self { + self.struct_names = struct_names; + + self + } + /// Configures whether tuples are single- or multi-line. /// If set to `true`, tuples will have their fields indented and in new /// lines. If set to `false`, tuples will be serialized without any @@ -201,6 +211,10 @@ fn default_indentor() -> String { " ".to_string() } +fn default_struct_names() -> bool { + false +} + fn default_separate_tuple_members() -> bool { false } @@ -215,11 +229,11 @@ impl Default for PrettyConfig { depth_limit: default_depth_limit(), new_line: default_new_line(), indentor: default_indentor(), + struct_names: default_struct_names(), separate_tuple_members: default_separate_tuple_members(), enumerate_arrays: default_enumerate_arrays(), extensions: Extensions::default(), decimal_floats: default_decimal_floats(), - _future_proof: (), } } } @@ -231,7 +245,6 @@ impl Default for PrettyConfig { pub struct Serializer { output: W, pretty: Option<(PrettyConfig, Pretty)>, - struct_names: bool, is_empty: Option, } @@ -239,7 +252,7 @@ impl Serializer { /// Creates a new `Serializer`. /// /// Most of the time you can just use `to_string` or `to_string_pretty`. - pub fn new(mut writer: W, config: Option, struct_names: bool) -> Result { + pub fn new(mut writer: W, config: Option) -> Result { if let Some(conf) = &config { if conf.extensions.contains(Extensions::IMPLICIT_SOME) { writer.write_all(b"#![enable(implicit_some)]")?; @@ -257,7 +270,6 @@ impl Serializer { }, ) }), - struct_names, is_empty: None, }) } @@ -349,6 +361,13 @@ impl Serializer { self.output.write_all(name.as_bytes())?; Ok(()) } + + fn struct_names(&self) -> bool { + self.pretty + .as_ref() + .map(|(pc, _)| pc.struct_names) + .unwrap_or(false) + } } impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { @@ -479,7 +498,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { } fn serialize_unit_struct(self, name: &'static str) -> Result<()> { - if self.struct_names { + if self.struct_names() { self.write_identifier(name)?; Ok(()) @@ -498,7 +517,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { where T: ?Sized + Serialize, { - if self.struct_names { + if self.struct_names() { self.write_identifier(name)?; } @@ -566,7 +585,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { name: &'static str, len: usize, ) -> Result { - if self.struct_names { + if self.struct_names() { self.write_identifier(name)?; } @@ -611,7 +630,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { } fn serialize_struct(self, name: &'static str, len: usize) -> Result { - if self.struct_names { + if self.struct_names() { self.write_identifier(name)?; } self.output.write_all(b"(")?; diff --git a/tests/to_string_pretty.rs b/tests/to_string_pretty.rs new file mode 100644 index 000000000..9379e1db4 --- /dev/null +++ b/tests/to_string_pretty.rs @@ -0,0 +1,21 @@ +use ron::ser::{to_string_pretty, PrettyConfig}; +use ron::to_string; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Deserialize, Serialize)] +struct Point { + x: f64, + y: f64, +} + +#[test] +fn test_struct_names() { + let value = Point { x: 1.0, y: 2.0 }; + let struct_name = to_string_pretty(&value, PrettyConfig::default().struct_names(true)); + assert_eq!( + struct_name, + Ok("Point(\n x: 1,\n y: 2,\n)".to_string()) + ); + let no_struct_name = to_string(&value); + assert_eq!(no_struct_name, Ok("(x:1,y:2)".to_string())); +} From 7780d2282777a641799d6bc4e08c38a87bea7fee Mon Sep 17 00:00:00 2001 From: Clemens Winter Date: Wed, 27 Oct 2021 19:32:35 -0700 Subject: [PATCH 2/6] Update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2520b5881..d422cca52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased +- Add `struct_names` option to `PrettyConfig` + ## [0.7.0] - 2021-10-22 - Add `unwrap_variant_newtypes` extension ([#319](https://github.com/ron-rs/ron/pull/319)) @@ -178,4 +181,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add roundtrip tests ([#24](https://github.com/ron-rs/ron/pull/24)) ## [0.0.1] - 2015-07-30 -Initial release \ No newline at end of file +Initial release From 631c0b2bd9a44f4d98543672a819e692a9d67e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lauzier?= Date: Sun, 8 Aug 2021 03:34:38 -0400 Subject: [PATCH 3/6] Fix some clippy warnings --- clippy.toml | 2 ++ src/de/mod.rs | 4 ++-- src/parse.rs | 2 +- src/ser/mod.rs | 7 ++++--- src/value.rs | 4 ++-- tests/129_indexmap.rs | 4 ++-- tests/extensions.rs | 4 ++-- tests/value.rs | 6 +++++- 8 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 clippy.toml diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..aa9f871f5 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,2 @@ +msrv = "1.36" +blacklisted-names = [] diff --git a/src/de/mod.rs b/src/de/mod.rs index ed91bb143..af355b8b8 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -41,7 +41,7 @@ impl<'de> Deserializer<'de> { } pub fn remainder(&self) -> Cow<'_, str> { - String::from_utf8_lossy(&self.bytes.bytes()) + String::from_utf8_lossy(self.bytes.bytes()) } } @@ -315,7 +315,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { let string = self.bytes.string()?; let base64_str = match string { ParsedStr::Allocated(ref s) => s.as_str(), - ParsedStr::Slice(ref s) => s, + ParsedStr::Slice(s) => s, }; base64::decode(base64_str) }; diff --git a/src/parse.rs b/src/parse.rs index 126873290..d20c0f9b2 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -323,7 +323,7 @@ impl<'a> Bytes<'a> { } pub fn bytes(&self) -> &[u8] { - &self.bytes + self.bytes } pub fn char(&mut self) -> Result { diff --git a/src/ser/mod.rs b/src/ser/mod.rs index d8c4ce649..0f85ec4c2 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -412,8 +412,8 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { fn serialize_f32(self, v: f32) -> Result<()> { write!(self.output, "{}", v)?; - // TODO: use f32::EPSILON when minimum supported rust version is 1.43 - pub const EPSILON: f32 = 1.192_092_9e-7; + #[allow(clippy::excessive_precision)] + pub const EPSILON: f32 = 1.192_092_90e-07_f32; if self.decimal_floats() && (v - v.floor()).abs() < EPSILON { write!(self.output, ".0")?; } @@ -423,7 +423,8 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { fn serialize_f64(self, v: f64) -> Result<()> { write!(self.output, "{}", v)?; // TODO: use f64::EPSILON when minimum supported rust version is 1.43 - pub const EPSILON: f64 = 2.220_446_049_250_313e-16; + #[allow(clippy::excessive_precision)] + pub const EPSILON: f64 = 2.220_446_049_250_313e-16_f64; if self.decimal_floats() && (v - v.floor()).abs() < EPSILON { write!(self.output, ".0")?; } diff --git a/src/value.rs b/src/value.rs index c7a435b04..339288a43 100644 --- a/src/value.rs +++ b/src/value.rs @@ -37,8 +37,8 @@ impl Map { } /// Returns `true` if `self.len() == 0`, `false` otherwise. - pub fn is_empty(&self) -> usize { - self.0.len() + pub fn is_empty(&self) -> bool { + self.0.len() == 0 } /// Inserts a new element, returning the previous element with this `key` if diff --git a/tests/129_indexmap.rs b/tests/129_indexmap.rs index a10a11e27..ccaaaef96 100644 --- a/tests/129_indexmap.rs +++ b/tests/129_indexmap.rs @@ -30,7 +30,7 @@ tasks: { Value::String("debug message".to_string()) ); assert_eq!( - *map.keys().skip(1).next().unwrap(), + *map.keys().nth(1).unwrap(), Value::String("shell command".to_string()) ); } @@ -65,7 +65,7 @@ tasks: { Value::String("shell command".to_string()) ); assert_eq!( - *map.keys().skip(1).next().unwrap(), + *map.keys().nth(1).unwrap(), Value::String("debug message".to_string()) ); } diff --git a/tests/extensions.rs b/tests/extensions.rs index 81157257a..bc07d699b 100644 --- a/tests/extensions.rs +++ b/tests/extensions.rs @@ -47,7 +47,7 @@ const CONFIG_U_NT: &str = " #[test] fn unwrap_newtypes() { - let d: Struct = ron::de::from_str(&CONFIG_U_NT).expect("Failed to deserialize"); + let d: Struct = ron::de::from_str(CONFIG_U_NT).expect("Failed to deserialize"); println!("unwrap_newtypes: {:#?}", d); } @@ -75,7 +75,7 @@ const CONFIG_I_S: &str = " #[test] fn implicit_some() { - let d: Struct = ron::de::from_str(&CONFIG_I_S).expect("Failed to deserialize"); + let d: Struct = ron::de::from_str(CONFIG_I_S).expect("Failed to deserialize"); println!("implicit_some: {:#?}", d); } diff --git a/tests/value.rs b/tests/value.rs index 1e1ff5b99..f71bb3dd3 100644 --- a/tests/value.rs +++ b/tests/value.rs @@ -1,5 +1,6 @@ use ron::value::{Map, Number, Value}; use serde::Serialize; +use std::f64; #[test] fn bool() { @@ -23,7 +24,10 @@ fn map() { #[test] fn number() { assert_eq!("42".parse(), Ok(Value::Number(Number::new(42)))); - assert_eq!("3.1415".parse(), Ok(Value::Number(Number::new(3.1415f64)))); + assert_eq!( + "3.141592653589793".parse(), + Ok(Value::Number(Number::new(f64::consts::PI))) + ); } #[test] From e611b2710eb7e79bafc9ecdd83864065d6a34905 Mon Sep 17 00:00:00 2001 From: Clemens Winter Date: Wed, 27 Oct 2021 19:42:17 -0700 Subject: [PATCH 4/6] Fix some more clippy warnings --- src/ser/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ser/tests.rs b/src/ser/tests.rs index b3543799f..f95de18b1 100644 --- a/src/ser/tests.rs +++ b/src/ser/tests.rs @@ -89,10 +89,10 @@ fn test_map() { map.insert((false, false), 123); let s = to_string(&map).unwrap(); - s.starts_with("{"); + s.starts_with('{'); s.contains("(true,false):4"); s.contains("(false,false):123"); - s.ends_with("}"); + s.ends_with('}'); } #[test] From 3669ecf762792d7bc9fd7da6dba4f25bd56a378c Mon Sep 17 00:00:00 2001 From: Clemens Winter Date: Tue, 26 Oct 2021 07:50:21 -0700 Subject: [PATCH 5/6] Add Value::Struct variant and preserve struct names and formatting --- src/de/mod.rs | 61 +++++++++++++++--- src/de/value.rs | 73 +++++++++++----------- src/ser/value.rs | 1 + src/value.rs | 159 +++++++++++++++++++++++++++++++++++++++++++++++ tests/value.rs | 1 + 5 files changed, 252 insertions(+), 43 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index af355b8b8..8dff57878 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,8 +1,10 @@ /// Deserialization module. pub use crate::error::{Error, ErrorCode, Result}; pub use crate::parse::Position; +use crate::{Map, Value}; use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor}; +use std::cell::RefCell; use std::{borrow::Cow, io, str}; use self::{id::IdDeserializer, tag::TagDeserializer}; @@ -24,6 +26,7 @@ mod value; pub struct Deserializer<'de> { bytes: Bytes<'de>, newtype_variant: bool, + struct_name: Option, } impl<'de> Deserializer<'de> { @@ -37,6 +40,7 @@ impl<'de> Deserializer<'de> { Ok(Deserializer { bytes: Bytes::new(input)?, newtype_variant: false, + struct_name: None, }) } @@ -150,14 +154,17 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { // `identifier` does not change state if it fails let ident = self.bytes.identifier().ok(); - if ident.is_some() { + if let Some(ident) = ident { self.bytes.skip_ws()?; - + self.struct_name = Some(String::from_utf8(ident.to_vec()).unwrap()); return self.handle_any_struct(visitor); } match self.bytes.peek_or_eof()? { - b'(' => self.handle_any_struct(visitor), + b'(' => { + self.struct_name = None; + self.handle_any_struct(visitor) + } b'[' => self.deserialize_seq(visitor), b'{' => self.deserialize_map(visitor), b'0'..=b'9' | b'+' | b'-' => { @@ -415,7 +422,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { if self.bytes.consume("[") { - let value = visitor.visit_seq(CommaSeparated::new(b']', &mut self))?; + let value = visitor.visit_seq(CommaSeparated::new(b']', None, &mut self))?; self.bytes.comma()?; if self.bytes.consume("]") { @@ -436,7 +443,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; - let value = visitor.visit_seq(CommaSeparated::new(b')', &mut self))?; + let value = visitor.visit_seq(CommaSeparated::new(b')', None, &mut self))?; self.bytes.comma()?; if old_newtype_variant || self.bytes.consume(")") { @@ -470,7 +477,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { if self.bytes.consume("{") { - let value = visitor.visit_map(CommaSeparated::new(b'}', &mut self))?; + let value = visitor.visit_map(CommaSeparated::new(b'}', None, &mut self))?; self.bytes.comma()?; if self.bytes.consume("}") { @@ -502,7 +509,17 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; - let value = visitor.visit_map(CommaSeparated::new(b')', &mut self))?; + let value = visitor.visit_map(CommaSeparated::new( + b')', + Some(( + self.struct_name + .as_ref() + .map(|s| s.as_bytes().into()) + .unwrap_or_default(), + RefCell::new(0), + )), + &mut self, + ))?; self.bytes.comma()?; if old_newtype_variant || self.bytes.consume(")") { @@ -546,14 +563,26 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { struct CommaSeparated<'a, 'de: 'a> { de: &'a mut Deserializer<'de>, + // If we are parsing a struct, this is set to the name of the struct and a counter + // that indicates how many bytes of the name have been read by the ValueVisitor + // constructing the Map/Struct. + // Serde does not provide a way to distinguish between structs and maps. However, + // we can abuse multiple calls to the `size_hint` method to access this information + // one byte at a time. + struct_name: Option<(Vec, RefCell)>, terminator: u8, had_comma: bool, } impl<'a, 'de> CommaSeparated<'a, 'de> { - fn new(terminator: u8, de: &'a mut Deserializer<'de>) -> Self { + fn new( + terminator: u8, + struct_name: Option<(Vec, RefCell)>, + de: &'a mut Deserializer<'de>, + ) -> Self { CommaSeparated { de, + struct_name, terminator, had_comma: true, } @@ -626,6 +655,22 @@ impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { self.err(ErrorCode::ExpectedMapColon) } } + + fn size_hint(&self) -> Option { + // trolololololol + match &self.struct_name { + Some((bytes, offset)) if *offset.borrow() <= bytes.len() => { + let i = *offset.borrow(); + *offset.borrow_mut() += 1; + if bytes.len() == i { + Some(0) + } else { + Some(bytes[i] as usize) + } + } + _ => None, + } + } } struct Enum<'a, 'de: 'a> { diff --git a/src/de/value.rs b/src/de/value.rs index 92acca206..da7dbc1a5 100644 --- a/src/de/value.rs +++ b/src/de/value.rs @@ -5,6 +5,7 @@ use serde::{ Deserialize, Deserializer, }; +use crate::value::Struct; use crate::{ de, value::{Map, Number, Value}, @@ -169,13 +170,32 @@ impl<'de> Visitor<'de> for ValueVisitor { where A: MapAccess<'de>, { - let mut res: Map = Map::new(); - - while let Some(entry) = map.next_entry()? { - res.insert(entry.0, entry.1); + let mut struct_name: Option> = None; + while let Some(byte) = map.size_hint() { + struct_name.get_or_insert_with(Vec::new).push(byte as u8); + } + match struct_name { + Some(mut bytes) => { + let name = if bytes.len() > 1 { + bytes.pop(); + Some(String::from_utf8(bytes).unwrap()) + } else { + None + }; + let mut res: Struct = Struct::new(name); + while let Some((Value::String(field), value)) = map.next_entry()? { + res.insert(field, value); + } + Ok(Value::Struct(res)) + } + None => { + let mut res: Map = Map::new(); + while let Some(entry) = map.next_entry()? { + res.insert(entry.0, entry.1); + } + Ok(Value::Map(res)) + } } - - Ok(Value::Map(res)) } } @@ -272,40 +292,23 @@ mod tests { ])" ), Value::Option(Some(Box::new(Value::Seq(vec![ - Value::Map( - vec![ - ( - Value::String("width".to_owned()), - Value::Number(Number::new(20)), - ), - ( - Value::String("height".to_owned()), - Value::Number(Number::new(5)), - ), - ( - Value::String("name".to_owned()), - Value::String("The Room".to_owned()), - ), + Value::Struct(Struct { + name: Some("Room".to_string()), + fields: vec![ + ("width".to_owned(), Value::Number(Number::new(20)),), + ("height".to_owned(), Value::Number(Number::new(5)),), + ("name".to_owned(), Value::String("The Room".to_owned()),), ] .into_iter() .collect(), - ), - Value::Map( + }), + Value::Struct( vec![ + ("width".to_owned(), Value::Number(Number::new(10.0)),), + ("height".to_owned(), Value::Number(Number::new(10.0)),), + ("name".to_owned(), Value::String("Another room".to_owned()),), ( - Value::String("width".to_owned()), - Value::Number(Number::new(10.0)), - ), - ( - Value::String("height".to_owned()), - Value::Number(Number::new(10.0)), - ), - ( - Value::String("name".to_owned()), - Value::String("Another room".to_owned()), - ), - ( - Value::String("enemy_levels".to_owned()), + "enemy_levels".to_owned(), Value::Map( vec![ ( diff --git a/src/ser/value.rs b/src/ser/value.rs index 1a4775898..fea5356b9 100644 --- a/src/ser/value.rs +++ b/src/ser/value.rs @@ -11,6 +11,7 @@ impl Serialize for Value { Value::Bool(b) => serializer.serialize_bool(b), Value::Char(c) => serializer.serialize_char(c), Value::Map(ref m) => Serialize::serialize(m, serializer), + Value::Struct(ref s) => Serialize::serialize(s, serializer), Value::Number(Number::Float(ref f)) => serializer.serialize_f64(f.get()), Value::Number(Number::Integer(i)) => serializer.serialize_i64(i), Value::Option(Some(ref o)) => serializer.serialize_some(o.as_ref()), diff --git a/src/value.rs b/src/value.rs index 339288a43..a361c1a81 100644 --- a/src/value.rs +++ b/src/value.rs @@ -138,6 +138,130 @@ pub enum Number { Float(Float), } +/// A `Struct` to `Value` map. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct Struct { + pub name: Option, + pub fields: StructInner, +} + +impl Struct { + /// Creates a new, empty `Struct`. + pub fn new(name: Option) -> Struct { + Struct { + name, + fields: Default::default(), + } + } + + /// Returns the number of elements in the map. + pub fn len(&self) -> usize { + self.fields.len() + } + + /// Returns `true` if `self.len() == 0`, `false` otherwise. + pub fn is_empty(&self) -> usize { + self.fields.len() + } + + /// Inserts a new element, returning the previous element with this `key` if + /// there was any. + pub fn insert(&mut self, key: String, value: Value) -> Option { + self.fields.insert(key, value) + } + + /// Removes an element by its `key`. + pub fn remove(&mut self, key: &str) -> Option { + self.fields.remove(key) + } + + /// Iterate all key-value pairs. + pub fn iter(&self) -> impl Iterator + DoubleEndedIterator { + self.fields.iter() + } + + /// Iterate all key-value pairs mutably. + pub fn iter_mut( + &mut self, + ) -> impl Iterator + DoubleEndedIterator { + self.fields.iter_mut() + } + + /// Iterate all keys. + pub fn keys(&self) -> impl Iterator + DoubleEndedIterator { + self.fields.keys() + } + + /// Iterate all values. + pub fn values(&self) -> impl Iterator + DoubleEndedIterator { + self.fields.values() + } + + /// Iterate all values mutably. + pub fn values_mut(&mut self) -> impl Iterator + DoubleEndedIterator { + self.fields.values_mut() + } +} + +impl FromIterator<(String, Value)> for Struct { + fn from_iter>(iter: T) -> Self { + Struct { + name: None, + fields: StructInner::from_iter(iter), + } + } +} + +/// Note: equality is only given if both values and order of values match +impl Eq for Struct {} + +impl Hash for Struct { + fn hash(&self, state: &mut H) { + self.iter().for_each(|x| x.hash(state)); + } +} + +impl Index<&str> for Struct { + type Output = Value; + + fn index(&self, index: &str) -> &Self::Output { + &self.fields[index] + } +} + +impl IndexMut<&str> for Struct { + fn index_mut(&mut self, index: &str) -> &mut Self::Output { + self.fields.get_mut(index).expect("no entry found for key") + } +} + +impl Ord for Struct { + fn cmp(&self, other: &Struct) -> Ordering { + self.iter().cmp(other.iter()) + } +} + +/// Note: equality is only given if both values and order of values match +impl PartialEq for Struct { + fn eq(&self, other: &Struct) -> bool { + self.iter().zip(other.iter()).all(|(a, b)| a == b) && self.name == other.name + } +} + +impl PartialOrd for Struct { + fn partial_cmp(&self, other: &Struct) -> Option { + match self.name.cmp(&other.name) { + Ordering::Equal => self.iter().partial_cmp(other.iter()), + o => Some(o), + } + } +} + +#[cfg(not(feature = "indexmap"))] +type StructInner = std::collections::BTreeMap; +#[cfg(feature = "indexmap")] +type StructInner = indexmap::IndexMap; + /// A wrapper for `f64`, which guarantees that the inner value /// is finite and thus implements `Eq`, `Hash` and `Ord`. #[derive(Copy, Clone, Debug)] @@ -319,6 +443,7 @@ pub enum Value { Bool(bool), Char(char), Map(Map), + Struct(Struct), Number(Number), Option(Option>), String(String), @@ -358,6 +483,10 @@ impl<'de> Deserializer<'de> for Value { keys: m.keys().cloned().rev().collect(), values: m.values().cloned().rev().collect(), }), + Value::Struct(s) => visitor.visit_map(StructAccessor { + fields: s.keys().cloned().rev().collect(), + values: s.values().cloned().rev().collect(), + }), Value::Number(Number::Float(ref f)) => visitor.visit_f64(f.get()), Value::Number(Number::Integer(i)) => visitor.visit_i64(i), Value::Option(Some(o)) => visitor.visit_some(*o), @@ -464,6 +593,36 @@ impl<'de> MapAccess<'de> for MapAccessor { } } +struct StructAccessor { + fields: Vec, + values: Vec, +} + +impl<'de> MapAccess<'de> for StructAccessor { + type Error = RonError; + + fn next_key_seed(&mut self, seed: K) -> Result> + where + K: DeserializeSeed<'de>, + { + // The `Vec` is reversed, so we can pop to get the originally first element + self.fields + .pop() + .map_or(Ok(None), |v| seed.deserialize(Value::String(v)).map(Some)) + } + + fn next_value_seed(&mut self, seed: V) -> Result + where + V: DeserializeSeed<'de>, + { + // The `Vec` is reversed, so we can pop to get the originally first element + self.values + .pop() + .map(|v| seed.deserialize(v)) + .expect("Contract violation") + } +} + struct Seq { seq: Vec, } diff --git a/tests/value.rs b/tests/value.rs index f71bb3dd3..afd60e541 100644 --- a/tests/value.rs +++ b/tests/value.rs @@ -22,6 +22,7 @@ fn map() { } #[test] +#[allow(clippy::approx_constant)] fn number() { assert_eq!("42".parse(), Ok(Value::Number(Number::new(42)))); assert_eq!( From b85bf640fa37828b882abfb928ab5ebe89ad46a7 Mon Sep 17 00:00:00 2001 From: Clemens Winter Date: Wed, 27 Oct 2021 19:08:49 -0700 Subject: [PATCH 6/6] Add Value::to_string{,pretty} methods that preserve structs --- src/de/mod.rs | 1 - src/error.rs | 1 - src/ser/mod.rs | 128 ++++++++++++++++++++++++++++++++++++++++++----- src/ser/tests.rs | 67 +++++++++++++++++++++++++ src/ser/value.rs | 55 +++++++++++++++++++- src/value.rs | 6 +-- 6 files changed, 238 insertions(+), 20 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 8dff57878..3e906fdd9 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,7 +1,6 @@ /// Deserialization module. pub use crate::error::{Error, ErrorCode, Result}; pub use crate::parse::Position; -use crate::{Map, Value}; use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor}; use std::cell::RefCell; diff --git a/src/error.rs b/src/error.rs index 6430a45f8..7cf7145b4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -106,7 +106,6 @@ impl fmt::Display for ErrorCode { ErrorCode::UnderscoreAtBeginning => f.write_str("Found underscore at the beginning"), ErrorCode::UnexpectedByte(_) => f.write_str("Unexpected byte"), ErrorCode::TrailingCharacters => f.write_str("Non-whitespace trailing characters"), - _ => f.write_str("Unknown ErrorCode"), } } } diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 0d88afc23..67947f6ef 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,6 +1,7 @@ use serde::{ser, Deserialize, Serialize}; use std::io; +use crate::Value; use crate::{ error::{Error, Result}, extensions::Extensions, @@ -56,6 +57,22 @@ where Ok(String::from_utf8(s.output).expect("Ron should be utf-8")) } +impl Value { + pub fn to_string(&self) -> Result { + let buf = Vec::new(); + let mut s = Serializer::new(buf, None)?; + self.enhanced_serialize(&mut s)?; + Ok(String::from_utf8(s.output).expect("Ron should be utf-8")) + } + + pub fn to_string_pretty(&self, config: PrettyConfig) -> Result { + let buf = Vec::new(); + let mut s = Serializer::new(buf, Some(config))?; + self.enhanced_serialize(&mut s)?; + Ok(String::from_utf8(s.output).expect("Ron should be utf-8")) + } +} + /// Pretty serializer state struct Pretty { indent: usize, @@ -86,7 +103,7 @@ pub struct PrettyConfig { /// Indentation string #[serde(default = "default_indentor")] pub indentor: String, - // Whether to emit struct names + /// Whether to emit struct names #[serde(default = "default_struct_names")] pub struct_names: bool, /// Separate tuple members with indentation @@ -368,6 +385,21 @@ impl Serializer { .map(|(pc, _)| pc.struct_names) .unwrap_or(false) } + + fn serialize_struct_dyn<'b>(&mut self, name: &'b str, len: usize) -> Result> { + if self.struct_names() { + self.write_identifier(name)?; + } + self.output.write_all(b"(")?; + + self.is_empty = Some(len == 0); + self.start_indent()?; + + Ok(Compound { + ser: self, + state: State::First, + }) + } } impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { @@ -631,18 +663,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { } fn serialize_struct(self, name: &'static str, len: usize) -> Result { - if self.struct_names() { - self.write_identifier(name)?; - } - self.output.write_all(b"(")?; - - self.is_empty = Some(len == 0); - self.start_indent()?; - - Ok(Compound { - ser: self, - state: State::First, - }) + self.serialize_struct_dyn(name, len) } fn serialize_struct_variant( @@ -866,6 +887,87 @@ impl<'a, W: io::Write> ser::SerializeMap for Compound<'a, W> { } } +impl<'a, W: io::Write> Compound<'a, W> { + fn serialize_field_dyn(&mut self, key: &str, value: &Value) -> Result<()> { + if let State::First = self.state { + self.state = State::Rest; + } else { + self.ser.output.write_all(b",")?; + + if let Some((ref config, ref pretty)) = self.ser.pretty { + if pretty.indent <= config.depth_limit { + self.ser.output.write_all(config.new_line.as_bytes())?; + } + } + } + self.ser.indent()?; + self.ser.write_identifier(key)?; + self.ser.output.write_all(b":")?; + + if self.ser.is_pretty() { + self.ser.output.write_all(b" ")?; + } + + value.enhanced_serialize(&mut *self.ser)?; + + Ok(()) + } + + fn serialize_element_dyn(&mut self, value: &Value) -> Result<()> { + if let State::First = self.state { + self.state = State::Rest; + } else { + self.ser.output.write_all(b",")?; + if let Some((ref config, ref mut pretty)) = self.ser.pretty { + if pretty.indent <= config.depth_limit { + if config.enumerate_arrays { + assert!(config.new_line.contains('\n')); + let index = pretty.sequence_index.last_mut().unwrap(); + //TODO: when /**/ comments are supported, prepend the index + // to an element instead of appending it. + write!(self.ser.output, "// [{}]", index).unwrap(); + *index += 1; + } + self.ser.output.write_all(config.new_line.as_bytes())?; + } + } + } + self.ser.indent()?; + + value.enhanced_serialize(&mut *self.ser)?; + + Ok(()) + } + + fn serialize_key_dyn(&mut self, key: &Value) -> Result<()> { + if let State::First = self.state { + self.state = State::Rest; + } else { + self.ser.output.write_all(b",")?; + + if let Some((ref config, ref pretty)) = self.ser.pretty { + if pretty.indent <= config.depth_limit { + self.ser.output.write_all(config.new_line.as_bytes())?; + } + } + } + self.ser.indent()?; + key.enhanced_serialize(&mut *self.ser) + } + + fn serialize_value_dyn(&mut self, value: &Value) -> Result<()> { + self.ser.output.write_all(b":")?; + + if self.ser.is_pretty() { + self.ser.output.write_all(b" ")?; + } + + value.enhanced_serialize(&mut *self.ser)?; + + Ok(()) + } +} + impl<'a, W: io::Write> ser::SerializeStruct for Compound<'a, W> { type Error = Error; type Ok = (); diff --git a/src/ser/tests.rs b/src/ser/tests.rs index f95de18b1..67aa75135 100644 --- a/src/ser/tests.rs +++ b/src/ser/tests.rs @@ -1,4 +1,7 @@ use super::to_string; +use crate::ser::PrettyConfig; +use crate::value::Struct; +use crate::{Map, Number, Value}; use serde::Serialize; #[derive(Serialize)] @@ -144,3 +147,67 @@ fn rename() { assert_eq!(to_string(&Foo::D2).unwrap(), "r#2d"); assert_eq!(to_string(&Foo::TriangleList).unwrap(), "r#triangle-list"); } + +#[test] +fn test_value_to_string() { + assert_eq!( + Value::Struct(Struct { + name: Some("Point".to_owned()), + fields: [ + ("x".to_owned(), Value::Number(Number::from(16.3))), + ("y".to_owned(), Value::Number(Number::from(3.3))), + ] + .iter() + .cloned() + .collect() + }) + .to_string_pretty(PrettyConfig::default().struct_names(true)), + Ok("Point(\n x: 16.3,\n y: 3.3,\n)".to_string()) + ) +} + +#[test] +fn test_nested_value_to_string() { + assert_eq!( + Value::Struct(Struct { + name: Some("Point".to_owned()), + fields: [ + ( + "x".to_owned(), + Value::Seq(vec![Value::Map(Map(vec![( + Value::String("a".to_owned()), + Value::Struct(Struct { + name: Some("InnerStruct".to_owned()), + fields: [ + ("x".to_owned(), Value::Number(Number::from(16.3))), + ("y".to_owned(), Value::Number(Number::from(3.3))), + ] + .iter() + .cloned() + .collect() + }) + ),] + .into_iter() + .collect()))]) + ), + ("y".to_owned(), Value::Number(Number::from(3.3))), + ] + .iter() + .cloned() + .collect() + }) + .to_string_pretty(PrettyConfig::default().struct_names(true)), + Ok("Point( + x: [ + { + \"a\": InnerStruct( + x: 16.3, + y: 3.3, + ), + }, + ], + y: 3.3, +)" + .to_string()) + ); +} diff --git a/src/ser/value.rs b/src/ser/value.rs index fea5356b9..0b0ea7de4 100644 --- a/src/ser/value.rs +++ b/src/ser/value.rs @@ -1,6 +1,9 @@ -use serde::ser::{Serialize, Serializer}; +use std::io; + +use serde::ser::{Serialize, SerializeMap, SerializeSeq, SerializeStruct, Serializer}; use crate::value::{Number, Value}; +use crate::{Error, Map}; impl Serialize for Value { fn serialize(&self, serializer: S) -> Result @@ -11,7 +14,14 @@ impl Serialize for Value { Value::Bool(b) => serializer.serialize_bool(b), Value::Char(c) => serializer.serialize_char(c), Value::Map(ref m) => Serialize::serialize(m, serializer), - Value::Struct(ref s) => Serialize::serialize(s, serializer), + Value::Struct(ref s) => Serialize::serialize( + &Map(s + .fields + .iter() + .map(|(k, v)| (Value::String(k.clone()), v.clone())) + .collect()), + serializer, + ), Value::Number(Number::Float(ref f)) => serializer.serialize_f64(f.get()), Value::Number(Number::Integer(i)) => serializer.serialize_i64(i), Value::Option(Some(ref o)) => serializer.serialize_some(o.as_ref()), @@ -22,3 +32,44 @@ impl Serialize for Value { } } } + +impl Value { + pub fn enhanced_serialize( + &self, + serializer: &mut crate::ser::Serializer, + ) -> Result<(), Error> { + match *self { + Value::Bool(b) => serializer.serialize_bool(b), + Value::Char(c) => serializer.serialize_char(c), + Value::Map(ref m) => { + let mut map = serializer.serialize_map(Some(m.len()))?; + for (k, v) in m.iter() { + map.serialize_key_dyn(k)?; + map.serialize_value_dyn(v)?; + } + SerializeMap::end(map) + } + Value::Struct(ref s) => { + let mut c = serializer + .serialize_struct_dyn(s.name.as_deref().unwrap_or(""), s.fields.len())?; + for (field, value) in s.iter() { + c.serialize_field_dyn(field.as_ref(), value)?; + } + SerializeStruct::end(c) + } + Value::Number(Number::Float(ref f)) => serializer.serialize_f64(f.get()), + Value::Number(Number::Integer(i)) => serializer.serialize_i64(i), + Value::Option(Some(ref o)) => serializer.serialize_some(o.as_ref()), + Value::Option(None) => serializer.serialize_none(), + Value::String(ref s) => serializer.serialize_str(s), + Value::Seq(ref s) => { + let mut seq = serializer.serialize_seq(Some(s.len()))?; + for v in s.iter() { + seq.serialize_element_dyn(v)?; + } + SerializeSeq::end(seq) + } + Value::Unit => serializer.serialize_unit(), + } + } +} diff --git a/src/value.rs b/src/value.rs index a361c1a81..456127a3c 100644 --- a/src/value.rs +++ b/src/value.rs @@ -23,7 +23,7 @@ use crate::de::{Error as RonError, Result}; /// The latter can be used by enabling the `indexmap` feature. This can be used /// to preserve the order of the parsed map. #[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct Map(MapInner); +pub struct Map(pub MapInner); impl Map { /// Creates a new, empty `Map`. @@ -160,8 +160,8 @@ impl Struct { } /// Returns `true` if `self.len() == 0`, `false` otherwise. - pub fn is_empty(&self) -> usize { - self.fields.len() + pub fn is_empty(&self) -> bool { + self.fields.is_empty() } /// Inserts a new element, returning the previous element with this `key` if