diff --git a/CHANGELOG.md b/CHANGELOG.md index c71a0bb5..1b6e4de8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix issue [#321](https://github.com/ron-rs/ron/issues/321) and allow parsing UTF-8 identifiers ([#488](https://github.com/ron-rs/ron/pull/488)) - Breaking: Enforce that ron always writes valid UTF-8 ([#488](https://github.com/ron-rs/ron/pull/488)) - Fix parsing of struct/variant names starting in `None`, `Some`, `true`, or `false` ([#499](https://github.com/ron-rs/ron/pull/499)) +- Add convenient `Value::from` impls ([#498](https://github.com/ron-rs/ron/pull/498)) ## [0.8.1] - 2023-08-17 diff --git a/src/de/tests.rs b/src/de/tests.rs index 9dab4c3f..d814fee6 100644 --- a/src/de/tests.rs +++ b/src/de/tests.rs @@ -16,9 +16,17 @@ struct EmptyStruct2 {} #[derive(Debug, PartialEq, Deserialize)] struct NewType(i32); +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename = "")] +struct UnnamedNewType(i32); + #[derive(Debug, PartialEq, Deserialize)] struct TupleStruct(f32, f32); +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename = "")] +struct UnnamedTupleStruct(f32, f32); + #[derive(Clone, Copy, Debug, PartialEq, Deserialize)] struct MyStruct { x: f32, @@ -56,8 +64,186 @@ fn test_struct() { check_from_str_bytes_reader("NewType(42)", Ok(NewType(42))); check_from_str_bytes_reader("(33)", Ok(NewType(33))); + check_from_str_bytes_reader::( + "NewType", + Err(SpannedError { + code: Error::ExpectedNamedStructLike("NewType"), + position: Position { line: 1, col: 8 }, + }), + ); + check_from_str_bytes_reader::( + "", + Err(SpannedError { + code: Error::ExpectedStructLike, + position: Position { line: 1, col: 1 }, + }), + ); + check_from_str_bytes_reader("(33)", Ok(UnnamedNewType(33))); + check_from_str_bytes_reader::( + "Newtype", + Err(SpannedError { + code: Error::ExpectedNamedStructLike(""), + position: Position { line: 1, col: 8 }, + }), + ); + check_from_str_bytes_reader("TupleStruct(2,5,)", Ok(TupleStruct(2.0, 5.0))); check_from_str_bytes_reader("(3,4)", Ok(TupleStruct(3.0, 4.0))); + check_from_str_bytes_reader::( + "", + Err(SpannedError { + code: Error::ExpectedNamedStructLike("TupleStruct"), + position: Position { line: 1, col: 1 }, + }), + ); + check_from_str_bytes_reader::( + "TupleStruct(2,5,)", + Err(SpannedError { + code: Error::ExpectedNamedStructLike(""), + position: Position { line: 1, col: 12 }, + }), + ); + check_from_str_bytes_reader("(3,4)", Ok(UnnamedTupleStruct(3.0, 4.0))); + check_from_str_bytes_reader::( + "", + Err(SpannedError { + code: Error::ExpectedStructLike, + position: Position { line: 1, col: 1 }, + }), + ); +} + +#[test] +fn test_unclosed_limited_seq_struct() { + #[derive(Debug, PartialEq)] + struct LimitedStruct; + + impl<'de> serde::Deserialize<'de> for LimitedStruct { + fn deserialize>(deserializer: D) -> Result { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = LimitedStruct; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("struct LimitedStruct") + } + // GRCOV_EXCL_STOP + + fn visit_map>( + self, + _map: A, + ) -> Result { + Ok(LimitedStruct) + } + } + + deserializer.deserialize_struct("LimitedStruct", &[], Visitor) + } + } + + check_from_str_bytes_reader::( + "(", + Err(SpannedError { + code: Error::ExpectedStructLikeEnd, + position: Position { line: 1, col: 2 }, + }), + ) +} + +#[test] +fn test_unclosed_limited_seq() { + #[derive(Debug, PartialEq)] + struct LimitedSeq; + + impl<'de> serde::Deserialize<'de> for LimitedSeq { + fn deserialize>(deserializer: D) -> Result { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = LimitedSeq; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("an empty sequence") + } + // GRCOV_EXCL_STOP + + fn visit_seq>( + self, + _seq: A, + ) -> Result { + Ok(LimitedSeq) + } + } + + deserializer.deserialize_seq(Visitor) + } + } + + check_from_str_bytes_reader::( + "[", + Err(SpannedError { + code: Error::ExpectedArrayEnd, + position: Position { line: 1, col: 2 }, + }), + ); + + assert_eq!( + crate::Value::from(vec![42]).into_rust::(), + Err(Error::ExpectedDifferentLength { + expected: String::from("a sequence of length 0"), + found: 1 + }) + ); +} + +#[test] +fn test_unclosed_limited_map() { + #[derive(Debug, PartialEq)] + struct LimitedMap; + + impl<'de> serde::Deserialize<'de> for LimitedMap { + fn deserialize>(deserializer: D) -> Result { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = LimitedMap; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("an empty map") + } + // GRCOV_EXCL_STOP + + fn visit_map>( + self, + _map: A, + ) -> Result { + Ok(LimitedMap) + } + } + + deserializer.deserialize_map(Visitor) + } + } + + check_from_str_bytes_reader::( + "{", + Err(SpannedError { + code: Error::ExpectedMapEnd, + position: Position { line: 1, col: 2 }, + }), + ); + + assert_eq!( + crate::Value::Map([("a", 42)].into_iter().collect()).into_rust::(), + Err(Error::ExpectedDifferentLength { + expected: String::from("a map of length 0"), + found: 1 + }) + ); } #[test] @@ -70,6 +256,13 @@ fn test_option() { fn test_enum() { check_from_str_bytes_reader("A", Ok(MyEnum::A)); check_from_str_bytes_reader("B(true,)", Ok(MyEnum::B(true))); + check_from_str_bytes_reader::( + "B", + Err(SpannedError { + code: Error::ExpectedStructLike, + position: Position { line: 1, col: 2 }, + }), + ); check_from_str_bytes_reader("C(true,3.5,)", Ok(MyEnum::C(true, 3.5))); check_from_str_bytes_reader("D(a:2,b:3,)", Ok(MyEnum::D { a: 2, b: 3 })); } @@ -206,10 +399,24 @@ fn untagged() { enum Untagged { U8(u8), Bool(bool), + Value(crate::Value), } check_from_str_bytes_reader("true", Ok(Untagged::Bool(true))); check_from_str_bytes_reader("8", Ok(Untagged::U8(8))); + + // Check for a failure in Deserializer::check_struct_type + // - untagged enum and a leading identifier trigger the serde content enum path + // - serde content uses deserialize_any, which retriggers the struct type check + // - struct type check inside a serde content performs a full newtype check + // - newtype check fails on the unclosed struct + check_from_str_bytes_reader::( + "Value(()", + Err(crate::error::SpannedError { + code: crate::Error::Eof, + position: crate::error::Position { line: 1, col: 9 }, + }), + ); } #[test] diff --git a/src/de/value.rs b/src/de/value.rs index 5c9003d4..b7e69b61 100644 --- a/src/de/value.rs +++ b/src/de/value.rs @@ -222,7 +222,12 @@ impl<'de> Visitor<'de> for ValueVisitor { { let mut res: Map = Map::new(); - while let Some(entry) = map.next_entry()? { + #[cfg(feature = "indexmap")] + if let Some(cap) = map.size_hint() { + res.0.reserve_exact(cap); + } + + while let Some(entry) = map.next_entry::()? { res.insert(entry.0, entry.1); } @@ -416,5 +421,16 @@ mod tests { position: crate::error::Position { line: 1, col: 4 }, }, ); + + // Check for a failure in Deserializer::check_struct_type + // - opening brace triggers the struct type check + // - unclosed block comment fails the whitespace skip + assert_eq!( + "( /*".parse::().unwrap_err(), + crate::error::SpannedError { + code: crate::Error::UnclosedBlockComment, + position: crate::error::Position { line: 1, col: 5 }, + }, + ); } } diff --git a/src/error.rs b/src/error.rs index 34371269..74b65b29 100644 --- a/src/error.rs +++ b/src/error.rs @@ -632,10 +632,10 @@ mod tests { ); check_error_message( &Error::MissingStructField { - field: "b+c", + field: "", outer: Some(String::from("S+T")), }, - "Unexpected missing field named `r#b+c` in `r#S+T`", + "Unexpected missing field named \"\"_[invalid identifier] in `r#S+T`", ); check_error_message( &Error::duplicate_field("a"), diff --git a/src/parse.rs b/src/parse.rs index 0c3a989e..3ea74a04 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -583,9 +583,9 @@ impl<'a> Parser<'a> { } let mut braces = 1_usize; - let mut comma = false; + let mut more_than_one = false; - // Skip ahead to see if the value is followed by a comma + // Skip ahead to see if the value is followed by another value while braces > 0 { // Skip spurious braces in comments, strings, and characters parser.skip_ws()?; @@ -608,12 +608,13 @@ impl<'a> Parser<'a> { } else if matches!(c, ')' | ']' | '}') { braces -= 1; } else if c == ',' && braces == 1 { - comma = true; + parser.skip_ws()?; + more_than_one = !parser.check_char(')'); break; } } - if comma { + if more_than_one { Ok(StructType::Tuple) } else { Ok(StructType::NewtypeOrTuple) @@ -658,6 +659,10 @@ impl<'a> Parser<'a> { Err(_) => return Err(Error::ExpectedNamedStructLike(ident)), }; + if ident.is_empty() { + return Err(Error::ExpectedNamedStructLike(ident)); + } + if found_ident != ident { return Err(Error::ExpectedDifferentStructName { expected: ident, diff --git a/src/value/map.rs b/src/value/map.rs index cdef2be2..2c100c02 100644 --- a/src/value/map.rs +++ b/src/value/map.rs @@ -17,7 +17,7 @@ use super::Value; /// to preserve the order of the parsed map. #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(transparent)] -pub struct Map(MapInner); +pub struct Map(pub(crate) MapInner); #[cfg(not(feature = "indexmap"))] type MapInner = std::collections::BTreeMap; @@ -27,7 +27,7 @@ type MapInner = indexmap::IndexMap; impl Map { /// Creates a new, empty [`Map`]. #[must_use] - pub fn new() -> Map { + pub fn new() -> Self { Self::default() } @@ -56,8 +56,8 @@ impl Map { /// Inserts a new element, returning the previous element with this `key` if /// there was any. - pub fn insert(&mut self, key: Value, value: Value) -> Option { - self.0.insert(key, value) + pub fn insert(&mut self, key: impl Into, value: impl Into) -> Option { + self.0.insert(key.into(), value.into()) } /// Removes an element by its `key`. @@ -107,15 +107,16 @@ impl Map { impl Index<&Value> for Map { type Output = Value; + #[allow(clippy::expect_used)] fn index(&self, index: &Value) -> &Self::Output { - &self.0[index] + self.get(index).expect("no entry found for key") } } impl IndexMut<&Value> for Map { #[allow(clippy::expect_used)] fn index_mut(&mut self, index: &Value) -> &mut Self::Output { - self.0.get_mut(index).expect("no entry found for key") + self.get_mut(index).expect("no entry found for key") } } @@ -129,16 +130,19 @@ impl IntoIterator for Map { } } -impl FromIterator<(Value, Value)> for Map { - fn from_iter>(iter: T) -> Self { - Map(MapInner::from_iter(iter)) +impl, V: Into> FromIterator<(K, V)> for Map { + fn from_iter>(iter: T) -> Self { + Map(iter + .into_iter() + .map(|(key, value)| (key.into(), value.into())) + .collect()) } } /// Note: equality is only given if both values and order of values match impl PartialEq for Map { fn eq(&self, other: &Map) -> bool { - self.cmp(other) == Ordering::Equal + self.cmp(other).is_eq() } } @@ -162,3 +166,110 @@ impl Hash for Map { self.iter().for_each(|x| x.hash(state)); } } + +#[cfg(test)] +mod tests { + use super::{Map, Value}; + + #[test] + fn map_usage() { + let mut map = Map::new(); + assert_eq!(map.len(), 0); + assert!(map.is_empty()); + + map.insert("a", 42); + assert_eq!(map.len(), 1); + assert!(!map.is_empty()); + + assert_eq!(map.keys().collect::>(), vec![&Value::from("a")]); + assert_eq!(map.values().collect::>(), vec![&Value::from(42)]); + assert_eq!( + map.iter().collect::>(), + vec![(&Value::from("a"), &Value::from(42))] + ); + + assert_eq!(map.get(&Value::from("a")), Some(&Value::from(42))); + assert_eq!(map.get(&Value::from("b")), None); + assert_eq!(map.get_mut(&Value::from("a")), Some(&mut Value::from(42))); + assert_eq!(map.get_mut(&Value::from("b")), None); + + map[&Value::from("a")] = Value::from(24); + assert_eq!(&map[&Value::from("a")], &Value::from(24)); + + for (key, value) in map.iter_mut() { + if key == &Value::from("a") { + *value = Value::from(42); + } + } + assert_eq!(&map[&Value::from("a")], &Value::from(42)); + + map.values_mut().for_each(|value| *value = Value::from(24)); + assert_eq!(&map[&Value::from("a")], &Value::from(24)); + + map.insert("b", 42); + assert_eq!(map.len(), 2); + assert!(!map.is_empty()); + assert_eq!(map.get(&Value::from("a")), Some(&Value::from(24))); + assert_eq!(map.get(&Value::from("b")), Some(&Value::from(42))); + + map.retain(|key, value| { + if key == &Value::from("a") { + *value = Value::from(42); + true + } else { + false + } + }); + assert_eq!(map.len(), 1); + assert_eq!(map.get(&Value::from("a")), Some(&Value::from(42))); + assert_eq!(map.get(&Value::from("b")), None); + + assert_eq!(map.remove(&Value::from("b")), None); + assert_eq!(map.remove(&Value::from("a")), Some(Value::from(42))); + assert_eq!(map.remove(&Value::from("a")), None); + } + + #[test] + fn map_hash() { + assert_same_hash(&Map::new(), &Map::new()); + assert_same_hash( + &[("a", 42)].into_iter().collect(), + &[("a", 42)].into_iter().collect(), + ); + assert_same_hash( + &[("b", 24), ("c", 42)].into_iter().collect(), + &[("b", 24), ("c", 42)].into_iter().collect(), + ); + } + + fn assert_same_hash(a: &Map, b: &Map) { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + assert_eq!(a, b); + assert!(a.cmp(b).is_eq()); + assert_eq!(a.partial_cmp(b), Some(std::cmp::Ordering::Equal)); + + let mut hasher = DefaultHasher::new(); + a.hash(&mut hasher); + let h1 = hasher.finish(); + + let mut hasher = DefaultHasher::new(); + b.hash(&mut hasher); + let h2 = hasher.finish(); + + assert_eq!(h1, h2); + } + + #[test] + #[should_panic(expected = "no entry found for key")] + fn map_index_panic() { + let _ = &Map::new()[&Value::Unit]; + } + + #[test] + #[should_panic(expected = "no entry found for key")] + fn map_index_mut_panic() { + let _ = &mut Map::new()[&Value::Unit]; + } +} diff --git a/src/value/mod.rs b/src/value/mod.rs index 4c0f56c9..28ea83f9 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,6 +1,6 @@ //! Value module. -use std::{cmp::Eq, hash::Hash}; +use std::{borrow::Cow, cmp::Eq, hash::Hash}; use serde::{ de::{DeserializeOwned, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor}, @@ -31,6 +31,91 @@ pub enum Value { Unit, } +impl From for Value { + fn from(value: bool) -> Self { + Self::Bool(value) + } +} + +impl From for Value { + fn from(value: char) -> Self { + Self::Char(value) + } +} + +impl, V: Into> FromIterator<(K, V)> for Value { + fn from_iter>(iter: T) -> Self { + Self::Map(iter.into_iter().collect()) + } +} + +impl From for Value { + fn from(value: Map) -> Self { + Self::Map(value) + } +} + +impl> From for Value { + fn from(value: T) -> Self { + Self::Number(value.into()) + } +} + +impl> From> for Value { + fn from(value: Option) -> Self { + Self::Option(value.map(Into::into).map(Box::new)) + } +} + +impl<'a> From<&'a str> for Value { + fn from(value: &'a str) -> Self { + String::from(value).into() + } +} + +impl<'a> From> for Value { + fn from(value: Cow<'a, str>) -> Self { + String::from(value).into() + } +} + +impl From for Value { + fn from(value: String) -> Self { + Self::String(value) + } +} + +/// Special case to allow `Value::from(b"byte string")` +impl From<&'static [u8; N]> for Value { + fn from(value: &'static [u8; N]) -> Self { + Self::Bytes(Vec::from(*value)) + } +} + +impl> FromIterator for Value { + fn from_iter>(iter: I) -> Self { + Self::Seq(iter.into_iter().map(Into::into).collect()) + } +} + +impl<'a, T: Clone + Into> From<&'a [T]> for Value { + fn from(value: &'a [T]) -> Self { + value.iter().map(Clone::clone).map(Into::into).collect() + } +} + +impl> From> for Value { + fn from(value: Vec) -> Self { + value.into_iter().collect() + } +} + +impl From<()> for Value { + fn from(_value: ()) -> Self { + Value::Unit + } +} + impl Value { /// Tries to deserialize this [`Value`] into `T`. pub fn into_rust(self) -> Result @@ -185,9 +270,12 @@ mod tests { let direct: T = from_str(s).unwrap(); let value: Value = from_str(s).unwrap(); - let value = T::deserialize(value).unwrap(); + let de = T::deserialize(value.clone()).unwrap(); + + assert_eq!(direct, de, "Deserialization for {:?} is not the same", s); - assert_eq!(direct, value, "Deserialization for {:?} is not the same", s); + let value_roundtrip = Value::deserialize(value.clone()).unwrap(); + assert_eq!(value_roundtrip, value); } fn assert_same_bytes<'de, T>(s: &'de [u8]) @@ -198,33 +286,63 @@ mod tests { let direct: T = from_bytes(s).unwrap(); let value: Value = from_bytes(s).unwrap(); - let value = T::deserialize(value).unwrap(); + let de = T::deserialize(value.clone()).unwrap(); - assert_eq!(direct, value, "Deserialization for {:?} is not the same", s); + assert_eq!(direct, de, "Deserialization for {:?} is not the same", s); + + let value_roundtrip = Value::deserialize(value.clone()).unwrap(); + assert_eq!(value_roundtrip, value); } #[test] fn boolean() { assert_same::("true"); assert_same::("false"); + + assert_eq!(Value::from(true), Value::Bool(true)); + assert_eq!(Value::from(false), Value::Bool(false)); } #[test] fn float() { assert_same::("0.123"); assert_same::("-4.19"); + + assert_eq!( + Value::from(42_f32), + Value::Number(Number::F32(42_f32.into())) + ); + assert_eq!( + Value::from(42_f64), + Value::Number(Number::F64(42_f64.into())) + ); } #[test] fn int() { assert_same::("626"); assert_same::("-50"); + + assert_eq!(Value::from(0_i8), Value::Number(Number::I8(0))); + assert_eq!(Value::from(0_i16), Value::Number(Number::I16(0))); + assert_eq!(Value::from(0_i32), Value::Number(Number::I32(0))); + assert_eq!(Value::from(0_i64), Value::Number(Number::I64(0))); + #[cfg(feature = "integer128")] + assert_eq!(Value::from(0_i128), Value::Number(Number::I128(0))); + assert_eq!(Value::from(0_u8), Value::Number(Number::U8(0))); + assert_eq!(Value::from(0_u16), Value::Number(Number::U16(0))); + assert_eq!(Value::from(0_u32), Value::Number(Number::U32(0))); + assert_eq!(Value::from(0_u64), Value::Number(Number::U64(0))); + #[cfg(feature = "integer128")] + assert_eq!(Value::from(0_u128), Value::Number(Number::U128(0))); } #[test] fn char() { assert_same::("'4'"); assert_same::("'c'"); + + assert_eq!(Value::from('🦀'), Value::Char('🦀')); } #[test] @@ -232,6 +350,16 @@ mod tests { assert_same::(r#""hello world""#); assert_same::(r#""this is a Rusty 🦀 string""#); assert_same::(r#""this is now valid UTF-8 \xf0\x9f\xa6\x80""#); + + assert_eq!(Value::from("slice"), Value::String(String::from("slice"))); + assert_eq!( + Value::from(String::from("string")), + Value::String(String::from("string")) + ); + assert_eq!( + Value::from(Cow::Borrowed("cow")), + Value::String(String::from("cow")) + ); } #[test] @@ -240,6 +368,8 @@ mod tests { assert_same_bytes::( br#"b"this is not valid UTF-8 \xf8\xa1\xa1\xa1\xa1""#, ); + + assert_eq!(Value::from(b"bytes"), Value::Bytes(Vec::from(*b"bytes"))); } #[test] @@ -250,21 +380,123 @@ mod tests { 'b': \"Bye\", }", ); + + assert_eq!(Value::from(Map::new()), Value::Map(Map::new())); + assert_eq!( + Value::from_iter([("a", 42)]), + Value::Map({ + let mut map = Map::new(); + map.insert(Value::from("a"), Value::from(42)); + map + }) + ); } #[test] fn option() { assert_same::>("Some('a')"); assert_same::>("None"); + + assert_eq!(Value::from(Option::::None), Value::Option(None)); + assert_eq!( + Value::from(Some(false)), + Value::Option(Some(Box::new(Value::Bool(false)))) + ); + assert_eq!( + Value::from(Some(Option::::None)), + Value::Option(Some(Box::new(Value::Option(None)))) + ); } #[test] fn seq() { assert_same::>("[1.0, 2.0, 3.0, 4.0]"); + + assert_eq!( + Value::from([-1_i8, 2, -3].as_slice()), + Value::Seq(vec![ + Value::from(-1_i8), + Value::from(2_i8), + Value::from(-3_i8) + ]) + ); + assert_eq!( + Value::from(vec![-1_i8, 2, -3]), + Value::Seq(vec![ + Value::from(-1_i8), + Value::from(2_i8), + Value::from(-3_i8) + ]) + ); + assert_eq!( + Value::from_iter([-1_i8, 2, -3]), + Value::Seq(vec![ + Value::from(-1_i8), + Value::from(2_i8), + Value::from(-3_i8) + ]) + ); } #[test] fn unit() { assert_same::<()>("()"); + + assert_eq!(Value::from(()), Value::Unit); + } + + #[test] + #[should_panic(expected = "Contract violation: value before key")] + fn map_access_contract_violation() { + struct BadVisitor; + + impl<'de> Visitor<'de> for BadVisitor { + type Value = (); + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a map") + } + // GRCOV_EXCL_STOP + + fn visit_map>( + self, + mut map: A, + ) -> Result { + map.next_value::<()>() + } + } + + let value = Value::Map([("a", 42)].into_iter().collect()); + let _ = value.deserialize_map(BadVisitor); + } + + #[test] + fn transparent_value_newtype() { + struct NewtypeDeserializer; + + impl<'de> Deserializer<'de> for NewtypeDeserializer { + type Error = Error; + + fn deserialize_any>(self, visitor: V) -> Result { + visitor.visit_newtype_struct(serde::de::value::CharDeserializer::new('🦀')) + } + + // GRCOV_EXCL_START + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } + + #[cfg(feature = "integer128")] + forward_to_deserialize_any! { i128 u128 } + // GRCOV_EXCL_STOP + } + + assert_eq!( + Value::deserialize(NewtypeDeserializer).unwrap(), + Value::from('🦀') + ); } } diff --git a/src/value/number.rs b/src/value/number.rs index 1bf23b22..b38016b5 100644 --- a/src/value/number.rs +++ b/src/value/number.rs @@ -275,4 +275,11 @@ mod tests { assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN))); assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN))); } + + #[test] + fn test_partial_ord() { + assert!(F32(f32::NAN) > F32(f32::INFINITY)); + assert!(F32(-f32::NAN) < F32(f32::NEG_INFINITY)); + assert!(F32(f32::NAN) == F32(f32::NAN)); + } } diff --git a/tests/401_raw_identifier.rs b/tests/401_raw_identifier.rs index 5876ff29..72cc0b7d 100644 --- a/tests/401_raw_identifier.rs +++ b/tests/401_raw_identifier.rs @@ -165,7 +165,7 @@ fn test_invalid_identifiers() { let de = ron::from_str::("r#+").unwrap_err(); assert_eq!( format!("{}", de), - r#"1:4: Expected struct ""_[invalid identifier] but found `r#+`"#, + r#"1:4: Expected only opening `(`, no name, for un-nameable struct"#, ); } diff --git a/tests/465_unwrap_some_newtype_variant_value.rs b/tests/465_unwrap_some_newtype_variant_value.rs index 52c2e09d..512aaf8d 100644 --- a/tests/465_unwrap_some_newtype_variant_value.rs +++ b/tests/465_unwrap_some_newtype_variant_value.rs @@ -27,9 +27,9 @@ fn deserialise_value_with_unwrap_some_newtype_variant() { ); assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42,)"), - Ok(ron::Value::Option(Some(Box::new(ron::Value::Seq(vec![ - ron::Value::Number(ron::value::Number::U8(42)) - ]))))), + Ok(ron::Value::Option(Some(Box::new(ron::Value::Number( + ron::value::Number::U8(42) + ))))), ); assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some()"),