From 8fe116d0fe78fc7422b7e276e05ab2902316bcd4 Mon Sep 17 00:00:00 2001 From: Momo Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Fri, 28 Jan 2022 10:44:56 +0200 Subject: [PATCH] Implement support for `DeserializeSeed` through `Options` (#360) --- CHANGELOG.md | 1 + Cargo.toml | 4 ++ src/de/mod.rs | 4 +- src/options.rs | 40 ++++++++++++++++-- tests/152_bitflags.rs | 79 +++++++++++++++++++++++++++++++++++ tests/359_deserialize_seed.rs | 46 ++++++++++++++++++++ 6 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 tests/152_bitflags.rs create mode 100644 tests/359_deserialize_seed.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ba90b1f..54f3b400 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 [#289](https://github.com/ron-rs/ron/issues/289) enumerate_arrays comments ([#344](https://github.com/ron-rs/ron/pull/344)) - Report struct name in expected struct error ([#342](https://github.com/ron-rs/ron/pull/342)) - Add `Options` builder to configure the RON serde roundtrip ([#343](https://github.com/ron-rs/ron/pull/343)) +- Fix issue [#359](https://github.com/ron-rs/ron/issues/359) with `DeserializeSeed` support ([#360](https://github.com/ron-rs/ron/pull/360)) ## [0.7.0] - 2021-10-22 diff --git a/Cargo.toml b/Cargo.toml index 346f514a..73ae0985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,7 @@ serde = { version = "1.0.60", features = ["serde_derive"] } [dev-dependencies] serde_bytes = "0.11" serde_json = "1" + +# for testing bitflags compatibility +bitflags-serial = { git = "https://github.com/kvark/bitflags-serial" } +option_set = "0.1" diff --git a/src/de/mod.rs b/src/de/mod.rs index 1a2ac852..c27ef679 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -57,8 +57,8 @@ impl<'de> Deserializer<'de> { } } -/// A convenience function for reading data from a reader -/// and feeding into a deserializer. +/// A convenience function for building a deserializer +/// and deserializing a value of type `T` from a reader. pub fn from_reader(rdr: R) -> Result where R: io::Read, diff --git a/src/options.rs b/src/options.rs index 0f6fc4d1..4dd3a992 100644 --- a/src/options.rs +++ b/src/options.rs @@ -66,8 +66,8 @@ impl Options { } impl Options { - /// A convenience function for reading data from a reader - /// and feeding into a deserializer. + /// A convenience function for building a deserializer + /// and deserializing a value of type `T` from a reader. pub fn from_reader(&self, mut rdr: R) -> Result where R: io::Read, @@ -93,10 +93,44 @@ impl Options { pub fn from_bytes<'a, T>(&self, s: &'a [u8]) -> Result where T: de::Deserialize<'a>, + { + self.from_bytes_seed(s, std::marker::PhantomData) + } + + /// A convenience function for building a deserializer + /// and deserializing a value of type `T` from a reader + /// and a seed. + pub fn from_reader_seed(&self, mut rdr: R, seed: S) -> Result + where + R: io::Read, + S: for<'a> de::DeserializeSeed<'a, Value = T>, + { + let mut bytes = Vec::new(); + rdr.read_to_end(&mut bytes)?; + + self.from_bytes_seed(&bytes, seed) + } + + /// A convenience function for building a deserializer + /// and deserializing a value of type `T` from a string + /// and a seed. + pub fn from_str_seed<'a, S, T>(&self, s: &'a str, seed: S) -> Result + where + S: de::DeserializeSeed<'a, Value = T>, + { + self.from_bytes_seed(s.as_bytes(), seed) + } + + /// A convenience function for building a deserializer + /// and deserializing a value of type `T` from bytes + /// and a seed. + pub fn from_bytes_seed<'a, S, T>(&self, s: &'a [u8], seed: S) -> Result + where + S: de::DeserializeSeed<'a, Value = T>, { let mut deserializer = Deserializer::from_bytes_with_options(s, self.clone())?; - let value = T::deserialize(&mut deserializer)?; + let value = seed.deserialize(&mut deserializer)?; deserializer.end()?; diff --git a/tests/152_bitflags.rs b/tests/152_bitflags.rs new file mode 100644 index 00000000..7e784f39 --- /dev/null +++ b/tests/152_bitflags.rs @@ -0,0 +1,79 @@ +use bitflags::*; +use option_set::option_set; + +#[macro_use] +extern crate bitflags_serial; + +bitflags! { + #[derive(serde::Serialize, serde::Deserialize)] + struct TestGood: u8 { + const ONE = 1; + const TWO = 1 << 1; + const THREE = 1 << 2; + } +} + +option_set! { + struct TestBad: UpperCamel + u8 { + const ONE = 1; + const TWO = 1 << 1; + const THREE = 1 << 2; + } +} + +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 + // https://github.com/ron-rs/ron/issues/152#issue-421298302 + let flag_good = TestGood::ONE | TestGood::TWO; + + 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)"); + + 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(); + + assert_eq!(json_de_good, flag_good); + assert_eq!(ron_de_good, flag_good); + + // option_set + let flag_bad = TestBad::ONE | TestBad::TWO; + + let json_ser_bad = serde_json::ser::to_string(&flag_bad).unwrap(); + let ron_ser_bad = ron::ser::to_string(&flag_bad).unwrap(); + + assert_eq!(json_ser_bad, "[\"One\",\"Two\"]"); + assert_eq!(ron_ser_bad, "[\"One\",\"Two\"]"); + + let json_de_bad: TestBad = serde_json::de::from_str(json_ser_bad.as_str()).unwrap(); + let ron_de_bad: TestBad = ron::de::from_str(ron_ser_bad.as_str()).unwrap(); + + 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); +} diff --git a/tests/359_deserialize_seed.rs b/tests/359_deserialize_seed.rs new file mode 100644 index 00000000..c193a7c1 --- /dev/null +++ b/tests/359_deserialize_seed.rs @@ -0,0 +1,46 @@ +#[test] +fn test_deserialize_seed() { + // Test adapted from David Tolnay's serde-yaml: + // https://github.com/dtolnay/serde-yaml/blob/8a806e316302fd2e6541dccee6d166dd51b689d6/tests/test_de.rs#L357-L392 + + struct Seed(i64); + + impl<'de> serde::de::DeserializeSeed<'de> for Seed { + type Value = i64; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + struct Visitor(i64); + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = i64; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "an integer") + } + + fn visit_i64(self, v: i64) -> Result { + Ok(v * self.0) + } + + fn visit_u64(self, v: u64) -> Result { + Ok(v as i64 * self.0) + } + } + + deserializer.deserialize_any(Visitor(self.0)) + } + } + + let cases = [("3", 5, 15), ("6", 7, 42), ("-5", 9, -45)]; + + for &(ron, seed, expected) in &cases { + let deserialized = ron::Options::default() + .from_str_seed(ron, Seed(seed)) + .unwrap(); + + assert_eq!(expected, deserialized); + } +}