From 4116e77ec90b47f14afed8b95692b22b68c2f388 Mon Sep 17 00:00:00 2001 From: Juniper Langenstein <50025784+MomoLangenstein@users.noreply.github.com> Date: Mon, 12 Sep 2022 09:01:45 +0300 Subject: [PATCH] Implemented ron::value::RawValue (#407) * Implemented ron::value::RawValue * Added more test cases for full coverage --- CHANGELOG.md | 1 + src/de/mod.rs | 15 +++ src/error.rs | 2 + src/parse.rs | 2 +- src/ser/mod.rs | 5 + src/ser/raw.rs | 172 ++++++++++++++++++++++++++ src/{value.rs => value/mod.rs} | 4 + src/value/raw.rs | 189 ++++++++++++++++++++++++++++ tests/407_raw_value.rs | 220 +++++++++++++++++++++++++++++++++ 9 files changed, 609 insertions(+), 1 deletion(-) create mode 100644 src/ser/raw.rs rename src/{value.rs => value/mod.rs} (99%) create mode 100644 src/value/raw.rs create mode 100644 tests/407_raw_value.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 557b277b..02cd3ac7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +- Add `ron::value::RawValue` helper type which can (de)serialize any valid RON ([#407](https://github.com/ron-rs/ron/pull/407)) ## [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)) diff --git a/src/de/mod.rs b/src/de/mod.rs index c0e62494..dbde3137 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -483,6 +483,21 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { + if name == crate::value::raw::RAW_VALUE_TOKEN { + let bytes_before = self.bytes.bytes(); + self.bytes.skip_ws()?; + let _ignored = self.deserialize_ignored_any(serde::de::IgnoredAny)?; + self.bytes.skip_ws()?; + let bytes_after = self.bytes.bytes(); + + let ron_bytes = &bytes_before[..bytes_before.len() - bytes_after.len()]; + let ron_str = str::from_utf8(ron_bytes).map_err(Error::from)?; + + return visitor + .visit_borrowed_str::(ron_str) + .map_err(|_| Error::ExpectedRawValue); + } + if self.bytes.exts.contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { self.newtype_variant = false; diff --git a/src/error.rs b/src/error.rs index 406fb058..12b52832 100644 --- a/src/error.rs +++ b/src/error.rs @@ -91,6 +91,7 @@ pub enum Error { InvalidIdentifier(String), SuggestRawIdentifier(String), ExceededRecursionLimit, + ExpectedRawValue, } impl fmt::Display for SpannedError { @@ -250,6 +251,7 @@ impl fmt::Display for Error { identifier, identifier ), Error::ExceededRecursionLimit => f.write_str("Exceeded recursion limit, try increasing the limit and using `serde_stacker` to protect against a stack overflow"), + Error::ExpectedRawValue => f.write_str("Expected a `ron::value::RawValue`"), } } } diff --git a/src/parse.rs b/src/parse.rs index bed7f449..59047b34 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -359,7 +359,7 @@ impl<'a> Bytes<'a> { } } - pub fn bytes(&self) -> &[u8] { + pub fn bytes(&self) -> &'a [u8] { self.bytes } diff --git a/src/ser/mod.rs b/src/ser/mod.rs index b1aefbcb..aecdabb1 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -14,6 +14,7 @@ use crate::{ }, }; +mod raw; #[cfg(test)] mod tests; mod value; @@ -572,6 +573,10 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { where T: ?Sized + Serialize, { + if name == crate::value::raw::RAW_VALUE_TOKEN { + return value.serialize(raw::RawValueSerializer::new(self)); + } + if self.extensions().contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { self.newtype_variant = false; diff --git a/src/ser/raw.rs b/src/ser/raw.rs new file mode 100644 index 00000000..f9186798 --- /dev/null +++ b/src/ser/raw.rs @@ -0,0 +1,172 @@ +use std::io; + +use serde::{ser, Serialize}; + +use super::{Error, Result, Serializer}; + +pub struct RawValueSerializer<'a, W: io::Write> { + ser: &'a mut Serializer, +} + +impl<'a, W: io::Write> RawValueSerializer<'a, W> { + pub fn new(ser: &'a mut Serializer) -> Self { + Self { ser } + } +} + +impl<'a, W: io::Write> ser::Serializer for RawValueSerializer<'a, W> { + type Error = Error; + type Ok = (); + type SerializeMap = ser::Impossible<(), Error>; + type SerializeSeq = ser::Impossible<(), Error>; + type SerializeStruct = ser::Impossible<(), Error>; + type SerializeStructVariant = ser::Impossible<(), Error>; + type SerializeTuple = ser::Impossible<(), Error>; + type SerializeTupleStruct = ser::Impossible<(), Error>; + type SerializeTupleVariant = ser::Impossible<(), Error>; + + fn serialize_bool(self, _: bool) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_i8(self, _: i8) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_i16(self, _: i16) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_i32(self, _: i32) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_i64(self, _: i64) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + #[cfg(feature = "integer128")] + fn serialize_i128(self, _: i128) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_u8(self, _: u8) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_u16(self, _: u16) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_u32(self, _: u32) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_u64(self, _: u64) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + #[cfg(feature = "integer128")] + fn serialize_u128(self, _: u128) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_f32(self, _: f32) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_f64(self, _: f64) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_char(self, _: char) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_str(self, ron: &str) -> Result<()> { + self.ser.output.write_all(ron.as_bytes())?; + Ok(()) + } + + fn serialize_bytes(self, _: &[u8]) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_none(self) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_some(self, _: &T) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_unit(self) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_unit_struct(self, _: &'static str) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_newtype_struct(self, _: &'static str, _: &T) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_newtype_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: &T, + ) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_seq(self, _: Option) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_tuple(self, _: usize) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_tuple_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_tuple_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_map(self, _: Option) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_struct(self, _: &'static str, _: usize) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_struct_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Err(Error::ExpectedRawValue) + } +} diff --git a/src/value.rs b/src/value/mod.rs similarity index 99% rename from src/value.rs rename to src/value/mod.rs index ba843bc1..0056a124 100644 --- a/src/value.rs +++ b/src/value/mod.rs @@ -15,6 +15,10 @@ use serde_derive::{Deserialize, Serialize}; use crate::{de::Error, error::Result}; +pub(crate) mod raw; + +pub use raw::RawValue; + /// A [`Value`] to [`Value`] map. /// /// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the diff --git a/src/value/raw.rs b/src/value/raw.rs new file mode 100644 index 00000000..77c2cf64 --- /dev/null +++ b/src/value/raw.rs @@ -0,0 +1,189 @@ +// Inspired by David Tolnay's serde-rs +// https://github.com/serde-rs/json/blob/master/src/raw.rs +// Licensed under either of Apache License, Version 2.0 or MIT license at your option. + +use std::fmt; + +use serde::{de, ser, Deserialize, Serialize}; + +use crate::{ + error::{Error, SpannedResult}, + options::Options, +}; + +pub(crate) const RAW_VALUE_TOKEN: &str = "$ron::private::RawValue"; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct RawValue { + ron: str, +} + +impl RawValue { + fn from_borrowed_str(ron: &str) -> &Self { + // Safety: RawValue is a transparent newtype around str + unsafe { std::mem::transmute::<&str, &RawValue>(ron) } + } + + fn from_boxed_str(ron: Box) -> Box { + // Safety: RawValue is a transparent newtype around str + unsafe { std::mem::transmute::, Box>(ron) } + } + + fn into_boxed_str(raw_value: Box) -> Box { + // Safety: RawValue is a transparent newtype around str + unsafe { std::mem::transmute::, Box>(raw_value) } + } +} + +impl Clone for Box { + fn clone(&self) -> Self { + (**self).to_owned() + } +} + +impl ToOwned for RawValue { + type Owned = Box; + + fn to_owned(&self) -> Self::Owned { + RawValue::from_boxed_str(self.ron.to_owned().into_boxed_str()) + } +} + +impl fmt::Debug for RawValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("RawValue") + .field(&format_args!("{}", &self.ron)) + .finish() + } +} + +impl fmt::Display for RawValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.ron) + } +} + +impl RawValue { + /// Get the inner raw RON string, which is guaranteed to contain valid RON. + pub fn get_ron(&self) -> &str { + &self.ron + } + + /// Helper function to validate a RON string and turn it into a `RawValue`. + pub fn from_ron(ron: &str) -> SpannedResult<&Self> { + Options::default() + .from_str::<&Self>(ron) + .map(|_| Self::from_borrowed_str(ron)) + } + + /// Helper function to validate a RON string and turn it into a `RawValue`. + pub fn from_boxed_ron(ron: Box) -> SpannedResult> { + match Options::default().from_str::<&Self>(&*ron) { + Ok(_) => Ok(Self::from_boxed_str(ron)), + Err(err) => Err(err), + } + } + + /// Helper function to deserialize the inner RON string into `T`. + pub fn into_rust<'de, T: Deserialize<'de>>(&'de self) -> SpannedResult { + Options::default().from_str(&self.ron) + } + + /// Helper function to serialize `value` into a RON string. + pub fn from_rust(value: &T) -> Result, Error> { + let ron = Options::default().to_string(value)?; + + Ok(RawValue::from_boxed_str(ron.into_boxed_str())) + } +} + +impl From> for Box { + fn from(raw_value: Box) -> Self { + RawValue::into_boxed_str(raw_value) + } +} + +impl Serialize for RawValue { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_newtype_struct(RAW_VALUE_TOKEN, &self.ron) + } +} + +impl<'de: 'a, 'a> Deserialize<'de> for &'a RawValue { + fn deserialize>(deserializer: D) -> Result { + struct ReferenceVisitor; + + impl<'de> de::Visitor<'de> for ReferenceVisitor { + type Value = &'de RawValue; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // This error message only shows up with foreign Deserializers + write!(formatter, "any valid borrowed RON-value-string") + } + + fn visit_borrowed_str(self, ron: &'de str) -> Result { + match Options::default().from_str::(ron) { + Ok(_) => Ok(RawValue::from_borrowed_str(ron)), + Err(err) => Err(de::Error::custom(format!( + "invalid RON value at {}: {}", + err.position, err.code + ))), + } + } + + fn visit_newtype_struct>( + self, + deserializer: D, + ) -> Result { + deserializer.deserialize_str(self) + } + } + + deserializer.deserialize_newtype_struct(RAW_VALUE_TOKEN, ReferenceVisitor) + } +} + +impl<'de> Deserialize<'de> for Box { + fn deserialize>(deserializer: D) -> Result { + struct BoxedVisitor; + + impl<'de> de::Visitor<'de> for BoxedVisitor { + type Value = Box; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // This error message only shows up with foreign Deserializers + write!(formatter, "any valid RON-value-string") + } + + fn visit_str(self, ron: &str) -> Result { + match Options::default().from_str::(ron) { + Ok(_) => Ok(RawValue::from_boxed_str(ron.to_owned().into_boxed_str())), + Err(err) => Err(de::Error::custom(format!( + "invalid RON value at {}: {}", + err.position, err.code + ))), + } + } + + fn visit_string(self, ron: String) -> Result { + match Options::default().from_str::(&ron) { + Ok(_) => Ok(RawValue::from_boxed_str(ron.into_boxed_str())), + Err(err) => Err(de::Error::custom(format!( + "invalid RON value at {}: {}", + err.position, err.code + ))), + } + } + + fn visit_newtype_struct>( + self, + deserializer: D, + ) -> Result { + deserializer.deserialize_string(self) + } + } + + deserializer.deserialize_newtype_struct(RAW_VALUE_TOKEN, BoxedVisitor) + } +} diff --git a/tests/407_raw_value.rs b/tests/407_raw_value.rs new file mode 100644 index 00000000..c80eb201 --- /dev/null +++ b/tests/407_raw_value.rs @@ -0,0 +1,220 @@ +use ron::{ + de::from_bytes, + error::{Error, Position, SpannedError}, + from_str, to_string, + value::RawValue, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +struct WithRawValue { + a: bool, + b: Box, +} + +#[test] +fn test_raw_value_simple() { + let raw: &RawValue = from_str("true").unwrap(); + assert_eq!(raw.get_ron(), "true"); + let ser = to_string(raw).unwrap(); + assert_eq!(ser, "true"); +} + +#[test] +fn test_raw_value_inner() { + let raw: WithRawValue = from_str("(a: false, b: [1, /* lol */ 2, 3])").unwrap(); + assert_eq!(raw.b.get_ron(), "[1, /* lol */ 2, 3]"); + let ser = to_string(&raw).unwrap(); + assert_eq!(ser, "(a:false,b:[1, /* lol */ 2, 3])"); +} + +#[test] +fn test_raw_value_comment() { + let raw: WithRawValue = from_str("(a: false, b: /* nope */ 4)").unwrap(); + assert_eq!(raw.b.get_ron(), "4"); + + let raw: WithRawValue = from_str("(a: false, b: 4 /* yes */)").unwrap(); + assert_eq!(raw.b.get_ron(), "4 /* yes */"); + + let raw: WithRawValue = from_str("(a: false, b: (/* this */ 4 /* too */))").unwrap(); + assert_eq!(raw.b.get_ron(), "(/* this */ 4 /* too */)"); +} + +#[test] +fn test_raw_value_invalid() { + let err = from_str::<&RawValue>("4.d").unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::TrailingCharacters, + position: Position { line: 1, col: 3 } + } + ); + + let err = from_bytes::<&RawValue>(b"\0").unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::UnexpectedByte('\0'), + position: Position { line: 1, col: 1 } + } + ) +} + +#[test] +fn test_raw_value_from_ron() { + let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); + assert_eq!(raw.get_ron(), "/* hi */ (None, 4.2) /* bye */"); + + let err = RawValue::from_ron("4.d").unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::TrailingCharacters, + position: Position { line: 1, col: 3 } + } + ); + + let raw = + RawValue::from_boxed_ron(String::from("/* hi */ (None, 4.2) /* bye */").into_boxed_str()) + .unwrap(); + assert_eq!(raw.get_ron(), "/* hi */ (None, 4.2) /* bye */"); + + let err = RawValue::from_boxed_ron(String::from("(").into_boxed_str()).unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::Eof, + position: Position { line: 1, col: 2 }, + } + ); +} + +#[test] +fn test_raw_value_into_rust() { + let raw = RawValue::from_ron("/* hi */ (a: false, b: None) /* bye */").unwrap(); + + let with: WithRawValue = raw.into_rust().unwrap(); + assert_eq!( + with, + WithRawValue { + a: false, + b: from_str("None").unwrap(), + } + ); + + let err = raw.into_rust::().unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::ExpectedInteger, + position: Position { line: 1, col: 10 }, + } + ); +} + +#[test] +fn test_raw_value_from_rust() { + let raw = RawValue::from_rust(&42).unwrap(); + assert_eq!(raw.get_ron(), "42"); + + let raw = RawValue::from_rust(&WithRawValue { + a: true, + b: from_str("4.2").unwrap(), + }) + .unwrap(); + assert_eq!(raw.get_ron(), "(a:true,b:4.2)"); +} + +#[test] +fn test_raw_value_serde_json() { + let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); + + let ser = serde_json::to_string(&WithRawValue { + a: true, + b: raw.to_owned(), + }) + .unwrap(); + assert_eq!(ser, "{\"a\":true,\"b\":\"/* hi */ (None, 4.2) /* bye */\"}"); + + let with: WithRawValue = serde_json::from_str(&ser).unwrap(); + assert_eq!(raw, &*with.b); + + let err = + serde_json::from_str::("{\"a\":true,\"b\":\"/* hi */ (a:) /* bye */\"}") + .unwrap_err(); + assert_eq!( + err.to_string(), + "invalid RON value at 1:13: Unexpected byte ')' at line 1 column 39" + ); + + let err = serde_json::from_str::("{\"a\":true,\"b\":42}").unwrap_err(); + assert_eq!( + err.to_string(), + "invalid type: integer `42`, expected any valid RON-value-string at line 1 column 16" + ); + + let err = serde_json::from_str::<&RawValue>("\"/* hi */ (a:) /* bye */\"").unwrap_err(); + assert_eq!( + err.to_string(), + "invalid RON value at 1:13: Unexpected byte ')' at line 1 column 25" + ); + + let err = serde_json::from_str::<&RawValue>("42").unwrap_err(); + assert_eq!( + err.to_string(), + "invalid type: integer `42`, expected any valid borrowed RON-value-string at line 1 column 2" + ); +} + +#[test] +fn test_raw_value_clone_into() { + let raw = RawValue::from_boxed_ron(String::from("(None, 4.2)").into_boxed_str()).unwrap(); + let raw2 = raw.clone(); + assert_eq!(raw, raw2); + + let boxed_str: Box = raw2.into(); + assert_eq!(&*boxed_str, "(None, 4.2)"); +} + +#[test] +fn test_raw_value_debug_display() { + let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); + + assert_eq!(format!("{}", raw), "/* hi */ (None, 4.2) /* bye */"); + assert_eq!( + format!("{:#?}", raw), + "\ +RawValue( + /* hi */ (None, 4.2) /* bye */, +)\ + " + ); +} + +#[test] +fn test_boxed_raw_value_deserialise_from_string() { + let string = serde::de::value::StringDeserializer::::new(String::from("4.2")); + + let err = <&RawValue>::deserialize(string.clone()).unwrap_err(); + assert_eq!( + err, + Error::InvalidValueForType { + expected: String::from("any valid borrowed RON-value-string"), + found: String::from("the string \"4.2\""), + } + ); + + let boxed_raw = Box::::deserialize(string).unwrap(); + assert_eq!(boxed_raw.get_ron(), "4.2"); + + let string = serde::de::value::StringDeserializer::::new(String::from("[")); + + let err = Box::::deserialize(string).unwrap_err(); + assert_eq!( + err, + Error::Message(String::from( + "invalid RON value at 1:2: Unexpected end of RON" + )) + ); +}