From 1869817fab553218d665d9e6651cd9f6c37cbc3b Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Tue, 16 Aug 2022 21:12:57 +0300 Subject: [PATCH 01/29] Expected bytes error message and imports cleanup (#400) * Format expected bytes error with base64 as in ron * Reformat imports to be consistent --- examples/decode.rs | 3 ++- examples/decode_file.rs | 3 ++- examples/encode.rs | 3 ++- rustfmt.toml | 2 +- src/de/mod.rs | 4 ++-- src/de/tests.rs | 3 ++- src/de/value.rs | 9 ++++++--- src/error.rs | 15 +++++---------- src/options.rs | 10 ++++++---- src/ser/mod.rs | 3 ++- src/ser/tests.rs | 3 ++- src/value.rs | 9 +++++---- tests/123_enum_representation.rs | 3 ++- tests/147_empty_sets_serialisation.rs | 3 ++- tests/250_variant_newtypes.rs | 5 ++++- tests/depth_limit.rs | 3 ++- tests/escape.rs | 3 ++- tests/extensions.rs | 3 ++- tests/numbers.rs | 6 ++++-- tests/options.rs | 3 +-- tests/preserve_sequence.rs | 3 ++- tests/roundtrip.rs | 2 +- tests/to_string_pretty.rs | 6 ++++-- tests/value.rs | 3 ++- 24 files changed, 65 insertions(+), 45 deletions(-) diff --git a/examples/decode.rs b/examples/decode.rs index 56a9f3007..5aa2042fa 100644 --- a/examples/decode.rs +++ b/examples/decode.rs @@ -1,8 +1,9 @@ #![allow(dead_code)] +use std::collections::HashMap; + use ron::de::from_str; use serde::Deserialize; -use std::collections::HashMap; #[derive(Debug, Deserialize)] struct Config { diff --git a/examples/decode_file.rs b/examples/decode_file.rs index 691e9367e..72ca8a05f 100644 --- a/examples/decode_file.rs +++ b/examples/decode_file.rs @@ -1,8 +1,9 @@ #![allow(dead_code)] +use std::{collections::HashMap, fs::File}; + use ron::de::from_reader; use serde::Deserialize; -use std::{collections::HashMap, fs::File}; #[derive(Debug, Deserialize)] struct Config { diff --git a/examples/encode.rs b/examples/encode.rs index ad1a237ba..30e968895 100644 --- a/examples/encode.rs +++ b/examples/encode.rs @@ -1,6 +1,7 @@ +use std::{collections::HashMap, iter::FromIterator}; + use ron::ser::{to_string_pretty, PrettyConfig}; use serde::Serialize; -use std::{collections::HashMap, iter::FromIterator}; #[derive(Serialize)] struct Config { diff --git a/rustfmt.toml b/rustfmt.toml index ff0319191..95127ec82 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,4 @@ hard_tabs = false use_field_init_shorthand = true use_try_shorthand = true -edition = "2018" +edition = "2021" diff --git a/src/de/mod.rs b/src/de/mod.rs index 0920a67b4..4af28cdfe 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,10 +1,10 @@ /// Deserialization module. -pub use crate::error::{Error, Position, SpannedError}; +use std::{borrow::Cow, io, str}; use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor}; -use std::{borrow::Cow, io, str}; use self::{id::IdDeserializer, tag::TagDeserializer}; +pub use crate::error::{Error, Position, SpannedError}; use crate::{ error::{Result, SpannedResult}, extensions::Extensions, diff --git a/src/de/tests.rs b/src/de/tests.rs index 3e37e4e9d..1dbae0e93 100644 --- a/src/de/tests.rs +++ b/src/de/tests.rs @@ -162,9 +162,10 @@ fn err(kind: Error, line: usize, col: usize) -> SpannedResult { #[test] fn test_err_wrong_value() { - use self::Error::*; use std::collections::HashMap; + use self::Error::*; + assert_eq!(from_str::("'c'"), err(ExpectedFloat, 1, 1)); assert_eq!(from_str::("'c'"), err(ExpectedString, 1, 1)); assert_eq!(from_str::>("'c'"), err(ExpectedMap, 1, 1)); diff --git a/src/de/value.rs b/src/de/value.rs index 544ee05e3..5883a37ad 100644 --- a/src/de/value.rs +++ b/src/de/value.rs @@ -5,8 +5,10 @@ use serde::{ Deserialize, Deserializer, }; -use crate::error::SpannedResult; -use crate::value::{Map, Number, Value}; +use crate::{ + error::SpannedResult, + value::{Map, Number, Value}, +}; impl std::str::FromStr for Value { type Err = crate::error::SpannedError; @@ -181,9 +183,10 @@ impl<'de> Visitor<'de> for ValueVisitor { #[cfg(test)] mod tests { - use super::*; use std::str::FromStr; + use super::*; + fn eval(s: &str) -> Value { s.parse().expect("Failed to parse") } diff --git a/src/error.rs b/src/error.rs index 7f7cc9b93..d64575452 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,7 @@ -use serde::{de, ser}; use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error}; +use serde::{de, ser}; + /// This type represents all possible errors that can occur when /// serializing or deserializing RON data. #[derive(Clone, Debug, PartialEq, Eq)] @@ -276,15 +277,9 @@ impl de::Error for Error { Float(n) => write!(f, "the floating point number `{}`", n), Char(c) => write!(f, "the UTF-8 character `{}`", c), Str(s) => write!(f, "the string {:?}", s), - Bytes(b) => { - f.write_str("the bytes b\"")?; - - for b in b { - write!(f, "\\x{:02x}", b)?; - } - - f.write_str("\"") - } + Bytes(b) => write!(f, "the bytes \"{}\"", { + base64::display::Base64Display::with_config(b, base64::STANDARD) + }), Unit => write!(f, "a unit value"), Option => write!(f, "an optional value"), NewtypeStruct => write!(f, "a newtype struct"), diff --git a/src/options.rs b/src/options.rs index 1e3511812..d550116ee 100644 --- a/src/options.rs +++ b/src/options.rs @@ -4,10 +4,12 @@ use std::io; use serde::{de, ser, Deserialize, Serialize}; -use crate::de::Deserializer; -use crate::error::{Result, SpannedResult}; -use crate::extensions::Extensions; -use crate::ser::{PrettyConfig, Serializer}; +use crate::{ + de::Deserializer, + error::{Result, SpannedResult}, + extensions::Extensions, + ser::{PrettyConfig, Serializer}, +}; /// Roundtrip serde options. /// diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f723d15e0..507f9f609 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,6 +1,7 @@ -use serde::{ser, Deserialize, Serialize}; use std::io; +use serde::{ser, Deserialize, Serialize}; + use crate::{ error::{Error, Result}, extensions::Extensions, diff --git a/src/ser/tests.rs b/src/ser/tests.rs index f1056e657..8b1b6c845 100644 --- a/src/ser/tests.rs +++ b/src/ser/tests.rs @@ -1,6 +1,7 @@ -use super::to_string; use serde::Serialize; +use super::to_string; + #[derive(Serialize)] struct EmptyStruct1; diff --git a/src/value.rs b/src/value.rs index 565bfa05e..46aceb6ec 100644 --- a/src/value.rs +++ b/src/value.rs @@ -12,8 +12,7 @@ use serde::{ forward_to_deserialize_any, Deserialize, Serialize, }; -use crate::de::Error; -use crate::error::Result; +use crate::{de::Error, error::Result}; /// A `Value` to `Value` map. /// @@ -484,10 +483,12 @@ impl<'de> SeqAccess<'de> for Seq { #[cfg(test)] mod tests { - use super::*; - use serde::Deserialize; use std::{collections::BTreeMap, fmt::Debug}; + use serde::Deserialize; + + use super::*; + fn assert_same<'de, T>(s: &'de str) where T: Debug + Deserialize<'de> + PartialEq, diff --git a/tests/123_enum_representation.rs b/tests/123_enum_representation.rs index 3f82b08cd..722baa8e3 100644 --- a/tests/123_enum_representation.rs +++ b/tests/123_enum_representation.rs @@ -1,6 +1,7 @@ +use std::{cmp::PartialEq, fmt::Debug}; + use ron::{de::from_str, ser::to_string}; use serde::{Deserialize, Serialize}; -use std::{cmp::PartialEq, fmt::Debug}; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] enum Inner { diff --git a/tests/147_empty_sets_serialisation.rs b/tests/147_empty_sets_serialisation.rs index 81cabf64f..d93f9dd11 100644 --- a/tests/147_empty_sets_serialisation.rs +++ b/tests/147_empty_sets_serialisation.rs @@ -1,6 +1,7 @@ -use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use serde::{Deserialize, Serialize}; + #[derive(Debug, PartialEq, Deserialize, Serialize)] struct UnitStruct; diff --git a/tests/250_variant_newtypes.rs b/tests/250_variant_newtypes.rs index f4c76606c..55673e92b 100644 --- a/tests/250_variant_newtypes.rs +++ b/tests/250_variant_newtypes.rs @@ -1,7 +1,10 @@ use std::collections::HashMap; use ron::{ - de::from_str, error::Error, extensions::Extensions, ser::to_string_pretty, ser::PrettyConfig, + de::from_str, + error::Error, + extensions::Extensions, + ser::{to_string_pretty, PrettyConfig}, }; use serde::{Deserialize, Serialize}; diff --git a/tests/depth_limit.rs b/tests/depth_limit.rs index 6d197bde9..ee61a09c8 100644 --- a/tests/depth_limit.rs +++ b/tests/depth_limit.rs @@ -1,6 +1,7 @@ -use serde::Serialize; use std::collections::HashMap; +use serde::Serialize; + #[derive(Serialize)] struct Config { float: (f32, f64), diff --git a/tests/escape.rs b/tests/escape.rs index 48f5fdaa1..2874c3f4f 100644 --- a/tests/escape.rs +++ b/tests/escape.rs @@ -1,6 +1,7 @@ +use std::{char::from_u32, fmt::Debug}; + use ron::{de::from_str, ser::to_string}; use serde::{Deserialize, Serialize}; -use std::{char::from_u32, fmt::Debug}; #[test] fn test_escape_basic() { diff --git a/tests/extensions.rs b/tests/extensions.rs index 6161d90b4..6b3ade87a 100644 --- a/tests/extensions.rs +++ b/tests/extensions.rs @@ -1,6 +1,7 @@ -use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use serde::{Deserialize, Serialize}; + #[derive(Debug, PartialEq, Deserialize, Serialize)] struct UnitStruct; diff --git a/tests/numbers.rs b/tests/numbers.rs index 4717ddb9f..bdd357478 100644 --- a/tests/numbers.rs +++ b/tests/numbers.rs @@ -1,5 +1,7 @@ -use ron::de::from_str; -use ron::error::{Error, Position, SpannedError}; +use ron::{ + de::from_str, + error::{Error, Position, SpannedError}, +}; #[test] fn test_hex() { diff --git a/tests/options.rs b/tests/options.rs index 89a122af8..0f6092468 100644 --- a/tests/options.rs +++ b/tests/options.rs @@ -1,6 +1,5 @@ -use serde::{Deserialize, Serialize}; - use ron::{extensions::Extensions, ser::PrettyConfig, Options}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] struct Newtype(f64); diff --git a/tests/preserve_sequence.rs b/tests/preserve_sequence.rs index 8ffb20d86..bdfe3575e 100644 --- a/tests/preserve_sequence.rs +++ b/tests/preserve_sequence.rs @@ -1,9 +1,10 @@ +use std::collections::BTreeMap; + use ron::{ de::from_str, ser::{to_string_pretty, PrettyConfig}, }; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; #[derive(Debug, Deserialize, Serialize)] struct Config { diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 7ca5bd8ad..cc168f024 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -1,7 +1,7 @@ -use serde::{Deserialize, Serialize}; use std::collections::HashMap; use ron::extensions::Extensions; +use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Deserialize, Serialize)] struct UnitStruct; diff --git a/tests/to_string_pretty.rs b/tests/to_string_pretty.rs index 0e1f93d1a..ad5c7ed26 100644 --- a/tests/to_string_pretty.rs +++ b/tests/to_string_pretty.rs @@ -1,5 +1,7 @@ -use ron::ser::{to_string_pretty, PrettyConfig}; -use ron::to_string; +use ron::{ + ser::{to_string_pretty, PrettyConfig}, + to_string, +}; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Deserialize, Serialize)] diff --git a/tests/value.rs b/tests/value.rs index 8256d3cf9..2fdecad00 100644 --- a/tests/value.rs +++ b/tests/value.rs @@ -1,6 +1,7 @@ +use std::f64; + use ron::value::{Map, Number, Value}; use serde::Serialize; -use std::f64; #[test] fn bool() { From 08b55ff85bea2ac009507b65f359bcc24b1ff18f Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Mon, 22 Aug 2022 10:35:37 +0300 Subject: [PATCH 02/29] Test for the array-Value roundtrip failure (#404) --- tests/238_array.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/238_array.rs diff --git a/tests/238_array.rs b/tests/238_array.rs new file mode 100644 index 000000000..8429f804c --- /dev/null +++ b/tests/238_array.rs @@ -0,0 +1,51 @@ +use ron::{ + error::{Error, Position, SpannedError}, + value::{Number, Value}, +}; + +#[test] +fn test_array() { + let array: [i32; 3] = [1, 2, 3]; + + let ser = ron::to_string(&array).unwrap(); + assert_eq!(ser, "(1,2,3)"); + + let de: [i32; 3] = ron::from_str(&ser).unwrap(); + assert_eq!(de, array); + + let value: Value = ron::from_str(&ser).unwrap(); + assert_eq!( + value, + Value::Seq(vec![ + Value::Number(Number::from(1)), + Value::Number(Number::from(2)), + Value::Number(Number::from(3)), + ]) + ); + + let ser = ron::to_string(&value).unwrap(); + assert_eq!(ser, "[1,2,3]"); + + let de: [i32; 3] = value.into_rust().unwrap(); + assert_eq!(de, array); + + // FIXME: fails and hence arrays do not roundtrip + let de: SpannedError = ron::from_str::<[i32; 3]>(&ser).unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::ExpectedStructLike, + position: Position { line: 1, col: 1 }, + } + ); + + let value: Value = ron::from_str(&ser).unwrap(); + assert_eq!( + value, + Value::Seq(vec![ + Value::Number(Number::from(1)), + Value::Number(Number::from(2)), + Value::Number(Number::from(3)), + ]) + ); +} From 1ad4db56e618d7404291f8001c8ee5f427eb142b Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Fri, 26 Aug 2022 11:29:20 +0300 Subject: [PATCH 03/29] Fix #277 and #405 with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` (#406) --- CHANGELOG.md | 2 ++ src/value.rs | 88 ++++++++++++++++++++++++++++++++++++++------------ tests/value.rs | 33 ++++++++++++++++++- 3 files changed, 102 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8e0e2656..0aa1aafce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Fix issues [#277](https://github.com/ron-rs/ron/issues/277) and [#405](https://github.com/ron-rs/ron/issues/405) with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` ([#406](https://github.com/ron-rs/ron/pull/406)) + ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) - Fix issue [#265](https://github.com/ron-rs/ron/issues/265) with better missing comma error ([#353](https://github.com/ron-rs/ron/pull/353)) diff --git a/src/value.rs b/src/value.rs index 46aceb6ec..1f4628692 100644 --- a/src/value.rs +++ b/src/value.rs @@ -83,6 +83,16 @@ impl FromIterator<(Value, Value)> for Map { } } +impl IntoIterator for Map { + type Item = (Value, Value); + + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + /// Note: equality is only given if both values and order of values match impl Eq for Map {} @@ -353,18 +363,45 @@ impl<'de> Deserializer<'de> for Value { match self { Value::Bool(b) => visitor.visit_bool(b), Value::Char(c) => visitor.visit_char(c), - Value::Map(m) => visitor.visit_map(MapAccessor { - keys: m.keys().cloned().rev().collect(), - values: m.values().cloned().rev().collect(), - }), + Value::Map(m) => { + let old_len = m.len(); + + let mut items: Vec<(Value, Value)> = m.into_iter().collect(); + items.reverse(); + + let value = visitor.visit_map(MapAccessor { + items: &mut items, + value: None, + })?; + + if items.is_empty() { + Ok(value) + } else { + Err(Error::ExpectedDifferentLength { + expected: format!("a map of length {}", old_len - items.len()), + found: old_len, + }) + } + } 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), Value::Option(None) => visitor.visit_none(), Value::String(s) => visitor.visit_string(s), Value::Seq(mut seq) => { + let old_len = seq.len(); + seq.reverse(); - visitor.visit_seq(Seq { seq }) + let value = visitor.visit_seq(Seq { seq: &mut seq })?; + + if seq.is_empty() { + Ok(value) + } else { + Err(Error::ExpectedDifferentLength { + expected: format!("a sequence of length {}", old_len - seq.len()), + found: old_len, + }) + } } Value::Unit => visitor.visit_unit(), } @@ -433,12 +470,12 @@ impl<'de> Deserializer<'de> for Value { } } -struct MapAccessor { - keys: Vec, - values: Vec, +struct MapAccessor<'a> { + items: &'a mut Vec<(Value, Value)>, + value: Option, } -impl<'de> MapAccess<'de> for MapAccessor { +impl<'a, 'de> MapAccess<'de> for MapAccessor<'a> { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result> @@ -446,28 +483,35 @@ impl<'de> MapAccess<'de> for MapAccessor { K: DeserializeSeed<'de>, { // The `Vec` is reversed, so we can pop to get the originally first element - self.keys - .pop() - .map_or(Ok(None), |v| seed.deserialize(v).map(Some)) + match self.items.pop() { + Some((key, value)) => { + self.value = Some(value); + seed.deserialize(key).map(Some) + } + None => Ok(None), + } } 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") + match self.value.take() { + Some(value) => seed.deserialize(value), + None => panic!("Contract violation: value before key"), + } + } + + fn size_hint(&self) -> Option { + Some(self.items.len()) } } -struct Seq { - seq: Vec, +struct Seq<'a> { + seq: &'a mut Vec, } -impl<'de> SeqAccess<'de> for Seq { +impl<'a, 'de> SeqAccess<'de> for Seq<'a> { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result> @@ -479,6 +523,10 @@ impl<'de> SeqAccess<'de> for Seq { .pop() .map_or(Ok(None), |v| seed.deserialize(v).map(Some)) } + + fn size_hint(&self) -> Option { + Some(self.seq.len()) + } } #[cfg(test)] diff --git a/tests/value.rs b/tests/value.rs index 2fdecad00..3919058c0 100644 --- a/tests/value.rs +++ b/tests/value.rs @@ -1,6 +1,9 @@ use std::f64; -use ron::value::{Map, Number, Value}; +use ron::{ + error::Error, + value::{Map, Number, Value}, +}; use serde::Serialize; #[test] @@ -68,6 +71,34 @@ fn seq() { Value::Number(Number::new(2f64)), ]; assert_eq!("[1, 2.0]".parse(), Ok(Value::Seq(seq))); + + let err = Value::Seq(vec![Value::Number(Number::new(1))]) + .into_rust::<[i32; 2]>() + .unwrap_err(); + + assert_eq!( + err, + Error::ExpectedDifferentLength { + expected: String::from("an array of length 2"), + found: 1, + } + ); + + let err = Value::Seq(vec![ + Value::Number(Number::new(1)), + Value::Number(Number::new(2)), + Value::Number(Number::new(3)), + ]) + .into_rust::<[i32; 2]>() + .unwrap_err(); + + assert_eq!( + err, + Error::ExpectedDifferentLength { + expected: String::from("a sequence of length 2"), + found: 3, + } + ); } #[test] From 33ef0819ffeb37e3bd746be85976479d4c84abdf Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Wed, 7 Sep 2022 18:59:58 +0300 Subject: [PATCH 04/29] Fixed raw struct name identifier parsing (#402) * Fixed raw struct name identifier parsing * Added more tests, raw identifier suggestion error, and invalid identifier error formatting * Even more empty identifier tests --- CHANGELOG.md | 1 + docs/grammar.md | 11 +++ src/de/mod.rs | 83 ++++++++++------- src/error.rs | 74 ++++++++++++--- src/parse.rs | 74 ++++++++++----- src/ser/mod.rs | 9 +- tests/401_raw_identifier.rs | 177 ++++++++++++++++++++++++++++++++++++ 7 files changed, 353 insertions(+), 76 deletions(-) create mode 100644 tests/401_raw_identifier.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aa1aafce..5f7a62edf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Fix issues [#277](https://github.com/ron-rs/ron/issues/277) and [#405](https://github.com/ron-rs/ron/issues/405) with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` ([#406](https://github.com/ron-rs/ron/pull/406)) +- Fix issue [#401](https://github.com/ron-rs/ron/issues/401) with correct raw struct name identifier parsing ([#402](https://github.com/ron-rs/ron/pull/402)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/docs/grammar.md b/docs/grammar.md index 5c88107e7..e119c9d82 100644 --- a/docs/grammar.md +++ b/docs/grammar.md @@ -138,3 +138,14 @@ enum_variant_unit = ident; enum_variant_tuple = ident, ws, tuple; enum_variant_named = ident, ws, "(", [named_field, { comma, named_field }, [comma]], ")"; ``` + +## Identifier + +```ebnf +ident = ident_std | ident_raw; +ident_std = ident_std_first, { ident_std_rest }; +ident_std_first = "A" | ... | "Z" | "a" | ... | "z" | "_"; +ident_std_rest = ident_std_first | digit; +ident_raw = "r", "#", ident_raw_rest, { ident_raw_rest }; +ident_raw_rest = ident_std_rest | "." | "+" | "-"; +``` diff --git a/src/de/mod.rs b/src/de/mod.rs index 4af28cdfe..9c5b6f99f 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -124,13 +124,56 @@ impl<'de> Deserializer<'de> { // first argument is technically incorrect, but ignored anyway self.deserialize_tuple(0, visitor) } else { - // first two arguments are technically incorrect, but ignored anyway - self.deserialize_struct("", &[], visitor) + // giving no name results in worse errors but is necessary here + self.handle_struct_after_name("", visitor) } } else { visitor.visit_unit() } } + + /// Called from `deserialize_struct`, `struct_variant`, and `handle_any_struct`. + /// Handles deserialising the enclosing parentheses and everything in between. + /// + /// This method assumes there is no struct name identifier left. + fn handle_struct_after_name( + &mut self, + name_for_pretty_errors_only: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + if self.newtype_variant || self.bytes.consume("(") { + let old_newtype_variant = self.newtype_variant; + self.newtype_variant = false; + + let value = visitor + .visit_map(CommaSeparated::new(b')', self)) + .map_err(|err| { + struct_error_name( + err, + if !old_newtype_variant && !name_for_pretty_errors_only.is_empty() { + Some(name_for_pretty_errors_only) + } else { + None + }, + ) + })?; + + self.bytes.comma()?; + + if old_newtype_variant || self.bytes.consume(")") { + Ok(value) + } else { + Err(Error::ExpectedStructLikeEnd) + } + } else if name_for_pretty_errors_only.is_empty() { + Err(Error::ExpectedStructLike) + } else { + Err(Error::ExpectedNamedStructLike(name_for_pretty_errors_only)) + } + } } impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { @@ -512,7 +555,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { } fn deserialize_struct( - mut self, + self, name: &'static str, _fields: &'static [&'static str], visitor: V, @@ -526,35 +569,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.bytes.skip_ws()?; - if self.newtype_variant || self.bytes.consume("(") { - let old_newtype_variant = self.newtype_variant; - self.newtype_variant = false; - - let value = visitor - .visit_map(CommaSeparated::new(b')', self)) - .map_err(|err| { - struct_error_name( - err, - if !old_newtype_variant && !name.is_empty() { - Some(name) - } else { - None - }, - ) - })?; - - self.bytes.comma()?; - - if old_newtype_variant || self.bytes.consume(")") { - Ok(value) - } else { - Err(Error::ExpectedStructLikeEnd) - } - } else if name.is_empty() { - Err(Error::ExpectedStructLike) - } else { - Err(Error::ExpectedNamedStructLike(name)) - } + self.handle_struct_after_name(name, visitor) } fn deserialize_enum( @@ -769,7 +784,7 @@ impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { self.de.deserialize_tuple(len, visitor) } - fn struct_variant(self, fields: &'static [&'static str], visitor: V) -> Result + fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where V: Visitor<'de>, { @@ -778,7 +793,7 @@ impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { self.de.bytes.skip_ws()?; self.de - .deserialize_struct("", fields, visitor) + .handle_struct_after_name("", visitor) .map_err(|err| struct_error_name(err, struct_variant)) } } diff --git a/src/error.rs b/src/error.rs index d64575452..6c8153453 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,8 @@ use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Err use serde::{de, ser}; +use crate::parse::{is_ident_first_char, is_ident_other_char, is_ident_raw_char}; + /// This type represents all possible errors that can occur when /// serializing or deserializing RON data. #[derive(Clone, Debug, PartialEq, Eq)] @@ -86,6 +88,8 @@ pub enum Error { field: &'static str, outer: Option, }, + InvalidIdentifier(String), + SuggestRawIdentifier(String), } impl fmt::Display for SpannedError { @@ -125,10 +129,19 @@ impl fmt::Display for Error { Error::ExpectedDifferentStructName { expected, ref found, - } => write!(f, "Expected struct `{}` but found `{}`", expected, found), + } => write!( + f, + "Expected struct {} but found {}", + Identifier(expected), + Identifier(found) + ), Error::ExpectedStructLike => f.write_str("Expected opening `(`"), Error::ExpectedNamedStructLike(name) => { - write!(f, "Expected opening `(` for struct `{}`", name) + if name.is_empty() { + f.write_str("Expected only opening `(`, no name, for un-nameable struct") + } else { + write!(f, "Expected opening `(` for struct {}", Identifier(name)) + } } Error::ExpectedStructLikeEnd => f.write_str("Expected closing `)`"), Error::ExpectedUnit => f.write_str("Expected unit"), @@ -137,7 +150,9 @@ impl fmt::Display for Error { Error::ExpectedIdentifier => f.write_str("Expected identifier"), Error::InvalidEscape(s) => f.write_str(s), Error::IntegerOutOfBounds => f.write_str("Integer is out of bounds"), - Error::NoSuchExtension(ref name) => write!(f, "No RON extension named `{}`", name), + Error::NoSuchExtension(ref name) => { + write!(f, "No RON extension named {}", Identifier(name)) + } Error::Utf8Error(ref e) => fmt::Display::fmt(e, f), Error::UnclosedBlockComment => f.write_str("Unclosed block comment"), Error::UnderscoreAtBeginning => { @@ -176,10 +191,10 @@ impl fmt::Display for Error { f.write_str("enum ")?; } - write!(f, "variant named `{}`", found)?; + write!(f, "variant named {}", Identifier(found))?; if let Some(outer) = outer { - write!(f, "in enum `{}`", outer)?; + write!(f, "in enum {}", Identifier(outer))?; } write!( @@ -196,10 +211,10 @@ impl fmt::Display for Error { ref found, ref outer, } => { - write!(f, "Unexpected field named `{}`", found)?; + write!(f, "Unexpected field named {}", Identifier(found))?; if let Some(outer) = outer { - write!(f, "in `{}`", outer)?; + write!(f, "in {}", Identifier(outer))?; } write!( @@ -212,21 +227,27 @@ impl fmt::Display for Error { ) } Error::MissingStructField { field, ref outer } => { - write!(f, "Unexpected missing field `{}`", field)?; + write!(f, "Unexpected missing field {}", Identifier(field))?; match outer { - Some(outer) => write!(f, " in `{}`", outer), + Some(outer) => write!(f, " in {}", Identifier(outer)), None => Ok(()), } } Error::DuplicateStructField { field, ref outer } => { - write!(f, "Unexpected duplicate field `{}`", field)?; + write!(f, "Unexpected duplicate field {}", Identifier(field))?; match outer { - Some(outer) => write!(f, " in `{}`", outer), + Some(outer) => write!(f, " in {}", Identifier(outer)), None => Ok(()), } } + Error::InvalidIdentifier(ref invalid) => write!(f, "Invalid identifier {:?}", invalid), + Error::SuggestRawIdentifier(ref identifier) => write!( + f, + "Found invalid std identifier `{}`, try the raw identifier `r#{}` instead", + identifier, identifier + ), } } } @@ -383,13 +404,18 @@ impl fmt::Display for OneOf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.alts { [] => write!(f, "there are no {}", self.none), - [a1] => write!(f, "expected `{}` instead", a1), - [a1, a2] => write!(f, "expected either `{}` or `{}` instead", a1, a2), + [a1] => write!(f, "expected {} instead", Identifier(a1)), + [a1, a2] => write!( + f, + "expected either {} or {} instead", + Identifier(a1), + Identifier(a2) + ), [a1, ref alts @ ..] => { - write!(f, "expected one of `{}`", a1)?; + write!(f, "expected one of {}", Identifier(a1))?; for alt in alts { - write!(f, ", `{}`", alt)?; + write!(f, ", {}", Identifier(alt))?; } f.write_str(" instead") @@ -397,3 +423,21 @@ impl fmt::Display for OneOf { } } } + +struct Identifier<'a>(&'a str); + +impl<'a> fmt::Display for Identifier<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.0.is_empty() || !self.0.as_bytes().iter().copied().all(is_ident_raw_char) { + return write!(f, "{:?}_[invalid identifier]", self.0); + } + + let mut bytes = self.0.as_bytes().iter().copied(); + + if !bytes.next().map_or(false, is_ident_first_char) || !bytes.all(is_ident_other_char) { + write!(f, "`r#{}`", self.0) + } else { + write!(f, "`{}`", self.0) + } + } +} diff --git a/src/parse.rs b/src/parse.rs index b0cc632f3..6c91d40d3 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -77,7 +77,7 @@ pub const fn is_ident_other_char(c: u8) -> bool { ENCODINGS[c as usize] & IDENT_OTHER_CHAR != 0 } -const fn is_ident_raw_char(c: u8) -> bool { +pub const fn is_ident_raw_char(c: u8) -> bool { ENCODINGS[c as usize] & IDENT_RAW_CHAR != 0 } @@ -175,7 +175,7 @@ impl<'a> Bytes<'a> { fn any_integer(&mut self, sign: i8) -> Result { let base = if self.peek() == Some(b'0') { - match self.bytes.get(1).cloned() { + match self.bytes.get(1).copied() { Some(b'x') => 16, Some(b'b') => 2, Some(b'o') => 8, @@ -450,27 +450,25 @@ impl<'a> Bytes<'a> { pub fn consume_struct_name(&mut self, ident: &'static str) -> Result { if self.check_ident("") { - Ok(false) - } else if ident.is_empty() { - Err(Error::ExpectedStructLike) - } else if self.check_ident(ident) { - let _ = self.advance(ident.len()); - - Ok(true) - } else { - // If the following is not even an identifier, then a missing - // opening `(` seems more likely - let maybe_ident = self - .identifier() - .map_err(|_| Error::ExpectedNamedStructLike(ident))?; + return Ok(false); + } - let found = std::str::from_utf8(maybe_ident).map_err(Error::from)?; + let found_ident = match self.identifier() { + Ok(maybe_ident) => std::str::from_utf8(maybe_ident)?, + Err(Error::SuggestRawIdentifier(found_ident)) if found_ident == ident => { + return Err(Error::SuggestRawIdentifier(found_ident)) + } + Err(_) => return Err(Error::ExpectedNamedStructLike(ident)), + }; - Err(Error::ExpectedDifferentStructName { + if found_ident != ident { + return Err(Error::ExpectedDifferentStructName { expected: ident, - found: String::from(found), - }) + found: String::from(found_ident), + }); } + + Ok(true) } pub fn consume(&mut self, s: &str) -> bool { @@ -584,6 +582,14 @@ impl<'a> Bytes<'a> { pub fn identifier(&mut self) -> Result<&'a [u8]> { let next = self.peek_or_eof()?; if !is_ident_first_char(next) { + if is_ident_raw_char(next) { + let ident_bytes = &self.bytes[..self.next_bytes_contained_in(is_ident_raw_char)]; + + if let Ok(ident) = std::str::from_utf8(ident_bytes) { + return Err(Error::SuggestRawIdentifier(String::from(ident))); + } + } + return Err(Error::ExpectedIdentifier); } @@ -593,7 +599,7 @@ impl<'a> Bytes<'a> { match self.bytes.get(1).ok_or(Error::Eof)? { b'"' => return Err(Error::ExpectedIdentifier), b'#' => { - let after_next = self.bytes.get(2).cloned().unwrap_or_default(); + let after_next = self.bytes.get(2).copied().unwrap_or_default(); // Note: it's important to check this before advancing forward, so that // the value-type deserializer can fall back to parsing it differently. if !is_ident_raw_char(after_next) { @@ -603,10 +609,30 @@ impl<'a> Bytes<'a> { let _ = self.advance(2); self.next_bytes_contained_in(is_ident_raw_char) } - _ => self.next_bytes_contained_in(is_ident_other_char), + _ => { + let std_ident_length = self.next_bytes_contained_in(is_ident_other_char); + let raw_ident_length = self.next_bytes_contained_in(is_ident_raw_char); + + if raw_ident_length > std_ident_length { + if let Ok(ident) = std::str::from_utf8(&self.bytes[..raw_ident_length]) { + return Err(Error::SuggestRawIdentifier(String::from(ident))); + } + } + + std_ident_length + } } } else { - self.next_bytes_contained_in(is_ident_other_char) + let std_ident_length = self.next_bytes_contained_in(is_ident_other_char); + let raw_ident_length = self.next_bytes_contained_in(is_ident_raw_char); + + if raw_ident_length > std_ident_length { + if let Ok(ident) = std::str::from_utf8(&self.bytes[..raw_ident_length]) { + return Err(Error::SuggestRawIdentifier(String::from(ident))); + } + } + + std_ident_length }; let ident = &self.bytes[..length]; @@ -656,11 +682,11 @@ impl<'a> Bytes<'a> { } pub fn peek(&self) -> Option { - self.bytes.first().cloned() + self.bytes.first().copied() } pub fn peek_or_eof(&self) -> Result { - self.bytes.first().cloned().ok_or(Error::Eof) + self.bytes.first().copied().ok_or(Error::Eof) } pub fn signed_integer(&mut self) -> Result diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 507f9f609..109a5255d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -6,7 +6,7 @@ use crate::{ error::{Error, Result}, extensions::Extensions, options::Options, - parse::{is_ident_first_char, is_ident_other_char, LargeSInt, LargeUInt}, + parse::{is_ident_first_char, is_ident_other_char, is_ident_raw_char, LargeSInt, LargeUInt}, }; #[cfg(test)] @@ -367,8 +367,11 @@ impl Serializer { Ok(()) } - fn write_identifier(&mut self, name: &str) -> io::Result<()> { - let mut bytes = name.as_bytes().iter().cloned(); + fn write_identifier(&mut self, name: &str) -> Result<()> { + if name.is_empty() || !name.as_bytes().iter().copied().all(is_ident_raw_char) { + return Err(Error::InvalidIdentifier(name.into())); + } + let mut bytes = name.as_bytes().iter().copied(); if !bytes.next().map_or(false, is_ident_first_char) || !bytes.all(is_ident_other_char) { self.output.write_all(b"r#")?; } diff --git a/tests/401_raw_identifier.rs b/tests/401_raw_identifier.rs new file mode 100644 index 000000000..23e6ecabd --- /dev/null +++ b/tests/401_raw_identifier.rs @@ -0,0 +1,177 @@ +use ron::error::{Error, Position, SpannedError}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename = "Hello World")] +struct InvalidStruct; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename = "")] +struct EmptyStruct; + +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(rename = "Hello+World")] +#[serde(deny_unknown_fields)] +struct RawStruct { + #[serde(rename = "ab.cd-ef")] + field: bool, +} + +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +enum RawEnum { + #[serde(rename = "Hello-World")] + RawVariant, +} + +#[test] +fn test_invalid_identifiers() { + let ser = ron::ser::to_string_pretty( + &InvalidStruct, + ron::ser::PrettyConfig::default().struct_names(true), + ); + assert_eq!( + ser, + Err(Error::InvalidIdentifier(String::from("Hello World"))) + ); + + let ser = ron::ser::to_string_pretty( + &EmptyStruct, + ron::ser::PrettyConfig::default().struct_names(true), + ); + assert_eq!(ser, Err(Error::InvalidIdentifier(String::from("")))); + + let de = ron::from_str::("Hello World").unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::ExpectedDifferentStructName { + expected: "Hello World", + found: String::from("Hello"), + }, + position: Position { line: 1, col: 6 }, + } + ); + + let de = ron::from_str::("").unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::ExpectedUnit, + position: Position { line: 1, col: 1 }, + } + ); + + let de = ron::from_str::("r#").unwrap_err(); + assert_eq!( + format!("{}", de), + "1:1: Expected only opening `(`, no name, for un-nameable struct" + ); + + let de = ron::from_str::("").unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::ExpectedNamedStructLike("Hello+World"), + position: Position { line: 1, col: 1 }, + }, + ); + + let de = ron::from_str::("r#").unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::ExpectedNamedStructLike("Hello+World"), + position: Position { line: 1, col: 1 }, + }, + ); + + let de = ron::from_str::("Hello+World").unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::SuggestRawIdentifier(String::from("Hello+World")), + position: Position { line: 1, col: 1 }, + } + ); + + let de = ron::from_str::( + "r#Hello+World( + ab.cd-ef: true, + )", + ) + .unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::SuggestRawIdentifier(String::from("ab.cd-ef")), + position: Position { line: 2, col: 9 }, + } + ); + + let de = ron::from_str::( + "r#Hello+World( + r#ab.cd+ef: true, + )", + ) + .unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::NoSuchStructField { + expected: &["ab.cd-ef"], + found: String::from("ab.cd+ef"), + outer: Some(String::from("Hello+World")), + }, + position: Position { line: 2, col: 19 }, + } + ); + + let de = ron::from_str::("Hello-World").unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::SuggestRawIdentifier(String::from("Hello-World")), + position: Position { line: 1, col: 1 }, + } + ); + + let de = ron::from_str::("r#Hello+World").unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::NoSuchEnumVariant { + expected: &["Hello-World"], + found: String::from("Hello+World"), + outer: Some(String::from("RawEnum")), + }, + position: Position { line: 1, col: 14 }, + } + ); + + let de = ron::from_str::("r#+").unwrap_err(); + assert_eq!( + format!("{}", de), + r#"1:4: Expected struct ""_[invalid identifier] but found `r#+`"#, + ); +} + +#[test] +fn test_raw_identifier_roundtrip() { + let val = RawStruct { field: true }; + + let ser = + ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default().struct_names(true)) + .unwrap(); + assert_eq!(ser, "r#Hello+World(\n r#ab.cd-ef: true,\n)"); + + let de: RawStruct = ron::from_str(&ser).unwrap(); + assert_eq!(de, val); + + let val = RawEnum::RawVariant; + + let ser = ron::ser::to_string(&val).unwrap(); + assert_eq!(ser, "r#Hello-World"); + + let de: RawEnum = ron::from_str(&ser).unwrap(); + assert_eq!(de, val); +} From 39620ccd20b9337b86fbc616783c2295adbac135 Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Tue, 4 Oct 2022 09:11:55 +0300 Subject: [PATCH 05/29] Fix trailing comma parsing in tuples and Some (#412) --- CHANGELOG.md | 1 + src/de/mod.rs | 10 ++-- tests/410_trailing_comma.rs | 95 +++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 tests/410_trailing_comma.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f7a62edf..1386dfe94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix issues [#277](https://github.com/ron-rs/ron/issues/277) and [#405](https://github.com/ron-rs/ron/issues/405) with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` ([#406](https://github.com/ron-rs/ron/pull/406)) - Fix issue [#401](https://github.com/ron-rs/ron/issues/401) with correct raw struct name identifier parsing ([#402](https://github.com/ron-rs/ron/pull/402)) +- Fix issue [#410](https://github.com/ron-rs/ron/issues/410) trailing comma parsing in tuples and `Some` ([#412](https://github.com/ron-rs/ron/pull/412)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/src/de/mod.rs b/src/de/mod.rs index 9c5b6f99f..1062c2601 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -161,7 +161,7 @@ impl<'de> Deserializer<'de> { ) })?; - self.bytes.comma()?; + self.bytes.skip_ws()?; if old_newtype_variant || self.bytes.consume(")") { Ok(value) @@ -402,7 +402,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { let v = visitor.visit_some(&mut *self)?; - self.bytes.skip_ws()?; + self.bytes.comma()?; if self.bytes.consume(")") { Ok(v) @@ -482,7 +482,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if self.bytes.consume("[") { let value = visitor.visit_seq(CommaSeparated::new(b']', self))?; - self.bytes.comma()?; + self.bytes.skip_ws()?; if self.bytes.consume("]") { Ok(value) @@ -503,7 +503,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.newtype_variant = false; let value = visitor.visit_seq(CommaSeparated::new(b')', self))?; - self.bytes.comma()?; + self.bytes.skip_ws()?; if old_newtype_variant || self.bytes.consume(")") { Ok(value) @@ -542,7 +542,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if self.bytes.consume("{") { let value = visitor.visit_map(CommaSeparated::new(b'}', self))?; - self.bytes.comma()?; + self.bytes.skip_ws()?; if self.bytes.consume("}") { Ok(value) diff --git a/tests/410_trailing_comma.rs b/tests/410_trailing_comma.rs new file mode 100644 index 000000000..91018a3ef --- /dev/null +++ b/tests/410_trailing_comma.rs @@ -0,0 +1,95 @@ +use std::collections::HashMap; + +use ron::from_str; +use serde::Deserialize; + +#[derive(Deserialize)] +struct Newtype(i32); + +#[derive(Deserialize)] +struct Tuple(i32, i32); + +#[derive(Deserialize)] +#[allow(dead_code)] +struct Struct { + a: i32, + b: i32, +} + +#[derive(Deserialize)] +#[allow(dead_code)] +enum Enum { + Newtype(i32), + Tuple(i32, i32), + Struct { a: i32, b: i32 }, +} + +#[test] +fn test_trailing_comma_some() { + assert!(from_str::>("Some(1)").is_ok()); + assert!(from_str::>("Some(1,)").is_ok()); + assert!(from_str::>("Some(1,,)").is_err()); +} + +#[test] +fn test_trailing_comma_tuple() { + assert!(from_str::<(i32, i32)>("(1,2)").is_ok()); + assert!(from_str::<(i32, i32)>("(1,2,)").is_ok()); + assert!(from_str::<(i32, i32)>("(1,2,,)").is_err()); +} + +#[test] +fn test_trailing_comma_list() { + assert!(from_str::>("[1,2]").is_ok()); + assert!(from_str::>("[1,2,]").is_ok()); + assert!(from_str::>("[1,2,,]").is_err()); +} + +#[test] +fn test_trailing_comma_map() { + assert!(from_str::>("{1:false,2:true}").is_ok()); + assert!(from_str::>("{1:false,2:true,}").is_ok()); + assert!(from_str::>("{1:false,2:true,,}").is_err()); +} + +#[test] +fn test_trailing_comma_newtype_struct() { + assert!(from_str::("(1)").is_ok()); + assert!(from_str::("(1,)").is_ok()); + assert!(from_str::("(1,,)").is_err()); +} + +#[test] +fn test_trailing_comma_tuple_struct() { + assert!(from_str::("(1,2)").is_ok()); + assert!(from_str::("(1,2,)").is_ok()); + assert!(from_str::("(1,2,,)").is_err()); +} + +#[test] +fn test_trailing_comma_struct() { + assert!(from_str::("(a:1,b:2)").is_ok()); + assert!(from_str::("(a:1,b:2,)").is_ok()); + assert!(from_str::("(a:1,b:2,,)").is_err()); +} + +#[test] +fn test_trailing_comma_enum_newtype_variant() { + assert!(from_str::("Newtype(1)").is_ok()); + assert!(from_str::("Newtype(1,)").is_ok()); + assert!(from_str::("Newtype(1,,)").is_err()); +} + +#[test] +fn test_trailing_comma_enum_tuple_variant() { + assert!(from_str::("Tuple(1,2)").is_ok()); + assert!(from_str::("Tuple(1,2,)").is_ok()); + assert!(from_str::("Tuple(1,2,,)").is_err()); +} + +#[test] +fn test_trailing_comma_enum_struct_variant() { + assert!(from_str::("Struct(a:1,b:2)").is_ok()); + assert!(from_str::("Struct(a:1,b:2,)").is_ok()); + assert!(from_str::("Struct(a:1,b:2,,)").is_err()); +} From 75e739bcf23f43f1f0fbb115d527a68e49161d6a Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Fri, 7 Oct 2022 16:14:46 +0300 Subject: [PATCH 06/29] Error instead of panic when deserializing non-identifier field name (#415) * Error instead of panic when deserializing non-identifier field name * Fix code coverage for tests * Added CHANGELOG entry --- CHANGELOG.md | 1 + src/de/id.rs | 54 ++++++++--------- tests/203_error_positions.rs | 2 +- tests/non_identifier_identifier.rs | 94 ++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 tests/non_identifier_identifier.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1386dfe94..c3e0451d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix issues [#277](https://github.com/ron-rs/ron/issues/277) and [#405](https://github.com/ron-rs/ron/issues/405) with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` ([#406](https://github.com/ron-rs/ron/pull/406)) - Fix issue [#401](https://github.com/ron-rs/ron/issues/401) with correct raw struct name identifier parsing ([#402](https://github.com/ron-rs/ron/pull/402)) - Fix issue [#410](https://github.com/ron-rs/ron/issues/410) trailing comma parsing in tuples and `Some` ([#412](https://github.com/ron-rs/ron/pull/412)) +- Error instead of panic when deserializing non-identifiers as field names ([#415](https://github.com/ron-rs/ron/pull/415)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/src/de/id.rs b/src/de/id.rs index 537ce5690..e7230da5f 100644 --- a/src/de/id.rs +++ b/src/de/id.rs @@ -40,35 +40,35 @@ impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> { where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_i8(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_i16(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_i32(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_i64(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } #[cfg(feature = "integer128")] @@ -76,35 +76,35 @@ impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> { where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_u8(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_u16(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_u32(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_u64(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } #[cfg(feature = "integer128")] @@ -112,105 +112,105 @@ impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> { where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_f32(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_f64(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_char(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_string(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_bytes(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_byte_buf(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_option(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_unit(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_unit_struct(self, _: &'static str, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_newtype_struct(self, _: &'static str, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_seq(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_tuple(self, _: usize, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_tuple_struct(self, _: &'static str, _: usize, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_map(self, _: V) -> Result where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_struct( @@ -222,7 +222,7 @@ impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> { where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_enum( @@ -234,7 +234,7 @@ impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> { where V: Visitor<'b>, { - unimplemented!("IdDeserializer may only be used for identifiers") + Err(Error::ExpectedIdentifier) } fn deserialize_ignored_any(self, visitor: V) -> Result diff --git a/tests/203_error_positions.rs b/tests/203_error_positions.rs index 0ef486a17..153e3f5ba 100644 --- a/tests/203_error_positions.rs +++ b/tests/203_error_positions.rs @@ -13,7 +13,7 @@ enum Test { StructVariant { a: bool, b: NonZeroU32, c: i32 }, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq)] // GRCOV_EXCL_LINE struct TypeError; impl<'de> Deserialize<'de> for TypeError { diff --git a/tests/non_identifier_identifier.rs b/tests/non_identifier_identifier.rs new file mode 100644 index 000000000..81b6c2cdc --- /dev/null +++ b/tests/non_identifier_identifier.rs @@ -0,0 +1,94 @@ +macro_rules! test_non_identifier { + ($test_name:ident => $deserialize_method:ident($($deserialize_param:expr),*)) => { + #[test] + fn $test_name() { + use serde::{Deserialize, Deserializer, de::Visitor, de::MapAccess}; + + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = FieldName; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("an error") + } + // GRCOV_EXCL_STOP + } + + struct FieldName; + + impl<'de> Deserialize<'de> for FieldName { + fn deserialize>(deserializer: D) + -> Result + { + deserializer.$deserialize_method($($deserialize_param,)* FieldVisitor) + } + } + + struct StructVisitor; + + impl<'de> Visitor<'de> for StructVisitor { + type Value = Struct; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a struct") + } + // GRCOV_EXCL_STOP + + fn visit_map>(self, mut map: A) + -> Result + { + map.next_key::().map(|_| Struct) + } + } + + #[derive(Debug)] + struct Struct; + + impl<'de> Deserialize<'de> for Struct { + fn deserialize>(deserializer: D) + -> Result + { + deserializer.deserialize_struct("Struct", &[], StructVisitor) + } + } + + assert_eq!( + ron::from_str::("(true: 4)").unwrap_err().code, + ron::Error::ExpectedIdentifier + ) + } + }; +} + +test_non_identifier! { test_bool => deserialize_bool() } +test_non_identifier! { test_i8 => deserialize_i8() } +test_non_identifier! { test_i16 => deserialize_i16() } +test_non_identifier! { test_i32 => deserialize_i32() } +test_non_identifier! { test_i64 => deserialize_i64() } +#[cfg(feature = "integer128")] +test_non_identifier! { test_i128 => deserialize_i128() } +test_non_identifier! { test_u8 => deserialize_u8() } +test_non_identifier! { test_u16 => deserialize_u16() } +test_non_identifier! { test_u32 => deserialize_u32() } +test_non_identifier! { test_u64 => deserialize_u64() } +#[cfg(feature = "integer128")] +test_non_identifier! { test_u128 => deserialize_u128() } +test_non_identifier! { test_f32 => deserialize_f32() } +test_non_identifier! { test_f64 => deserialize_f64() } +test_non_identifier! { test_char => deserialize_char() } +test_non_identifier! { test_string => deserialize_string() } +test_non_identifier! { test_bytes => deserialize_bytes() } +test_non_identifier! { test_byte_buf => deserialize_byte_buf() } +test_non_identifier! { test_option => deserialize_option() } +test_non_identifier! { test_unit => deserialize_unit() } +test_non_identifier! { test_unit_struct => deserialize_unit_struct("") } +test_non_identifier! { test_newtype_struct => deserialize_newtype_struct("") } +test_non_identifier! { test_seq => deserialize_seq() } +test_non_identifier! { test_tuple => deserialize_tuple(0) } +test_non_identifier! { test_tuple_struct => deserialize_tuple_struct("", 0) } +test_non_identifier! { test_map => deserialize_map() } +test_non_identifier! { test_struct => deserialize_struct("", &[]) } +test_non_identifier! { test_enum => deserialize_enum("", &[]) } From 146fad07a1cbc0a692952f783b813018a906e226 Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Sat, 8 Oct 2022 12:46:11 +0300 Subject: [PATCH 07/29] Add tests for extensions (#417) --- src/extensions.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/extensions.rs b/src/extensions.rs index 39357ac7d..08abf0696 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -26,3 +26,30 @@ impl Default for Extensions { Extensions::empty() } } + +#[cfg(test)] +mod tests { + use super::Extensions; + + fn roundtrip_extensions(ext: Extensions) { + let ron = crate::to_string(&ext).unwrap(); + let ext2: Extensions = crate::from_str(&ron).unwrap(); + assert_eq!(ext, ext2); + } + + #[test] + fn test_extension_serde() { + roundtrip_extensions(Extensions::default()); + roundtrip_extensions(Extensions::UNWRAP_NEWTYPES); + roundtrip_extensions(Extensions::IMPLICIT_SOME); + roundtrip_extensions(Extensions::UNWRAP_VARIANT_NEWTYPES); + roundtrip_extensions(Extensions::UNWRAP_NEWTYPES | Extensions::IMPLICIT_SOME); + roundtrip_extensions(Extensions::UNWRAP_NEWTYPES | Extensions::UNWRAP_VARIANT_NEWTYPES); + roundtrip_extensions(Extensions::IMPLICIT_SOME | Extensions::UNWRAP_VARIANT_NEWTYPES); + roundtrip_extensions( + Extensions::UNWRAP_NEWTYPES + | Extensions::IMPLICIT_SOME + | Extensions::UNWRAP_VARIANT_NEWTYPES, + ); + } +} From 531d2afbb8883bfaf676a2468d29714b3a4a9dc9 Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Wed, 12 Oct 2022 09:58:28 +0300 Subject: [PATCH 08/29] Add explicit recursion limits to avoid stack overflows (#420) * Explicit recursion limit during deserializing * Explicit recursion limit during serializing * Added CHANGELOG entry --- CHANGELOG.md | 1 + fuzz/Cargo.lock | 107 +++++++++++++++++++++------------- fuzz/fuzz_targets/from_str.rs | 4 +- src/de/mod.rs | 93 ++++++++++++++++++++--------- src/error.rs | 2 + src/options.rs | 24 ++++++++ src/ser/mod.rs | 102 +++++++++++++++++++------------- tests/307_stack_overflow.rs | Bin 0 -> 7646 bytes 8 files changed, 223 insertions(+), 110 deletions(-) create mode 100644 tests/307_stack_overflow.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c3e0451d0..4500242bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix issue [#401](https://github.com/ron-rs/ron/issues/401) with correct raw struct name identifier parsing ([#402](https://github.com/ron-rs/ron/pull/402)) - Fix issue [#410](https://github.com/ron-rs/ron/issues/410) trailing comma parsing in tuples and `Some` ([#412](https://github.com/ron-rs/ron/pull/412)) - Error instead of panic when deserializing non-identifiers as field names ([#415](https://github.com/ron-rs/ron/pull/415)) +- Breaking: Fix issue [#307](https://github.com/ron-rs/ron/issues/307) stack overflow with explicit recursion limits in serialising and deserialising ([#420](https://github.com/ron-rs/ron/pull/420)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 254d6fa6f..a46a2aea6 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -1,109 +1,134 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "arbitrary" -version = "1.0.0" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86fd10d912cab78764cc44307d9cd5f164e09abbeb87fb19fb6d95937e8da5f" [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" -version = "1.0.67" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + +[[package]] +name = "jobserver" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" [[package]] name = "libfuzzer-sys" -version = "0.4.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae185684fe19814afd066da15a7cc41e126886c21282934225d9fc847582da58" dependencies = [ - "arbitrary 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.67 (registry+https://github.com/rust-lang/crates.io-index)", + "arbitrary", + "cc", + "once_cell", ] +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "ron" -version = "0.6.0" +version = "0.8.0" dependencies = [ - "base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", + "base64", + "bitflags", + "serde", ] [[package]] name = "ron-fuzz" version = "0.0.0" dependencies = [ - "libfuzzer-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ron 0.6.0", + "libfuzzer-sys", + "ron", ] [[package]] name = "serde" -version = "1.0.124" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ - "serde_derive 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.124" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "syn" -version = "1.0.64" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "unicode-xid" -version = "0.2.1" +name = "unicode-ident" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum arbitrary 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "698b65a961a9d730fb45b6b0327e20207810c9f61ee421b082b27ba003f49e2b" -"checksum base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum cc 1.0.67 (registry+https://github.com/rust-lang/crates.io-index)" = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" -"checksum libfuzzer-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86c975d637bc2a2f99440932b731491fc34c7f785d239e38af3addd3c2fd0e46" -"checksum proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" -"checksum quote 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" -"checksum serde 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)" = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" -"checksum serde_derive 1.0.124 (registry+https://github.com/rust-lang/crates.io-index)" = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" -"checksum syn 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" -"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" diff --git a/fuzz/fuzz_targets/from_str.rs b/fuzz/fuzz_targets/from_str.rs index a6754b0eb..7ab71fa14 100644 --- a/fuzz/fuzz_targets/from_str.rs +++ b/fuzz/fuzz_targets/from_str.rs @@ -2,5 +2,7 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &str| { - let _ = ron::from_str::(data); + if let Ok(value) = ron::from_str::(data) { + let _ = ron::to_string(&value); + } }); diff --git a/src/de/mod.rs b/src/de/mod.rs index 1062c2601..b00edb448 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -26,6 +26,7 @@ pub struct Deserializer<'de> { bytes: Bytes<'de>, newtype_variant: bool, last_identifier: Option<&'de str>, + recursion_limit: Option, } impl<'de> Deserializer<'de> { @@ -48,6 +49,7 @@ impl<'de> Deserializer<'de> { bytes: Bytes::new(input)?, newtype_variant: false, last_identifier: None, + recursion_limit: options.recursion_limit, }; deserializer.bytes.exts |= options.default_extensions; @@ -92,6 +94,26 @@ where Options::default().from_bytes(s) } +macro_rules! guard_recursion { + ($self:expr => $expr:expr) => {{ + if let Some(limit) = &mut $self.recursion_limit { + if let Some(new_limit) = limit.checked_sub(1) { + *limit = new_limit; + } else { + return Err(Error::ExceededRecursionLimit); + } + } + + let result = $expr; + + if let Some(limit) = &mut $self.recursion_limit { + *limit = limit.saturating_add(1); + } + + result + }}; +} + impl<'de> Deserializer<'de> { /// Check if the remaining bytes are whitespace only, /// otherwise return an error. @@ -148,18 +170,20 @@ impl<'de> Deserializer<'de> { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; - let value = visitor - .visit_map(CommaSeparated::new(b')', self)) - .map_err(|err| { - struct_error_name( - err, - if !old_newtype_variant && !name_for_pretty_errors_only.is_empty() { - Some(name_for_pretty_errors_only) - } else { - None - }, - ) - })?; + let value = guard_recursion! { self => + visitor + .visit_map(CommaSeparated::new(b')', self)) + .map_err(|err| { + struct_error_name( + err, + if !old_newtype_variant && !name_for_pretty_errors_only.is_empty() { + Some(name_for_pretty_errors_only) + } else { + None + }, + ) + })? + }; self.bytes.skip_ws()?; @@ -400,7 +424,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { } { self.bytes.skip_ws()?; - let v = visitor.visit_some(&mut *self)?; + let v = guard_recursion! { self => visitor.visit_some(&mut *self)? }; self.bytes.comma()?; @@ -410,7 +434,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { Err(Error::ExpectedOptionEnd) } } else if self.bytes.exts.contains(Extensions::IMPLICIT_SOME) { - visitor.visit_some(&mut *self) + guard_recursion! { self => visitor.visit_some(&mut *self) } } else { Err(Error::ExpectedOption) } @@ -450,7 +474,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if self.bytes.exts.contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { self.newtype_variant = false; - return visitor.visit_newtype_struct(&mut *self); + return guard_recursion! { self => visitor.visit_newtype_struct(&mut *self) }; } self.bytes.consume_struct_name(name)?; @@ -459,7 +483,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if self.bytes.consume("(") { self.bytes.skip_ws()?; - let value = visitor.visit_newtype_struct(&mut *self)?; + let value = guard_recursion! { self => visitor.visit_newtype_struct(&mut *self)? }; self.bytes.comma()?; if self.bytes.consume(")") { @@ -481,7 +505,9 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.newtype_variant = false; if self.bytes.consume("[") { - let value = visitor.visit_seq(CommaSeparated::new(b']', self))?; + let value = guard_recursion! { self => + visitor.visit_seq(CommaSeparated::new(b']', self))? + }; self.bytes.skip_ws()?; if self.bytes.consume("]") { @@ -502,7 +528,9 @@ 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')', self))?; + let value = guard_recursion! { self => + visitor.visit_seq(CommaSeparated::new(b')', self))? + }; self.bytes.skip_ws()?; if old_newtype_variant || self.bytes.consume(")") { @@ -541,7 +569,9 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.newtype_variant = false; if self.bytes.consume("{") { - let value = visitor.visit_map(CommaSeparated::new(b'}', self))?; + let value = guard_recursion! { self => + visitor.visit_map(CommaSeparated::new(b'}', self))? + }; self.bytes.skip_ws()?; if self.bytes.consume("}") { @@ -583,7 +613,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { { self.newtype_variant = false; - match visitor.visit_enum(Enum::new(self)) { + match guard_recursion! { self => visitor.visit_enum(Enum::new(self)) } { Ok(value) => Ok(value), Err(Error::NoSuchEnumVariant { expected, @@ -657,7 +687,7 @@ impl<'de, 'a> de::SeqAccess<'de> for CommaSeparated<'a, 'de> { T: DeserializeSeed<'de>, { if self.has_element()? { - let res = seed.deserialize(&mut *self.de)?; + let res = guard_recursion! { self.de => seed.deserialize(&mut *self.de)? }; self.had_comma = self.de.bytes.comma()?; @@ -677,10 +707,11 @@ impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { { if self.has_element()? { if self.terminator == b')' { - seed.deserialize(&mut IdDeserializer::new(&mut *self.de)) - .map(Some) + guard_recursion! { self.de => + seed.deserialize(&mut IdDeserializer::new(&mut *self.de)).map(Some) + } } else { - seed.deserialize(&mut *self.de).map(Some) + guard_recursion! { self.de => seed.deserialize(&mut *self.de).map(Some) } } } else { Ok(None) @@ -696,7 +727,9 @@ impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { if self.de.bytes.consume(":") { self.de.bytes.skip_ws()?; - let res = seed.deserialize(&mut TagDeserializer::new(&mut *self.de))?; + let res = guard_recursion! { self.de => + seed.deserialize(&mut TagDeserializer::new(&mut *self.de))? + }; self.had_comma = self.de.bytes.comma()?; @@ -727,7 +760,7 @@ impl<'de, 'a> de::EnumAccess<'de> for Enum<'a, 'de> { { self.de.bytes.skip_ws()?; - let value = seed.deserialize(&mut *self.de)?; + let value = guard_recursion! { self.de => seed.deserialize(&mut *self.de)? }; Ok((value, self)) } @@ -757,9 +790,11 @@ impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { .exts .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); - let val = seed - .deserialize(&mut *self.de) - .map_err(|err| struct_error_name(err, newtype_variant))?; + let val = guard_recursion! { self.de => + seed + .deserialize(&mut *self.de) + .map_err(|err| struct_error_name(err, newtype_variant))? + }; self.de.newtype_variant = false; diff --git a/src/error.rs b/src/error.rs index 6c8153453..1cd38b7d3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -90,6 +90,7 @@ pub enum Error { }, InvalidIdentifier(String), SuggestRawIdentifier(String), + ExceededRecursionLimit, } impl fmt::Display for SpannedError { @@ -248,6 +249,7 @@ impl fmt::Display for Error { "Found invalid std identifier `{}`, try the raw identifier `r#{}` instead", identifier, identifier ), + Error::ExceededRecursionLimit => f.write_str("Exceeded recursion limit, try increasing the limit and using `serde_stacker` to protect against a stack overflow"), } } } diff --git a/src/options.rs b/src/options.rs index d550116ee..3b9ade288 100644 --- a/src/options.rs +++ b/src/options.rs @@ -38,12 +38,19 @@ pub struct Options { /// activation is NOT included in the output RON. /// No extensions are enabled by default. pub default_extensions: Extensions, + /// Default recursion limit that is checked during serialization and + /// deserialization. + /// If set to `None`, infinite recursion is allowed and stack overflow + /// errors can crash the serialization or deserialization process. + /// Defaults to `Some(128)`, i.e. 128 recursive calls are allowed. + pub recursion_limit: Option, } impl Default for Options { fn default() -> Self { Self { default_extensions: Extensions::empty(), + recursion_limit: Some(128), } } } @@ -62,6 +69,23 @@ impl Options { self.default_extensions &= !default_extension; self } + + #[must_use] + /// Set a maximum recursion limit during serialization and deserialization. + pub fn with_recursion_limit(mut self, recursion_limit: usize) -> Self { + self.recursion_limit = Some(recursion_limit); + self + } + + #[must_use] + /// Disable the recursion limit during serialization and deserialization. + /// + /// If you expect to handle highly recursive datastructures, consider wrapping + /// `ron` with [`serde_stacker`](https://docs.rs/serde_stacker/latest/serde_stacker/). + pub fn without_recursion_limit(mut self) -> Self { + self.recursion_limit = None; + self + } } impl Options { diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 109a5255d..f27cd5076 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -226,6 +226,7 @@ pub struct Serializer { default_extensions: Extensions, is_empty: Option, newtype_variant: bool, + recursion_limit: Option, } impl Serializer { @@ -276,6 +277,7 @@ impl Serializer { default_extensions: options.default_extensions, is_empty: None, newtype_variant: false, + recursion_limit: options.recursion_limit, }) } @@ -387,6 +389,26 @@ impl Serializer { } } +macro_rules! guard_recursion { + ($self:expr => $expr:expr) => {{ + if let Some(limit) = &mut $self.recursion_limit { + if let Some(new_limit) = limit.checked_sub(1) { + *limit = new_limit; + } else { + return Err(Error::ExceededRecursionLimit); + } + } + + let result = $expr; + + if let Some(limit) = &mut $self.recursion_limit { + *limit = limit.saturating_add(1); + } + + result + }}; +} + impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { type Error = Error; type Ok = (); @@ -495,7 +517,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { if !implicit_some { self.output.write_all(b"Some(")?; } - value.serialize(&mut *self)?; + guard_recursion! { self => value.serialize(&mut *self)? }; if !implicit_some { self.output.write_all(b")")?; } @@ -536,7 +558,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { if self.extensions().contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { self.newtype_variant = false; - return value.serialize(&mut *self); + return guard_recursion! { self => value.serialize(&mut *self) }; } if self.struct_names() { @@ -544,7 +566,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { } self.output.write_all(b"(")?; - value.serialize(&mut *self)?; + guard_recursion! { self => value.serialize(&mut *self)? }; self.output.write_all(b")")?; Ok(()) } @@ -566,7 +588,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { .extensions() .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); - value.serialize(&mut *self)?; + guard_recursion! { self => value.serialize(&mut *self)? }; self.newtype_variant = false; @@ -591,11 +613,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { pretty.sequence_index.push(0); } - Ok(Compound { - ser: self, - state: State::First, - newtype_variant: false, - }) + Compound::try_new(self, false) } fn serialize_tuple(self, len: usize) -> Result { @@ -612,11 +630,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { self.start_indent()?; } - Ok(Compound { - ser: self, - state: State::First, - newtype_variant: old_newtype_variant, - }) + Compound::try_new(self, old_newtype_variant) } fn serialize_tuple_struct( @@ -649,11 +663,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { self.start_indent()?; } - Ok(Compound { - ser: self, - state: State::First, - newtype_variant: false, - }) + Compound::try_new(self, false) } fn serialize_map(self, len: Option) -> Result { @@ -667,11 +677,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { self.start_indent()?; - Ok(Compound { - ser: self, - state: State::First, - newtype_variant: false, - }) + Compound::try_new(self, false) } fn serialize_struct(self, name: &'static str, len: usize) -> Result { @@ -688,11 +694,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { self.is_empty = Some(len == 0); self.start_indent()?; - Ok(Compound { - ser: self, - state: State::First, - newtype_variant: old_newtype_variant, - }) + Compound::try_new(self, old_newtype_variant) } fn serialize_struct_variant( @@ -710,11 +712,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { self.is_empty = Some(len == 0); self.start_indent()?; - Ok(Compound { - ser: self, - state: State::First, - newtype_variant: false, - }) + Compound::try_new(self, false) } } @@ -730,6 +728,32 @@ pub struct Compound<'a, W: io::Write> { newtype_variant: bool, } +impl<'a, W: io::Write> Compound<'a, W> { + fn try_new(ser: &'a mut Serializer, newtype_variant: bool) -> Result { + if let Some(limit) = &mut ser.recursion_limit { + if let Some(new_limit) = limit.checked_sub(1) { + *limit = new_limit; + } else { + return Err(Error::ExceededRecursionLimit); + } + } + + Ok(Compound { + ser, + state: State::First, + newtype_variant, + }) + } +} + +impl<'a, W: io::Write> Drop for Compound<'a, W> { + fn drop(&mut self) { + if let Some(limit) = &mut self.ser.recursion_limit { + *limit = limit.saturating_add(1); + } + } +} + impl<'a, W: io::Write> ser::SerializeSeq for Compound<'a, W> { type Error = Error; type Ok = (); @@ -763,7 +787,7 @@ impl<'a, W: io::Write> ser::SerializeSeq for Compound<'a, W> { } } - value.serialize(&mut *self.ser)?; + guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; Ok(()) } @@ -817,7 +841,7 @@ impl<'a, W: io::Write> ser::SerializeTuple for Compound<'a, W> { self.ser.indent()?; } - value.serialize(&mut *self.ser)?; + guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; Ok(()) } @@ -898,7 +922,7 @@ impl<'a, W: io::Write> ser::SerializeMap for Compound<'a, W> { } } self.ser.indent()?; - key.serialize(&mut *self.ser) + guard_recursion! { self.ser => key.serialize(&mut *self.ser) } } fn serialize_value(&mut self, value: &T) -> Result<()> @@ -911,7 +935,7 @@ impl<'a, W: io::Write> ser::SerializeMap for Compound<'a, W> { self.ser.output.write_all(config.separator.as_bytes())?; } - value.serialize(&mut *self.ser)?; + guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; Ok(()) } @@ -961,7 +985,7 @@ impl<'a, W: io::Write> ser::SerializeStruct for Compound<'a, W> { self.ser.output.write_all(config.separator.as_bytes())?; } - value.serialize(&mut *self.ser)?; + guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; Ok(()) } diff --git a/tests/307_stack_overflow.rs b/tests/307_stack_overflow.rs new file mode 100644 index 0000000000000000000000000000000000000000..7076e6e13e789b091b2ebe2b8b71e54e5fbd2353 GIT binary patch literal 7646 zcmeHLQE%En4Bj)p;>IKzstTo0U`;#TD(zv~rdHZZRYegoQzS^(<?hsGop2KXVseEQRmHHOfi z1#_n@@`liB3QB;K;+>F5?0FNJ%=}bI&%0jPUxH}{Bd68On;i*r2$XZ~`mXD?+np@_ zE`zzRvz@lCY1D#LH=}&`_S(^OXPks^RtVonnP}CUA7g+J!dn<;GL4e>Lo|z2yRkGq zhk6H(;A}S1erV`cvau3(FrD1O1SG^tZ}R#i7_Iw{;uxeSKFyUDyKbEXWwE2PqQG-Q zDJofWnPo~mngQtPkPbvH)|EddF^J{WW|(tjhS@6ef>fN2lQ>nvFCtcSXO(J1lNmT! zoCXunf~`}NyZk*%a%EIvu1geubqehY^WS3h_GBj6V&Pv5FNgh}d)^(o!(R8TYmG+# zIduQcymUFG?4HPai!*A7Mv&~#_ShB)-L+Y7aYhZ%2=01oTkA8uAsRuV&U;9pWinZa zM$pR4bM3h=)&>-2@xV0?-L?Pe2dN?Wr!ZkEn2a2v5hQxH$F@l5uFZOjGir!NaMxSg zTA%3+(Fks>zS{(*GejduTH!q;&@!1UL?dWrK6fjdyV$JNA;N&CdwGqJDuU+ez^si_ z?ljkjGjgC{j(YmdUg2o5?a{96e9^z`_Iv$7ujjhmi_1av^98fzBYm8}i=2S7m-Qk? zd)zdhz+V1m{5uV|a>Sbn{Qp5jUrVm6TNV?-aB+xReW${tG2bZZ9g)?fzOmRdb zNb2$)5@?xB7NQZfGWTsTeIOb^(j4z0ftFbK+@Ebk$KmdRux8bK>_ o^$m$Ie_ Date: Tue, 18 Oct 2022 11:21:41 +0300 Subject: [PATCH 09/29] Fix #423 deserialising an identifier into a borrowed str (#424) --- CHANGELOG.md | 1 + src/de/mod.rs | 2 +- tests/423_de_borrowed_identifier.rs | 43 +++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/423_de_borrowed_identifier.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4500242bf..a9e60d498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix issue [#410](https://github.com/ron-rs/ron/issues/410) trailing comma parsing in tuples and `Some` ([#412](https://github.com/ron-rs/ron/pull/412)) - Error instead of panic when deserializing non-identifiers as field names ([#415](https://github.com/ron-rs/ron/pull/415)) - Breaking: Fix issue [#307](https://github.com/ron-rs/ron/issues/307) stack overflow with explicit recursion limits in serialising and deserialising ([#420](https://github.com/ron-rs/ron/pull/420)) +- Fix issue [#423](https://github.com/ron-rs/ron/issues/423) deserialising an identifier into a borrowed str ([#424](https://github.com/ron-rs/ron/pull/424)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/src/de/mod.rs b/src/de/mod.rs index b00edb448..0a5598130 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -636,7 +636,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.last_identifier = Some(identifier); - visitor.visit_str(identifier) + visitor.visit_borrowed_str(identifier) } fn deserialize_ignored_any(self, visitor: V) -> Result diff --git a/tests/423_de_borrowed_identifier.rs b/tests/423_de_borrowed_identifier.rs new file mode 100644 index 000000000..3333d290d --- /dev/null +++ b/tests/423_de_borrowed_identifier.rs @@ -0,0 +1,43 @@ +use std::any::Any; + +use serde::{ + de::{MapAccess, Visitor}, + Deserializer, +}; + +#[test] +fn manually_deserialize_dyn() { + let ron = r#"SerializeDyn( + type: "engine_utils::types::registry::tests::Player", + )"#; + + let mut de = ron::Deserializer::from_bytes(ron.as_bytes()).unwrap(); + + let result = de + .deserialize_struct("SerializeDyn", &["type"], SerializeDynVisitor) + .unwrap(); + + assert_eq!( + *result.downcast::>().unwrap(), + Some(( + String::from("type"), + String::from("engine_utils::types::registry::tests::Player") + )) + ); +} + +struct SerializeDynVisitor; + +impl<'de> Visitor<'de> for SerializeDynVisitor { + type Value = Box; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a serialize dyn struct") + } + + fn visit_map>(self, mut map: A) -> Result { + let entry = map.next_entry::<&str, String>()?; + + Ok(Box::new(entry.map(|(k, v)| (String::from(k), v)))) + } +} From 04601bbd5d50551bc130d4071a713c9f4bf909ca Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Fri, 21 Oct 2022 13:39:37 +0300 Subject: [PATCH 10/29] Small fixes to codecov and rustdoc (#427) --- src/ser/mod.rs | 23 +++++++++++++++-------- tests/423_de_borrowed_identifier.rs | 2 ++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f27cd5076..f45cee852 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -104,7 +104,7 @@ impl PrettyConfig { /// (indentation level) 6, everything will be put into the same line, /// without pretty formatting. /// - /// Default: [std::usize::MAX] + /// Default: [usize::MAX] pub fn depth_limit(mut self, depth_limit: usize) -> Self { self.depth_limit = depth_limit; @@ -169,15 +169,22 @@ impl PrettyConfig { self } - /// Configures whether every array should be a single line (true) or a multi line one (false) - /// When false, `["a","b"]` (as well as any array) will serialize to - /// ` + /// Configures whether every array should be a single line (`true`) + /// or a multi line one (`false`). + /// + /// When `false`, `["a","b"]` (as well as any array) will serialize to + /// ``` /// [ /// "a", /// "b", /// ] - /// ` - /// When true, `["a","b"]` (as well as any array) will serialize to `["a","b"]` + /// # ; + /// ``` + /// When `true`, `["a","b"]` (as well as any array) will serialize to + /// ``` + /// ["a","b"] + /// # ; + /// ``` /// /// Default: `false` pub fn compact_arrays(mut self, compact_arrays: bool) -> Self { @@ -188,7 +195,7 @@ impl PrettyConfig { /// Configures extensions /// - /// Default: Extensions::empty() + /// Default: [Extensions::empty()] pub fn extensions(mut self, extensions: Extensions) -> Self { self.extensions = extensions; @@ -199,7 +206,7 @@ impl PrettyConfig { impl Default for PrettyConfig { fn default() -> Self { PrettyConfig { - depth_limit: !0, + depth_limit: usize::MAX, new_line: if cfg!(not(target_os = "windows")) { String::from("\n") } else { diff --git a/tests/423_de_borrowed_identifier.rs b/tests/423_de_borrowed_identifier.rs index 3333d290d..d29a0050f 100644 --- a/tests/423_de_borrowed_identifier.rs +++ b/tests/423_de_borrowed_identifier.rs @@ -31,9 +31,11 @@ struct SerializeDynVisitor; impl<'de> Visitor<'de> for SerializeDynVisitor { type Value = Box; + // GRCOV_EXCL_START fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "a serialize dyn struct") } + // GRCOV_EXCL_STOP fn visit_map>(self, mut map: A) -> Result { let entry = map.next_entry::<&str, String>()?; From 4481a984b18946bec19c9fb6fe2756ca583a4b84 Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Tue, 10 Jan 2023 11:58:28 +0200 Subject: [PATCH 11/29] Bump MSRV to 1.57.0 and base64 to 0.20 (#431) --- .github/workflows/ci.yaml | 2 +- CHANGELOG.md | 1 + Cargo.toml | 10 ++++++---- README.md | 2 +- clippy.toml | 2 +- src/error.rs | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e74809fe9..24973280d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: strategy: matrix: - rust: [1.56.0, stable, nightly] + rust: [1.57.0, stable, nightly] steps: - uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index a9e60d498..9ad1b817a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Error instead of panic when deserializing non-identifiers as field names ([#415](https://github.com/ron-rs/ron/pull/415)) - Breaking: Fix issue [#307](https://github.com/ron-rs/ron/issues/307) stack overflow with explicit recursion limits in serialising and deserialising ([#420](https://github.com/ron-rs/ron/pull/420)) - Fix issue [#423](https://github.com/ron-rs/ron/issues/423) deserialising an identifier into a borrowed str ([#424](https://github.com/ron-rs/ron/pull/424)) +- Bump MSRV to 1.57.0 and bump dependency: `base64` to 0.20 ([#431](https://github.com/ron-rs/ron/pull/431)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/Cargo.toml b/Cargo.toml index 63876f77d..38822e788 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,19 +16,21 @@ readme = "README.md" homepage = "https://github.com/ron-rs/ron" repository = "https://github.com/ron-rs/ron" documentation = "https://docs.rs/ron/" +rust-version = "1.57.0" [features] default = [] integer128 = [] [dependencies] -base64 = "0.13" -bitflags = "1.0.4" -indexmap = { version = "1.0.2", features = ["serde-1"], optional = true } +base64 = "0.20" +bitflags = "1.3" +indexmap = { version = "1.9", features = ["serde-1"], optional = true } +# serde supports i128/u128 from 1.0.60 onwards serde = { version = "1.0.60", features = ["serde_derive"] } [dev-dependencies] serde_bytes = "0.11" -serde_json = "1" +serde_json = "1.0" bitflags-serial = { git = "https://github.com/kvark/bitflags-serial" } option_set = "0.1" diff --git a/README.md b/README.md index 36036bc21..164d5d1e5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![CI](https://github.com/ron-rs/ron/actions/workflows/ci.yaml/badge.svg)](https://github.com/ron-rs/ron/actions/workflows/ci.yaml) [![codecov](https://img.shields.io/codecov/c/github/ron-rs/ron/codecov?token=x4Q5KA51Ul)](https://codecov.io/gh/ron-rs/ron) [![Crates.io](https://img.shields.io/crates/v/ron.svg)](https://crates.io/crates/ron) -[![MSRV](https://img.shields.io/badge/MSRV-1.56.0-orange)](https://github.com/ron-rs/ron) +[![MSRV](https://img.shields.io/badge/MSRV-1.57.0-orange)](https://github.com/ron-rs/ron) [![Docs](https://docs.rs/ron/badge.svg)](https://docs.rs/ron) [![Matrix](https://img.shields.io/matrix/ron-rs:matrix.org.svg)](https://matrix.to/#/#ron-rs:matrix.org) diff --git a/clippy.toml b/clippy.toml index 9e89f43f4..28c44eb03 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,2 +1,2 @@ -msrv = "1.56.0" +msrv = "1.57.0" blacklisted-names = [] diff --git a/src/error.rs b/src/error.rs index 1cd38b7d3..29030b859 100644 --- a/src/error.rs +++ b/src/error.rs @@ -301,7 +301,7 @@ impl de::Error for Error { Char(c) => write!(f, "the UTF-8 character `{}`", c), Str(s) => write!(f, "the string {:?}", s), Bytes(b) => write!(f, "the bytes \"{}\"", { - base64::display::Base64Display::with_config(b, base64::STANDARD) + base64::display::Base64Display::from(b, &base64::engine::DEFAULT_ENGINE) }), Unit => write!(f, "a unit value"), Option => write!(f, "an optional value"), From 843e1923e4f35249a4c68639be820433c057adb8 Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Tue, 10 Jan 2023 12:58:54 +0200 Subject: [PATCH 12/29] Bump base64 to v0.21 (#433) --- CHANGELOG.md | 1 + Cargo.toml | 2 +- src/de/mod.rs | 5 +++-- src/error.rs | 4 ++-- src/parse.rs | 4 ++++ src/ser/mod.rs | 8 ++++++-- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad1b817a..3370d63a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Breaking: Fix issue [#307](https://github.com/ron-rs/ron/issues/307) stack overflow with explicit recursion limits in serialising and deserialising ([#420](https://github.com/ron-rs/ron/pull/420)) - Fix issue [#423](https://github.com/ron-rs/ron/issues/423) deserialising an identifier into a borrowed str ([#424](https://github.com/ron-rs/ron/pull/424)) - Bump MSRV to 1.57.0 and bump dependency: `base64` to 0.20 ([#431](https://github.com/ron-rs/ron/pull/431)) +- Bump dependency `base64` to 0.21 ([#433](https://github.com/ron-rs/ron/pull/433)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/Cargo.toml b/Cargo.toml index 38822e788..1927e1527 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ default = [] integer128 = [] [dependencies] -base64 = "0.20" +base64 = "0.21" bitflags = "1.3" indexmap = { version = "1.9", features = ["serde-1"], optional = true } # serde supports i128/u128 from 1.0.60 onwards diff --git a/src/de/mod.rs b/src/de/mod.rs index 0a5598130..91b81298d 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,6 +1,7 @@ /// Deserialization module. use std::{borrow::Cow, io, str}; +use base64::Engine; use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor}; use self::{id::IdDeserializer, tag::TagDeserializer}; @@ -9,7 +10,7 @@ use crate::{ error::{Result, SpannedResult}, extensions::Extensions, options::Options, - parse::{AnyNum, Bytes, ParsedStr}, + parse::{AnyNum, Bytes, ParsedStr, BASE64_ENGINE}, }; mod id; @@ -403,7 +404,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { ParsedStr::Allocated(ref s) => s.as_str(), ParsedStr::Slice(s) => s, }; - base64::decode(base64_str) + BASE64_ENGINE.decode(base64_str) }; match res { diff --git a/src/error.rs b/src/error.rs index 29030b859..406fb058c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Err use serde::{de, ser}; -use crate::parse::{is_ident_first_char, is_ident_other_char, is_ident_raw_char}; +use crate::parse::{is_ident_first_char, is_ident_other_char, is_ident_raw_char, BASE64_ENGINE}; /// This type represents all possible errors that can occur when /// serializing or deserializing RON data. @@ -301,7 +301,7 @@ impl de::Error for Error { Char(c) => write!(f, "the UTF-8 character `{}`", c), Str(s) => write!(f, "the string {:?}", s), Bytes(b) => write!(f, "the bytes \"{}\"", { - base64::display::Base64Display::from(b, &base64::engine::DEFAULT_ENGINE) + base64::display::Base64Display::new(b, &BASE64_ENGINE) }), Unit => write!(f, "a unit value"), Option => write!(f, "an optional value"), diff --git a/src/parse.rs b/src/parse.rs index 6c91d40d3..20eca1620 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -5,11 +5,15 @@ use std::{ str::{from_utf8, from_utf8_unchecked, FromStr}, }; +use base64::engine::general_purpose::{GeneralPurpose, STANDARD}; + use crate::{ error::{Error, Position, Result, SpannedError, SpannedResult}, extensions::Extensions, }; +pub const BASE64_ENGINE: GeneralPurpose = STANDARD; + // We have the following char categories. const INT_CHAR: u8 = 1 << 0; // [0-9A-Fa-f_] const FLOAT_CHAR: u8 = 1 << 1; // [0-9\.Ee+-_] diff --git a/src/ser/mod.rs b/src/ser/mod.rs index f45cee852..7e650aa4d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,12 +1,16 @@ use std::io; +use base64::Engine; use serde::{ser, Deserialize, Serialize}; use crate::{ error::{Error, Result}, extensions::Extensions, options::Options, - parse::{is_ident_first_char, is_ident_other_char, is_ident_raw_char, LargeSInt, LargeUInt}, + parse::{ + is_ident_first_char, is_ident_other_char, is_ident_raw_char, LargeSInt, LargeUInt, + BASE64_ENGINE, + }, }; #[cfg(test)] @@ -507,7 +511,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { } fn serialize_bytes(self, v: &[u8]) -> Result<()> { - self.serialize_str(base64::encode(v).as_str()) + self.serialize_str(BASE64_ENGINE.encode(v).as_str()) } fn serialize_none(self) -> Result<()> { From 5b8fc3ede80e0af6eac0d397986c76f14852f8fd Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Mon, 6 Feb 2023 20:07:46 +0200 Subject: [PATCH 13/29] Update juntyr's author details (#437) --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 1927e1527..f49b29a28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ authors = [ "Christopher Durham ", "Dzmitry Malyshau ", "Thomas Schaller ", + "Juniper Tyree ", ] edition = "2021" description = "Rusty Object Notation" From 0f324a26d25a64f03eedbf44c099a603c46e61d3 Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Mon, 13 Mar 2023 13:36:05 +0200 Subject: [PATCH 14/29] Document nested block comments and add more intra docs links (#440) --- README.md | 4 ++++ docs/grammar.md | 3 ++- src/de/mod.rs | 15 ++++++++----- src/options.rs | 9 ++++++-- src/parse.rs | 2 +- src/ser/mod.rs | 23 +++++++++++-------- src/value.rs | 59 ++++++++++++++++++++++++++----------------------- 7 files changed, 68 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 164d5d1e5..d20bcd2b1 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,10 @@ fn main() { let x: MyStruct = ron::from_str("(boolean: true, float: 1.23)").unwrap(); println!("RON: {}", ron::to_string(&x).unwrap()); + + println!("Pretty RON: {}", ron::ser::to_string_pretty( + &x, ron::ser::PrettyConfig::default()).unwrap(), + ); } ``` diff --git a/docs/grammar.md b/docs/grammar.md index e119c9d82..3ef2b7f7e 100644 --- a/docs/grammar.md +++ b/docs/grammar.md @@ -18,7 +18,8 @@ RON = [extensions], ws, value, ws; ```ebnf ws = { ws_single | comment }; ws_single = "\n" | "\t" | "\r" | " "; -comment = ["//", { no_newline }, "\n"] | ["/*", { ? any character ? }, "*/"]; +comment = ["//", { no_newline }, "\n"] | ["/*", nested_block_comment, "*/"]; +nested_block_comment = { ? any characters except "/*" or "*/" ? }, [ "/*", nested_block_comment, "*/", nested_block_comment ]; ``` ## Commas diff --git a/src/de/mod.rs b/src/de/mod.rs index 91b81298d..68199b5a8 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -22,7 +22,7 @@ mod value; /// The RON deserializer. /// /// If you just want to simply deserialize a value, -/// you can use the `from_str` convenience function. +/// you can use the [`from_str`] convenience function. pub struct Deserializer<'de> { bytes: Bytes<'de>, newtype_variant: bool, @@ -128,9 +128,9 @@ impl<'de> Deserializer<'de> { } } - /// Called from `deserialize_any` when a struct was detected. Decides if - /// there is a unit, tuple or usual struct and deserializes it - /// accordingly. + /// Called from [`deserialize_any`][serde::Deserializer::deserialize_any] + /// when a struct was detected. Decides if there is a unit, tuple or usual + /// struct and deserializes it accordingly. /// /// This method assumes there is no identifier left. fn handle_any_struct(&mut self, visitor: V) -> Result @@ -155,8 +155,11 @@ impl<'de> Deserializer<'de> { } } - /// Called from `deserialize_struct`, `struct_variant`, and `handle_any_struct`. - /// Handles deserialising the enclosing parentheses and everything in between. + /// Called from + /// [`deserialize_struct`][serde::Deserializer::deserialize_struct], + /// [`struct_variant`][serde::de::VariantAccess::struct_variant], and + /// [`handle_any_struct`][Self::handle_any_struct]. Handles + /// deserialising the enclosing parentheses and everything in between. /// /// This method assumes there is no struct name identifier left. fn handle_struct_after_name( diff --git a/src/options.rs b/src/options.rs index 3b9ade288..588b3813d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -162,7 +162,11 @@ impl Options { Ok(value) } - /// Serializes `value` into `writer` + /// Serializes `value` into `writer`. + /// + /// This function does not generate any newlines or nice formatting; + /// if you want that, you can use + /// [`to_writer_pretty`][Self::to_writer_pretty] instead. pub fn to_writer(&self, writer: W, value: &T) -> Result<()> where W: io::Write, @@ -185,7 +189,8 @@ impl Options { /// Serializes `value` and returns it as string. /// /// This function does not generate any newlines or nice formatting; - /// if you want that, you can use `to_string_pretty` instead. + /// if you want that, you can use + /// [`to_string_pretty`][Self::to_string_pretty] instead. pub fn to_string(&self, value: &T) -> Result where T: ?Sized + ser::Serialize, diff --git a/src/parse.rs b/src/parse.rs index 20eca1620..bed7f4491 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -109,7 +109,7 @@ pub enum AnyNum { #[derive(Clone, Copy, Debug)] pub struct Bytes<'a> { - /// Bits set according to the `Extensions` enum. + /// Bits set according to the [`Extensions`] enum. pub exts: Extensions, bytes: &'a [u8], cursor: Position, diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 7e650aa4d..7e56a7b20 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -17,7 +17,10 @@ use crate::{ mod tests; mod value; -/// Serializes `value` into `writer` +/// Serializes `value` into `writer`. +/// +/// This function does not generate any newlines or nice formatting; +/// if you want that, you can use [`to_writer_pretty`] instead. pub fn to_writer(writer: W, value: &T) -> Result<()> where W: io::Write, @@ -38,7 +41,7 @@ where /// Serializes `value` and returns it as string. /// /// This function does not generate any newlines or nice formatting; -/// if you want that, you can use `to_string_pretty` instead. +/// if you want that, you can use [`to_string_pretty`] instead. pub fn to_string(value: &T) -> Result where T: ?Sized + Serialize, @@ -98,7 +101,7 @@ pub struct PrettyConfig { } impl PrettyConfig { - /// Creates a default `PrettyConfig`. + /// Creates a default [`PrettyConfig`]. pub fn new() -> Self { Default::default() } @@ -229,8 +232,8 @@ impl Default for PrettyConfig { /// The RON serializer. /// -/// You can just use `to_string` for deserializing a value. -/// If you want it pretty-printed, take a look at the `pretty` module. +/// You can just use [`to_string`] for deserializing a value. +/// If you want it pretty-printed, take a look at [`to_string_pretty`]. pub struct Serializer { output: W, pretty: Option<(PrettyConfig, Pretty)>, @@ -241,16 +244,18 @@ pub struct Serializer { } impl Serializer { - /// Creates a new `Serializer`. + /// Creates a new [`Serializer`]. /// - /// Most of the time you can just use `to_string` or `to_string_pretty`. + /// Most of the time you can just use [`to_string`] or + /// [`to_string_pretty`]. pub fn new(writer: W, config: Option) -> Result { Self::with_options(writer, config, Options::default()) } - /// Creates a new `Serializer`. + /// Creates a new [`Serializer`]. /// - /// Most of the time you can just use `to_string` or `to_string_pretty`. + /// Most of the time you can just use [`to_string`] or + /// [`to_string_pretty`]. pub fn with_options( mut writer: W, config: Option, diff --git a/src/value.rs b/src/value.rs index 1f4628692..1e5e27ddf 100644 --- a/src/value.rs +++ b/src/value.rs @@ -14,7 +14,7 @@ use serde::{ use crate::{de::Error, error::Result}; -/// A `Value` to `Value` map. +/// A [`Value`] to [`Value`] map. /// /// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the /// [IndexMap](indexmap::IndexMap) internally. @@ -25,7 +25,7 @@ use crate::{de::Error, error::Result}; pub struct Map(MapInner); impl Map { - /// Creates a new, empty `Map`. + /// Creates a new, empty [`Map`]. pub fn new() -> Map { Default::default() } @@ -140,20 +140,20 @@ type MapInner = std::collections::BTreeMap; #[cfg(feature = "indexmap")] type MapInner = indexmap::IndexMap; -/// A wrapper for a number, which can be either `f64` or `i64`. +/// A wrapper for a number, which can be either [`f64`] or [`i64`]. #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)] pub enum Number { Integer(i64), Float(Float), } -/// A wrapper for `f64`, which guarantees that the inner value -/// is finite and thus implements `Eq`, `Hash` and `Ord`. +/// A wrapper for [`f64`], which guarantees that the inner value +/// is finite and thus implements [`Eq`], [`Hash`] and [`Ord`]. #[derive(Copy, Clone, Debug)] pub struct Float(f64); impl Float { - /// Construct a new `Float`. + /// Construct a new [`Float`]. pub fn new(v: f64) -> Self { Float(v) } @@ -170,8 +170,8 @@ impl Number { v.into() } - /// Returns the `f64` representation of the number regardless of whether the number is stored - /// as a float or integer. + /// Returns the [`f64`] representation of the [`Number`] regardless of + /// whether the number is stored as a float or integer. /// /// # Example /// @@ -186,7 +186,7 @@ impl Number { self.map_to(|i| i as f64, |f| f) } - /// If the `Number` is a float, return it. Otherwise return `None`. + /// If the [`Number`] is a float, return it. Otherwise return [`None`]. /// /// # Example /// @@ -201,7 +201,7 @@ impl Number { self.map_to(|_| None, Some) } - /// If the `Number` is an integer, return it. Otherwise return `None`. + /// If the [`Number`] is an integer, return it. Otherwise return [`None`]. /// /// # Example /// @@ -257,8 +257,9 @@ impl From for Number { } } -// The following number conversion checks if the integer fits losslessly into an i64, before -// constructing a Number::Integer variant. If not, the conversion defaults to float. +/// The following [`Number`] conversion checks if the integer fits losslessly +/// into an [`i64`], before constructing a [`Number::Integer`] variant. +/// If not, the conversion defaults to [`Number::Float`]. impl From for Number { fn from(i: u64) -> Number { @@ -271,9 +272,9 @@ impl From for Number { } /// Partial equality comparison -/// In order to be able to use `Number` as a mapping key, NaN floating values -/// wrapped in `Float` are equals to each other. It is not the case for -/// underlying `f64` values itself. +/// In order to be able to use [`Number`] as a mapping key, NaN floating values +/// wrapped in [`Float`] are equal to each other. It is not the case for +/// underlying [`f64`] values itself. impl PartialEq for Float { fn eq(&self, other: &Self) -> bool { self.0.is_nan() && other.0.is_nan() || self.0 == other.0 @@ -281,9 +282,9 @@ impl PartialEq for Float { } /// Equality comparison -/// In order to be able to use `Float` as a mapping key, NaN floating values -/// wrapped in `Float` are equals to each other. It is not the case for -/// underlying `f64` values itself. +/// In order to be able to use [`Float`] as a mapping key, NaN floating values +/// wrapped in [`Float`] are equal to each other. It is not the case for +/// underlying [`f64`] values itself. impl Eq for Float {} impl Hash for Float { @@ -293,9 +294,11 @@ impl Hash for Float { } /// Partial ordering comparison -/// In order to be able to use `Number` as a mapping key, NaN floating values -/// wrapped in `Number` are equals to each other and are less then any other -/// floating value. It is not the case for the underlying `f64` values themselves. +/// In order to be able to use [`Number`] as a mapping key, NaN floating values +/// wrapped in [`Number`] are equal to each other and are less then any other +/// floating value. It is not the case for the underlying [`f64`] values +/// themselves. +/// /// ``` /// use ron::value::Number; /// assert!(Number::new(std::f64::NAN) < Number::new(std::f64::NEG_INFINITY)); @@ -313,10 +316,10 @@ impl PartialOrd for Float { } /// Ordering comparison -/// In order to be able to use `Float` as a mapping key, NaN floating values -/// wrapped in `Float` are equals to each other and are less then any other -/// floating value. It is not the case for underlying `f64` values itself. See -/// the `PartialEq` implementation. +/// In order to be able to use [`Float`] as a mapping key, NaN floating values +/// wrapped in [`Float`] are equal to each other and are less then any other +/// floating value. It is not the case for underlying [`f64`] values itself. +/// See the [`PartialEq`] implementation. impl Ord for Float { fn cmp(&self, other: &Self) -> Ordering { self.partial_cmp(other).expect("Bug: Contract violation") @@ -336,7 +339,7 @@ pub enum Value { } impl Value { - /// Tries to deserialize this `Value` into `T`. + /// Tries to deserialize this [`Value`] into `T`. pub fn into_rust(self) -> Result where T: DeserializeOwned, @@ -345,8 +348,8 @@ impl Value { } } -/// Deserializer implementation for RON `Value`. -/// This does not support enums (because `Value` doesn't store them). +/// Deserializer implementation for RON [`Value`]. +/// This does not support enums (because [`Value`] does not store them). impl<'de> Deserializer<'de> for Value { type Error = Error; From d5cdeab2ee6812bb632a6cf6ec0e5506d9e46a68 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Mon, 13 Mar 2023 20:18:36 +0100 Subject: [PATCH 15/29] Depend on `serde_derive` separately for more compilation parallelism (#441) * depend on serde_derive separately for more compilation parallelism * add changelog entry --- CHANGELOG.md | 1 + Cargo.toml | 4 +++- src/de/tests.rs | 2 +- src/extensions.rs | 2 +- src/options.rs | 3 ++- src/ser/mod.rs | 3 ++- src/ser/tests.rs | 2 +- src/value.rs | 3 ++- 8 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3370d63a3..dec81f87a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix issue [#423](https://github.com/ron-rs/ron/issues/423) deserialising an identifier into a borrowed str ([#424](https://github.com/ron-rs/ron/pull/424)) - Bump MSRV to 1.57.0 and bump dependency: `base64` to 0.20 ([#431](https://github.com/ron-rs/ron/pull/431)) - Bump dependency `base64` to 0.21 ([#433](https://github.com/ron-rs/ron/pull/433)) +- Depend on `serde_derive` directly to potentially enable more compilation parallelism ([#441](https://github.com/ron-rs/ron/pull/441)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/Cargo.toml b/Cargo.toml index f49b29a28..3985221bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,9 +28,11 @@ base64 = "0.21" bitflags = "1.3" indexmap = { version = "1.9", features = ["serde-1"], optional = true } # serde supports i128/u128 from 1.0.60 onwards -serde = { version = "1.0.60", features = ["serde_derive"] } +serde = "1.0.60" +serde_derive = "1.0" [dev-dependencies] +serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" serde_json = "1.0" bitflags-serial = { git = "https://github.com/kvark/bitflags-serial" } diff --git a/src/de/tests.rs b/src/de/tests.rs index 1dbae0e93..2c46eb68f 100644 --- a/src/de/tests.rs +++ b/src/de/tests.rs @@ -1,5 +1,5 @@ -use serde::Deserialize; use serde_bytes; +use serde_derive::Deserialize; use crate::{ de::from_str, diff --git a/src/extensions.rs b/src/extensions.rs index 08abf0696..19472c2a2 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +use serde_derive::{Deserialize, Serialize}; bitflags::bitflags! { #[derive(Serialize, Deserialize)] diff --git a/src/options.rs b/src/options.rs index 588b3813d..b0c5816bd 100644 --- a/src/options.rs +++ b/src/options.rs @@ -2,7 +2,8 @@ use std::io; -use serde::{de, ser, Deserialize, Serialize}; +use serde::{de, ser}; +use serde_derive::{Deserialize, Serialize}; use crate::{ de::Deserializer, diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 7e56a7b20..b1aefbcba 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,7 +1,8 @@ use std::io; use base64::Engine; -use serde::{ser, Deserialize, Serialize}; +use serde::{ser, ser::Serialize}; +use serde_derive::{Deserialize, Serialize}; use crate::{ error::{Error, Result}, diff --git a/src/ser/tests.rs b/src/ser/tests.rs index 8b1b6c845..fce7f0071 100644 --- a/src/ser/tests.rs +++ b/src/ser/tests.rs @@ -1,4 +1,4 @@ -use serde::Serialize; +use serde_derive::Serialize; use super::to_string; diff --git a/src/value.rs b/src/value.rs index 1e5e27ddf..56b0bdbe1 100644 --- a/src/value.rs +++ b/src/value.rs @@ -9,8 +9,9 @@ use std::{ use serde::{ de::{DeserializeOwned, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor}, - forward_to_deserialize_any, Deserialize, Serialize, + forward_to_deserialize_any, }; +use serde_derive::{Deserialize, Serialize}; use crate::{de::Error, error::Result}; From efc55dedd78ec092a24ddcb6bcce50cef8bf6938 Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:51:23 +0300 Subject: [PATCH 16/29] Add serde attributes limitations to README.md (#450) --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index d20bcd2b1..fc812408f 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,13 @@ Note the following advantages of RON over JSON: * optional struct names improve readability * enums are supported (and less verbose than their JSON representation) +## Limitations + +RON is not designed to be a fully self-describing format (unlike JSON) and is thus not guaranteed to work when [`deserialize_any`](https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_any) is used instead of its typed alternatives. In particular, the following Serde attributes are not yet supported: +- `#[serde(tag = "type")]`, i.e. internally tagged enums +- `#[serde(untagged)]`, i.e. untagged enums +- `#[serde(flatten)]`, i.e. flattening an inner struct into its outer container + ## RON syntax overview * Numbers: `42`, `3.14`, `0xFF`, `0b0110` From a6fc97698696801322062fa727e4dffcc068d9cb Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Mon, 1 May 2023 13:16:19 +0300 Subject: [PATCH 17/29] Upgrade to bitflags 2.0 (#443) * Upgrade to bitflags 2.0 * Update the fuzzer Cargo.lock --- CHANGELOG.md | 1 + Cargo.toml | 3 +-- fuzz/Cargo.lock | 63 ++++++++++++++++++++++--------------------- src/de/mod.rs | 2 +- src/extensions.rs | 2 +- tests/152_bitflags.rs | 36 +++++-------------------- 6 files changed, 43 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dec81f87a..6942cda38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump MSRV to 1.57.0 and bump dependency: `base64` to 0.20 ([#431](https://github.com/ron-rs/ron/pull/431)) - Bump dependency `base64` to 0.21 ([#433](https://github.com/ron-rs/ron/pull/433)) - Depend on `serde_derive` directly to potentially enable more compilation parallelism ([#441](https://github.com/ron-rs/ron/pull/441)) +- Breaking: Bump `bitflags` dependency to 2.0, changes `serde` impls of `Extensions` ([#443](https://github.com/ron-rs/ron/pull/443)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/Cargo.toml b/Cargo.toml index 3985221bb..72443e11e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ integer128 = [] [dependencies] base64 = "0.21" -bitflags = "1.3" +bitflags = { version = "2.0", features = ["serde"] } indexmap = { version = "1.9", features = ["serde-1"], optional = true } # serde supports i128/u128 from 1.0.60 onwards serde = "1.0.60" @@ -35,5 +35,4 @@ serde_derive = "1.0" serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" serde_json = "1.0" -bitflags-serial = { git = "https://github.com/kvark/bitflags-serial" } option_set = "0.1" diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index a46a2aea6..716824710 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -4,51 +4,54 @@ version = 3 [[package]] name = "arbitrary" -version = "1.1.7" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86fd10d912cab78764cc44307d9cd5f164e09abbeb87fb19fb6d95937e8da5f" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "base64" -version = "0.13.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" +dependencies = [ + "serde", +] [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] [[package]] name = "libc" -version = "0.2.134" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "libfuzzer-sys" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae185684fe19814afd066da15a7cc41e126886c21282934225d9fc847582da58" +checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e" dependencies = [ "arbitrary", "cc", @@ -57,24 +60,24 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -86,6 +89,7 @@ dependencies = [ "base64", "bitflags", "serde", + "serde_derive", ] [[package]] @@ -98,18 +102,15 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" -dependencies = [ - "serde_derive", -] +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", @@ -118,9 +119,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.102" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -129,6 +130,6 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" diff --git a/src/de/mod.rs b/src/de/mod.rs index 68199b5a8..87971c621 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, io, str}; use base64::Engine; -use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor}; +use serde::de::{self, DeserializeSeed, Deserializer as _, Visitor}; use self::{id::IdDeserializer, tag::TagDeserializer}; pub use crate::error::{Error, Position, SpannedError}; diff --git a/src/extensions.rs b/src/extensions.rs index 19472c2a2..0455a34dd 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -1,7 +1,7 @@ use serde_derive::{Deserialize, Serialize}; bitflags::bitflags! { - #[derive(Serialize, Deserialize)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct Extensions: usize { const UNWRAP_NEWTYPES = 0x1; const IMPLICIT_SOME = 0x2; diff --git a/tests/152_bitflags.rs b/tests/152_bitflags.rs index b51fd5510..2ae212cff 100644 --- a/tests/152_bitflags.rs +++ b/tests/152_bitflags.rs @@ -1,11 +1,11 @@ use bitflags::*; use option_set::option_set; -#[macro_use] -extern crate bitflags_serial; - bitflags! { - #[derive(serde::Serialize, serde::Deserialize)] + #[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, + serde::Serialize, serde::Deserialize, + )] struct TestGood: u8 { const ONE = 1; const TWO = 1 << 1; @@ -14,6 +14,7 @@ bitflags! { } option_set! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] struct TestBad: UpperCamel + u8 { const ONE = 1; const TWO = 1 << 1; @@ -21,14 +22,6 @@ option_set! { } } -bitflags_serial! { - struct TestBadTWO: u8 { - const ONE = 1; - const TWO = 1 << 1; - const THREE = 1 << 2; - } -} - #[test] fn test_bitflags() { // Test case provided by jaynus in @@ -39,8 +32,8 @@ fn test_bitflags() { let json_ser_good = serde_json::ser::to_string(&flag_good).unwrap(); let ron_ser_good = ron::ser::to_string(&flag_good).unwrap(); - assert_eq!(json_ser_good, "{\"bits\":3}"); - assert_eq!(ron_ser_good, "(bits:3)"); + assert_eq!(json_ser_good, "\"ONE | TWO\""); + assert_eq!(ron_ser_good, "(\"ONE | TWO\")"); let json_de_good: TestGood = serde_json::de::from_str(json_ser_good.as_str()).unwrap(); let ron_de_good: TestGood = ron::de::from_str(ron_ser_good.as_str()).unwrap(); @@ -62,19 +55,4 @@ fn test_bitflags() { assert_eq!(json_de_bad, flag_bad); assert_eq!(ron_de_bad, flag_bad); - - // bitflags_serial - let flag_bad_two = TestBadTWO::ONE | TestBadTWO::TWO; - - let json_ser_bad_two = serde_json::ser::to_string(&flag_bad_two).unwrap(); - let ron_ser_bad_two = ron::ser::to_string(&flag_bad_two).unwrap(); - - assert_eq!(json_ser_bad_two, "[\"ONE\",\"TWO\"]"); - assert_eq!(ron_ser_bad_two, "[ONE,TWO]"); - - let json_de_bad_two: TestBadTWO = serde_json::de::from_str(json_ser_bad_two.as_str()).unwrap(); - let ron_de_bad_two: TestBadTWO = ron::de::from_str(ron_ser_bad_two.as_str()).unwrap(); - - assert_eq!(json_de_bad_two, flag_bad_two); - assert_eq!(ron_de_bad_two, flag_bad_two); } From 90a8e22ecb9267f18d3491837520fd739f8f1692 Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Sun, 21 May 2023 10:07:22 +0300 Subject: [PATCH 18/29] Add test for #456 and fix bitflags glob import (#457) --- src/de/mod.rs | 6 +++--- tests/152_bitflags.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 87971c621..128efde09 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -502,7 +502,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } - fn deserialize_seq(mut self, visitor: V) -> Result + fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { @@ -524,7 +524,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } - fn deserialize_tuple(mut self, _len: usize, visitor: V) -> Result + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { @@ -566,7 +566,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { }) } - fn deserialize_map(mut self, visitor: V) -> Result + fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { diff --git a/tests/152_bitflags.rs b/tests/152_bitflags.rs index 2ae212cff..8a4435ee0 100644 --- a/tests/152_bitflags.rs +++ b/tests/152_bitflags.rs @@ -1,4 +1,4 @@ -use bitflags::*; +use bitflags::bitflags; use option_set::option_set; bitflags! { From 5a63b664d40ba73f99417c56a4580b05abc4fe76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos?= Date: Wed, 28 Jun 2023 04:13:36 -0300 Subject: [PATCH 19/29] Add the `Map::retain` method (#460) --- CHANGELOG.md | 1 + src/value.rs | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6942cda38..a187ccfa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump dependency `base64` to 0.21 ([#433](https://github.com/ron-rs/ron/pull/433)) - Depend on `serde_derive` directly to potentially enable more compilation parallelism ([#441](https://github.com/ron-rs/ron/pull/441)) - Breaking: Bump `bitflags` dependency to 2.0, changes `serde` impls of `Extensions` ([#443](https://github.com/ron-rs/ron/pull/443)) +- Add `Map::retain` method ([#460](https://github.com/ron-rs/ron/pull/460)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/src/value.rs b/src/value.rs index 56b0bdbe1..ba843bc16 100644 --- a/src/value.rs +++ b/src/value.rs @@ -76,6 +76,19 @@ impl Map { pub fn values_mut(&mut self) -> impl Iterator + DoubleEndedIterator { self.0.values_mut() } + + /// Retains only the elements specified by the `keep` predicate. + /// + /// In other words, remove all pairs `(k, v)` for which `keep(&k, &mut v)` + /// returns `false`. + /// + /// The elements are visited in iteration order. + pub fn retain(&mut self, keep: F) + where + F: FnMut(&Value, &mut Value) -> bool, + { + self.0.retain(keep); + } } impl FromIterator<(Value, Value)> for Map { From 13359aea3a0d2badf7ab075a5591eec2d739236d Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Sun, 2 Jul 2023 11:28:49 +0300 Subject: [PATCH 20/29] Remove Cargo.lock for fuzzing (#461) --- fuzz/Cargo.lock | 135 ------------------------------------------------ 1 file changed, 135 deletions(-) delete mode 100644 fuzz/Cargo.lock diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock deleted file mode 100644 index 716824710..000000000 --- a/fuzz/Cargo.lock +++ /dev/null @@ -1,135 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "arbitrary" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "bitflags" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" -dependencies = [ - "serde", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -dependencies = [ - "jobserver", -] - -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - -[[package]] -name = "libc" -version = "0.2.142" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "proc-macro2" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ron" -version = "0.8.0" -dependencies = [ - "base64", - "bitflags", - "serde", - "serde_derive", -] - -[[package]] -name = "ron-fuzz" -version = "0.0.0" -dependencies = [ - "libfuzzer-sys", - "ron", -] - -[[package]] -name = "serde" -version = "1.0.160" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" - -[[package]] -name = "serde_derive" -version = "1.0.160" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syn" -version = "2.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" From aadc9ad0972c20ebaa0cdb7ade2828c58a3ec8fd Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Thu, 6 Jul 2023 10:27:21 +0300 Subject: [PATCH 21/29] Install grcov with the latest dependencies (#463) --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 24973280d..cd3f0e42a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -84,7 +84,7 @@ jobs: profile: minimal components: llvm-tools-preview override: true - - run: cargo install grcov --force --locked + - run: cargo install grcov --force env: CARGO_TARGET_DIR: target/ - run: | From 3609bd0b1af4fcd68ca1471f01f651d50eff0665 Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Thu, 6 Jul 2023 10:29:40 +0300 Subject: [PATCH 22/29] Bump indexmap to 2.0 and update MSRV to 1.64.0 (#459) --- .github/workflows/ci.yaml | 2 +- CHANGELOG.md | 1 + Cargo.toml | 4 ++-- README.md | 2 +- clippy.toml | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cd3f0e42a..8a3db72a5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: strategy: matrix: - rust: [1.57.0, stable, nightly] + rust: [1.64.0, stable, nightly] steps: - uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index a187ccfa4..77a4af87f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Depend on `serde_derive` directly to potentially enable more compilation parallelism ([#441](https://github.com/ron-rs/ron/pull/441)) - Breaking: Bump `bitflags` dependency to 2.0, changes `serde` impls of `Extensions` ([#443](https://github.com/ron-rs/ron/pull/443)) - Add `Map::retain` method ([#460](https://github.com/ron-rs/ron/pull/460)) +- Bump MSRV to 1.64.0 and bump dependency: `indexmap` to 2.0 ([#459](https://github.com/ron-rs/ron/pull/459)) ## [0.8.0] - 2022-08-17 - Add `integer128` feature that guards `i128` and `u128` ([#304](https://github.com/ron-rs/ron/pull/304), [#351](https://github.com/ron-rs/ron/pull/351)) diff --git a/Cargo.toml b/Cargo.toml index 72443e11e..c4908f6ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ readme = "README.md" homepage = "https://github.com/ron-rs/ron" repository = "https://github.com/ron-rs/ron" documentation = "https://docs.rs/ron/" -rust-version = "1.57.0" +rust-version = "1.64.0" [features] default = [] @@ -26,7 +26,7 @@ integer128 = [] [dependencies] base64 = "0.21" bitflags = { version = "2.0", features = ["serde"] } -indexmap = { version = "1.9", features = ["serde-1"], optional = true } +indexmap = { version = "2.0", features = ["serde"], optional = true } # serde supports i128/u128 from 1.0.60 onwards serde = "1.0.60" serde_derive = "1.0" diff --git a/README.md b/README.md index fc812408f..2fbf07dc6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![CI](https://github.com/ron-rs/ron/actions/workflows/ci.yaml/badge.svg)](https://github.com/ron-rs/ron/actions/workflows/ci.yaml) [![codecov](https://img.shields.io/codecov/c/github/ron-rs/ron/codecov?token=x4Q5KA51Ul)](https://codecov.io/gh/ron-rs/ron) [![Crates.io](https://img.shields.io/crates/v/ron.svg)](https://crates.io/crates/ron) -[![MSRV](https://img.shields.io/badge/MSRV-1.57.0-orange)](https://github.com/ron-rs/ron) +[![MSRV](https://img.shields.io/badge/MSRV-1.64.0-orange)](https://github.com/ron-rs/ron) [![Docs](https://docs.rs/ron/badge.svg)](https://docs.rs/ron) [![Matrix](https://img.shields.io/matrix/ron-rs:matrix.org.svg)](https://matrix.to/#/#ron-rs:matrix.org) diff --git a/clippy.toml b/clippy.toml index 28c44eb03..cb8cfcd1c 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,2 +1,2 @@ -msrv = "1.57.0" +msrv = "1.64.0" blacklisted-names = [] From b9efe6c91076a0904c6389ff155f9fcf476a23dd Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Thu, 6 Jul 2023 19:07:55 +0300 Subject: [PATCH 23/29] Add a fuzzer test that serialises and deserialises arbitrary typed serde data + fix `&[u8]` de (#462) * Add a fuzzer test that serialises and deserialises arbitrary typed serde data + fix &[u8] de * Fix the byte slice serialising test --- fuzz/.gitignore | 8 +- fuzz/Cargo.toml | 13 ++- fuzz/fuzz_targets/arbitrary.rs | 194 +++++++++++++++++++++++++++++++++ fuzz/fuzz_targets/from_str.rs | 1 + src/de/mod.rs | 10 +- tests/462_bytes.rs | 22 ++++ 6 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 fuzz/fuzz_targets/arbitrary.rs create mode 100644 tests/462_bytes.rs diff --git a/fuzz/.gitignore b/fuzz/.gitignore index 572e03bdf..7fa3e6967 100644 --- a/fuzz/.gitignore +++ b/fuzz/.gitignore @@ -1,4 +1,4 @@ - -target -corpus -artifacts +/artifacts +/corpus +/target +/Cargo.lock diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 4de1f17b7..a48994356 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -2,7 +2,6 @@ [package] name = "ron-fuzz" version = "0.0.0" -authors = ["Automatically generated"] publish = false edition = "2018" @@ -10,10 +9,10 @@ edition = "2018" cargo-fuzz = true [dependencies] +arbitrary = { version = "1.0", features = ["derive"] } libfuzzer-sys = "0.4" - -[dependencies.ron] -path = ".." +ron = { path = "..", features = ["integer128"] } +serde = { version = "1.0", features = ["derive"] } # Prevent this from interfering with workspaces [workspace] @@ -24,3 +23,9 @@ name = "from_str" path = "fuzz_targets/from_str.rs" test = false doc = false + +[[bin]] +name = "arbitrary" +path = "fuzz_targets/arbitrary.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/arbitrary.rs b/fuzz/fuzz_targets/arbitrary.rs new file mode 100644 index 000000000..0adf49d7a --- /dev/null +++ b/fuzz/fuzz_targets/arbitrary.rs @@ -0,0 +1,194 @@ +#![no_main] + +use std::borrow::Cow; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; + +use arbitrary::{Arbitrary, Unstructured}; +use libfuzzer_sys::fuzz_target; +use serde::{ + de::{MapAccess, Visitor}, + ser::SerializeMap, + Deserialize, Deserializer, Serialize, Serializer, +}; + +fuzz_target!(|data: &[u8]| { + if let Ok(value) = SerdeData::arbitrary(&mut Unstructured::new(data)) { + let ron = match ron::to_string(&value) { + Ok(ron) => ron, + Err(ron::error::Error::ExceededRecursionLimit) => return, + Err(err) => panic!("{:?} -! {:?}", value, err), + }; + let de = match ron::from_str::(&ron) { + Ok(de) => de, + Err(err) if err.code == ron::error::Error::ExceededRecursionLimit => return, + Err(err) => panic!("{:?} -> {:?} -! {:?}", value, ron, err), + }; + assert_eq!(value, de, "{:?} -> {:?} -> {:?}", value, ron, de); + } +}); + +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] +enum SerdeData<'a> { + Bool(bool), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + I128(i128), + U8(u8), + U16(u16), + U32(u32), + U128(u128), + F32(F32), + F64(F64), + Char(char), + #[serde(borrow)] + Str(Cow<'a, str>), + String(String), + #[serde(borrow)] + Bytes(Cow<'a, [u8]>), + ByteBuf(Vec), + Option(Option>), + Unit(()), + #[serde(borrow)] + Map(SerdeMap<'a>), + Seq(Vec), + #[serde(borrow)] + Enum(SerdeEnum<'a>), + #[serde(borrow)] + Struct(SerdeStruct<'a>), +} + +#[allow(clippy::enum_variant_names)] +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] +enum SerdeEnum<'a> { + UnitVariant, + #[serde(borrow)] + NewtypeVariant(Box>), + TupleVariant(Box>, Box>, Box>), + StructVariant { + a: Box>, + r#fn: Box>, + c: Box>, + }, +} + +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] +enum SerdeStruct<'a> { + Unit(SerdeUnitStruct), + #[serde(borrow)] + Newtype(SerdeNewtypeStruct<'a>), + #[serde(borrow)] + Tuple(SerdeTupleStruct<'a>), + #[serde(borrow)] + Struct(SerdeStructStruct<'a>), +} + +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] +struct SerdeUnitStruct; + +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] +#[repr(transparent)] +struct SerdeNewtypeStruct<'a>(#[serde(borrow)] Box>); + +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] +struct SerdeTupleStruct<'a>( + #[serde(borrow)] Box>, + Box>, + Box>, +); + +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] +struct SerdeStructStruct<'a> { + #[serde(borrow)] + a: Box>, + #[serde(borrow)] + r#fn: Box>, + #[serde(borrow)] + c: Box>, +} + +#[derive(Debug, Serialize, Deserialize, Arbitrary)] +#[repr(transparent)] +struct F32(f32); + +impl PartialEq for F32 { + fn eq(&self, other: &Self) -> bool { + if self.0.is_nan() && other.0.is_nan() { + return true; + } + self.0.to_bits() == other.0.to_bits() + } +} + +impl Eq for F32 {} + +impl Hash for F32 { + fn hash(&self, state: &mut H) { + state.write_u32(self.0.to_bits()) + } +} + +#[derive(Debug, Serialize, Deserialize, Arbitrary)] +#[repr(transparent)] +struct F64(f64); + +impl PartialEq for F64 { + fn eq(&self, other: &Self) -> bool { + if self.0.is_nan() && other.0.is_nan() { + return true; + } + self.0.to_bits() == other.0.to_bits() + } +} + +impl Eq for F64 {} + +impl Hash for F64 { + fn hash(&self, state: &mut H) { + state.write_u64(self.0.to_bits()) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Arbitrary)] +struct SerdeMap<'a>(Vec<(SerdeData<'a>, SerdeData<'a>)>); + +impl<'a> Serialize for SerdeMap<'a> { + fn serialize(&self, serializer: S) -> Result { + let mut map = serializer.serialize_map(Some(self.0.len()))?; + + for (key, value) in &self.0 { + map.serialize_entry(key, value)?; + } + + map.end() + } +} + +impl<'a, 'de: 'a> Deserialize<'de> for SerdeMap<'a> { + fn deserialize>(deserializer: D) -> Result { + struct SerdeMapVisitor<'a>(PhantomData<&'a ()>); + + impl<'a, 'de: 'a> Visitor<'de> for SerdeMapVisitor<'a> { + type Value = SerdeMap<'a>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a map") + } + + fn visit_map>(self, mut map: A) -> Result { + let mut values = Vec::with_capacity(map.size_hint().unwrap_or(0)); + + while let Some(entry) = map.next_entry()? { + values.push(entry); + } + + Ok(SerdeMap(values)) + } + } + + deserializer.deserialize_map(SerdeMapVisitor(PhantomData)) + } +} diff --git a/fuzz/fuzz_targets/from_str.rs b/fuzz/fuzz_targets/from_str.rs index 7ab71fa14..b388385b9 100644 --- a/fuzz/fuzz_targets/from_str.rs +++ b/fuzz/fuzz_targets/from_str.rs @@ -1,4 +1,5 @@ #![no_main] + use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &str| { diff --git a/src/de/mod.rs b/src/de/mod.rs index 128efde09..c0e62494b 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -2,7 +2,10 @@ use std::{borrow::Cow, io, str}; use base64::Engine; -use serde::de::{self, DeserializeSeed, Deserializer as _, Visitor}; +use serde::{ + de::{self, DeserializeSeed, Deserializer as _, Visitor}, + Deserialize, +}; use self::{id::IdDeserializer, tag::TagDeserializer}; pub use crate::error::{Error, Position, SpannedError}; @@ -401,6 +404,11 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { + if let Some(b'[') = self.bytes.peek() { + let bytes = Vec::::deserialize(self)?; + return visitor.visit_byte_buf(bytes); + } + let res = { let string = self.bytes.string()?; let base64_str = match string { diff --git a/tests/462_bytes.rs b/tests/462_bytes.rs new file mode 100644 index 000000000..ac9189039 --- /dev/null +++ b/tests/462_bytes.rs @@ -0,0 +1,22 @@ +#[test] +fn test_deserialise_byte_slice() { + let val: &[u8] = &[0_u8, 1_u8, 2_u8, 3_u8]; + let ron = ron::to_string(val).unwrap(); + assert_eq!(ron, "[0,1,2,3]"); + + // deserialising a byte slice from a byte sequence should fail + // with the error that a borrowed slice was expected but a byte + // buffer was provided + // NOT with an expected string error, since the byte slice + // serialisation never serialises to a string + assert_eq!( + ron::from_str::<&[u8]>(&ron), + Err(ron::error::SpannedError { + code: ron::error::Error::InvalidValueForType { + expected: String::from("a borrowed byte array"), + found: String::from("the bytes \"AAECAw==\""), + }, + position: ron::error::Position { line: 1, col: 10 }, + }) + ); +} From 90d98d558d1bdcd77e7d649142256f9a623ba09f Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Sat, 8 Jul 2023 11:31:27 +0300 Subject: [PATCH 24/29] Protect against stack overflow in arbitrary fuzzer (#464) --- fuzz/fuzz_targets/arbitrary.rs | 44 ++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/fuzz/fuzz_targets/arbitrary.rs b/fuzz/fuzz_targets/arbitrary.rs index 0adf49d7a..eee678a21 100644 --- a/fuzz/fuzz_targets/arbitrary.rs +++ b/fuzz/fuzz_targets/arbitrary.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use std::fmt; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; +use std::sync::atomic::{AtomicUsize, Ordering}; use arbitrary::{Arbitrary, Unstructured}; use libfuzzer_sys::fuzz_target; @@ -37,10 +38,12 @@ enum SerdeData<'a> { I32(i32), I64(i64), I128(i128), + ISize(isize), U8(u8), U16(u16), U32(u32), U128(u128), + USize(usize), F32(F32), F64(F64), Char(char), @@ -50,20 +53,41 @@ enum SerdeData<'a> { #[serde(borrow)] Bytes(Cow<'a, [u8]>), ByteBuf(Vec), - Option(Option>), + Option(#[arbitrary(with = arbitrary_recursion_guard)] Option>), Unit(()), #[serde(borrow)] - Map(SerdeMap<'a>), - Seq(Vec), + Map(#[arbitrary(with = arbitrary_recursion_guard)] SerdeMap<'a>), + Seq(#[arbitrary(with = arbitrary_recursion_guard)] Vec), #[serde(borrow)] - Enum(SerdeEnum<'a>), + Enum(#[arbitrary(with = arbitrary_recursion_guard)] SerdeEnum<'a>), #[serde(borrow)] - Struct(SerdeStruct<'a>), + Struct(#[arbitrary(with = arbitrary_recursion_guard)] SerdeStruct<'a>), +} + +fn arbitrary_recursion_guard<'a, T: Arbitrary<'a> + Default>( + u: &mut Unstructured<'a>, +) -> arbitrary::Result { + static RECURSION_DEPTH: AtomicUsize = AtomicUsize::new(0); + + let max_depth = ron::Options::default() + .recursion_limit + .map_or(256, |limit| limit * 2); + + let result = if RECURSION_DEPTH.fetch_add(1, Ordering::Relaxed) < max_depth { + T::arbitrary(u) + } else { + Ok(T::default()) + }; + + RECURSION_DEPTH.fetch_sub(1, Ordering::Relaxed); + + result } #[allow(clippy::enum_variant_names)] -#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] +#[derive(Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] enum SerdeEnum<'a> { + #[default] UnitVariant, #[serde(borrow)] NewtypeVariant(Box>), @@ -86,6 +110,12 @@ enum SerdeStruct<'a> { Struct(SerdeStructStruct<'a>), } +impl<'a> Default for SerdeStruct<'a> { + fn default() -> Self { + Self::Unit(SerdeUnitStruct) + } +} + #[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Arbitrary)] struct SerdeUnitStruct; @@ -152,7 +182,7 @@ impl Hash for F64 { } } -#[derive(Debug, PartialEq, Eq, Hash, Arbitrary)] +#[derive(Debug, Default, PartialEq, Eq, Hash, Arbitrary)] struct SerdeMap<'a>(Vec<(SerdeData<'a>, SerdeData<'a>)>); impl<'a> Serialize for SerdeMap<'a> { From 4076f29a3f2ed976640e680e1632e0f6fa31f63e Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 18 Jul 2023 08:15:48 -0700 Subject: [PATCH 25/29] Fix "WASD" example in README (#466) * trim trailing whitespace in README * fix 'wasd' key map in README example, very important --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2fbf07dc6..be89f9610 100644 --- a/README.md +++ b/README.md @@ -18,23 +18,23 @@ GameConfig( // optional struct name window_size: (800, 600), window_title: "PAC-MAN", fullscreen: false, - + mouse_sensitivity: 1.4, key_bindings: { "up": Up, "down": Down, "left": Left, "right": Right, - + // Uncomment to enable WASD controls /* "W": Up, - "A": Down, - "S": Left, + "S": Down, + "A": Left, "D": Right, */ }, - + difficulty_options: ( start_difficulty: Easy, adaptive: false, @@ -146,7 +146,7 @@ struct MyStruct { fn main() { let x: MyStruct = ron::from_str("(boolean: true, float: 1.23)").unwrap(); - + println!("RON: {}", ron::to_string(&x).unwrap()); println!("Pretty RON: {}", ron::ser::to_string_pretty( From 963bd351628755a7bf7b0e7214223cb220c39f22 Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Fri, 21 Jul 2023 08:53:20 +0300 Subject: [PATCH 26/29] Update option-set dev-dependency to 0.2 (#468) --- Cargo.toml | 2 +- tests/152_bitflags.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c4908f6ff..698a6b2a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,4 +35,4 @@ serde_derive = "1.0" serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" serde_json = "1.0" -option_set = "0.1" +option_set = "0.2" diff --git a/tests/152_bitflags.rs b/tests/152_bitflags.rs index 8a4435ee0..b4085b93d 100644 --- a/tests/152_bitflags.rs +++ b/tests/152_bitflags.rs @@ -14,7 +14,6 @@ bitflags! { } option_set! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] struct TestBad: UpperCamel + u8 { const ONE = 1; const TWO = 1 << 1; From 285fb1fa9381f7a181b8e142ae0fe59fcd96207c Mon Sep 17 00:00:00 2001 From: Juniper Tyree <50025784+juntyr@users.noreply.github.com> Date: Sun, 6 Aug 2023 15:34:23 +0300 Subject: [PATCH 27/29] Update adjacent enum non-roundtrip tests to match serde-rs/serde#2505 (#471) Update adjacent enum non-roundtrip tests to match https://github.com/serde-rs/serde/pull/2505 --- tests/123_enum_representation.rs | 8 ++++---- tests/207_adjacently_tagged_enum.rs | 2 +- tests/393_serde_errors.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/123_enum_representation.rs b/tests/123_enum_representation.rs index 722baa8e3..414350f69 100644 --- a/tests/123_enum_representation.rs +++ b/tests/123_enum_representation.rs @@ -108,14 +108,14 @@ fn test_adjacently_a_ser() { bar: 2, different: Inner::Foo, }; - let e = "(type:\"VariantA\",content:(foo:1,bar:2,different:Foo))"; + let e = "(type:VariantA,content:(foo:1,bar:2,different:Foo))"; test_ser(&v, e); } #[test] fn test_adjacently_b_ser() { let v = EnumStructAdjacently::VariantB { foo: 1, bar: 2 }; - let e = "(type:\"VariantB\",content:(foo:1,bar:2))"; + let e = "(type:VariantB,content:(foo:1,bar:2))"; test_ser(&v, e); } @@ -175,7 +175,7 @@ fn test_internally_b_de() { #[test] fn test_adjacently_a_de() { - let s = "(type:\"VariantA\",content:(foo:1,bar:2,different:Foo))"; + let s = "(type:VariantA,content:(foo:1,bar:2,different:Foo))"; let e = EnumStructAdjacently::VariantA { foo: 1, bar: 2, @@ -186,7 +186,7 @@ fn test_adjacently_a_de() { #[test] fn test_adjacently_b_de() { - let s = "(type:\"VariantB\",content:(foo:1,bar:2))"; + let s = "(type:VariantB,content:(foo:1,bar:2))"; let e = EnumStructAdjacently::VariantB { foo: 1, bar: 2 }; test_de(s, e); } diff --git a/tests/207_adjacently_tagged_enum.rs b/tests/207_adjacently_tagged_enum.rs index 46fda5c64..8a81d753e 100644 --- a/tests/207_adjacently_tagged_enum.rs +++ b/tests/207_adjacently_tagged_enum.rs @@ -14,7 +14,7 @@ fn test_adjacently_tagged() { let ron_string = to_string(&source).unwrap(); - assert_eq!(ron_string, "(type:\"Index\",data:1)"); + assert_eq!(ron_string, "(type:Index,data:1)"); let deserialized = from_str::(&ron_string).unwrap(); diff --git a/tests/393_serde_errors.rs b/tests/393_serde_errors.rs index f57532bf6..9246e6633 100644 --- a/tests/393_serde_errors.rs +++ b/tests/393_serde_errors.rs @@ -188,13 +188,13 @@ fn test_adjacently_tagged_enum() { // the enum as a struct assert_eq!( - ron::from_str::("(type: \"StructVariant\", content: (d: 4))"), + ron::from_str::("(type: StructVariant, content: (d: 4))"), Err(SpannedError { code: Error::MissingStructField { field: "a", outer: Some(String::from("TestEnumAdjacent")), }, - position: Position { line: 1, col: 39 }, + position: Position { line: 1, col: 37 }, }) ); } From 3dd5ac2d7dbb53eb71f41a90534c6c90bf134819 Mon Sep 17 00:00:00 2001 From: Juniper Tyree Date: Tue, 15 Aug 2023 23:46:05 +0000 Subject: [PATCH 28/29] Prepare CHANGELOG for v0.8.1 --- CHANGELOG.md | 5 +++-- Cargo.toml | 2 +- src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77a4af87f..da2f040a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,16 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [0.8.1] - 2023-08-?? - Fix issues [#277](https://github.com/ron-rs/ron/issues/277) and [#405](https://github.com/ron-rs/ron/issues/405) with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` ([#406](https://github.com/ron-rs/ron/pull/406)) - Fix issue [#401](https://github.com/ron-rs/ron/issues/401) with correct raw struct name identifier parsing ([#402](https://github.com/ron-rs/ron/pull/402)) - Fix issue [#410](https://github.com/ron-rs/ron/issues/410) trailing comma parsing in tuples and `Some` ([#412](https://github.com/ron-rs/ron/pull/412)) - Error instead of panic when deserializing non-identifiers as field names ([#415](https://github.com/ron-rs/ron/pull/415)) -- Breaking: Fix issue [#307](https://github.com/ron-rs/ron/issues/307) stack overflow with explicit recursion limits in serialising and deserialising ([#420](https://github.com/ron-rs/ron/pull/420)) +- [Non-API] Breaking: Fix issue [#307](https://github.com/ron-rs/ron/issues/307) stack overflow with explicit recursion limits in serialising and deserialising ([#420](https://github.com/ron-rs/ron/pull/420)) - Fix issue [#423](https://github.com/ron-rs/ron/issues/423) deserialising an identifier into a borrowed str ([#424](https://github.com/ron-rs/ron/pull/424)) - Bump MSRV to 1.57.0 and bump dependency: `base64` to 0.20 ([#431](https://github.com/ron-rs/ron/pull/431)) - Bump dependency `base64` to 0.21 ([#433](https://github.com/ron-rs/ron/pull/433)) - Depend on `serde_derive` directly to potentially enable more compilation parallelism ([#441](https://github.com/ron-rs/ron/pull/441)) -- Breaking: Bump `bitflags` dependency to 2.0, changes `serde` impls of `Extensions` ([#443](https://github.com/ron-rs/ron/pull/443)) +- [Non-API] Breaking: Bump `bitflags` dependency to 2.0, changes `serde` impls of `Extensions` ([#443](https://github.com/ron-rs/ron/pull/443)) - Add `Map::retain` method ([#460](https://github.com/ron-rs/ron/pull/460)) - Bump MSRV to 1.64.0 and bump dependency: `indexmap` to 2.0 ([#459](https://github.com/ron-rs/ron/pull/459)) diff --git a/Cargo.toml b/Cargo.toml index 698a6b2a5..8233d726e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ron" # Memo: update version in src/lib.rs too (doc link) -version = "0.8.0" +version = "0.8.1" license = "MIT/Apache-2.0" keywords = ["parser", "serde", "serialization"] authors = [ diff --git a/src/lib.rs b/src/lib.rs index 418f9faf3..06db05a75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,7 @@ Serializing / Deserializing is as simple as calling `to_string` / `from_str`. !*/ -#![doc(html_root_url = "https://docs.rs/ron/0.8.0")] +#![doc(html_root_url = "https://docs.rs/ron/0.8.1")] pub mod de; pub mod ser; From 90fd4841571fe4e2c9a3e5ac386655c56de186f0 Mon Sep 17 00:00:00 2001 From: Juniper Tyree Date: Thu, 17 Aug 2023 05:08:21 +0000 Subject: [PATCH 29/29] Add release date for 0.8.1 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 557b277bf..fd9ec5977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -## [0.8.1] - 2023-08-?? +## [0.8.1] - 2023-08-17 - Fix issues [#277](https://github.com/ron-rs/ron/issues/277) and [#405](https://github.com/ron-rs/ron/issues/405) with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` ([#406](https://github.com/ron-rs/ron/pull/406)) - Fix issue [#401](https://github.com/ron-rs/ron/issues/401) with correct raw struct name identifier parsing ([#402](https://github.com/ron-rs/ron/pull/402)) - Fix issue [#410](https://github.com/ron-rs/ron/issues/410) trailing comma parsing in tuples and `Some` ([#412](https://github.com/ron-rs/ron/pull/412))