diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71a7fade5..4a4b09c0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: include: # Test MSRV - - rust: 1.36.0 + - rust: 1.40.0 TARGET: x86_64-unknown-linux-gnu # Test nightly but don't fail diff --git a/src/de/enum_.rs b/src/de/enum_.rs index 7e516878e..9012873a5 100644 --- a/src/de/enum_.rs +++ b/src/de/enum_.rs @@ -53,3 +53,56 @@ impl<'de, 'a> de::VariantAccess<'de> for UnitVariantAccess<'a, 'de> { Err(Error::InvalidType) } } + +pub(crate) struct VariantAccess<'a, 'b> { + de: &'a mut Deserializer<'b>, +} + +impl<'a, 'b> VariantAccess<'a, 'b> { + pub(crate) fn new(de: &'a mut Deserializer<'b>) -> Self { + VariantAccess { de } + } +} + +impl<'a, 'de> de::EnumAccess<'de> for VariantAccess<'a, 'de> { + type Error = Error; + type Variant = Self; + + fn variant_seed(self, seed: V) -> Result<(V::Value, Self)> + where + V: de::DeserializeSeed<'de>, + { + let variant = seed.deserialize(&mut *self.de)?; + self.de.parse_object_colon()?; + Ok((variant, self)) + } +} + +impl<'de, 'a> de::VariantAccess<'de> for VariantAccess<'a, 'de> { + type Error = Error; + + fn unit_variant(self) -> Result<()> { + de::Deserialize::deserialize(self.de) + } + + fn newtype_variant_seed(self, seed: T) -> Result + where + T: de::DeserializeSeed<'de>, + { + seed.deserialize(self.de) + } + + fn tuple_variant(self, _len: usize, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + de::Deserializer::deserialize_seq(self.de, visitor) + } + + fn struct_variant(self, fields: &'static [&'static str], visitor: V) -> Result + where + V: de::Visitor<'de>, + { + de::Deserializer::deserialize_struct(self.de, "", fields, visitor) + } +} diff --git a/src/de/mod.rs b/src/de/mod.rs index da89308b0..5bed2dcfc 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -5,7 +5,7 @@ use core::{fmt, str}; use serde::de::{self, Visitor}; -use self::enum_::UnitVariantAccess; +use self::enum_::{UnitVariantAccess, VariantAccess}; use self::map::MapAccess; use self::seq::SeqAccess; @@ -18,6 +18,7 @@ pub type Result = core::result::Result; /// This type represents all possible errors that can occur when deserializing JSON data #[derive(Debug, PartialEq)] +#[non_exhaustive] pub enum Error { /// EOF while parsing a list. EofWhileParsingList, @@ -73,9 +74,6 @@ pub enum Error { /// Error with a custom message that was preserved. #[cfg(feature = "custom-error-messages")] CustomErrorWithMessage(heapless::String), - - #[doc(hidden)] - __Extensible, } #[cfg(feature = "std")] @@ -498,28 +496,40 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } - /// Unsupported. Use a more specific deserialize_* method - fn deserialize_unit(self, _visitor: V) -> Result + fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + let peek = match self.parse_whitespace() { + Some(b) => b, + None => { + return Err(Error::EofWhileParsingValue); + } + }; + + match peek { + b'n' => { + self.eat_char(); + self.parse_ident(b"ull")?; + visitor.visit_unit() + } + _ => Err(Error::InvalidType), + } } - /// Unsupported. Use a more specific deserialize_* method - fn deserialize_unit_struct(self, _name: &'static str, _visitor: V) -> Result + fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + self.deserialize_unit(visitor) } /// Unsupported. We can’t parse newtypes because we don’t know the underlying type. - fn deserialize_newtype_struct(self, _name: &'static str, _visitor: V) -> Result + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { - unreachable!() + visitor.visit_newtype_struct(self) } fn deserialize_seq(self, visitor: V) -> Result @@ -600,6 +610,17 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { { match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { b'"' => visitor.visit_enum(UnitVariantAccess::new(self)), + b'{' => { + self.eat_char(); + let value = visitor.visit_enum(VariantAccess::new(self))?; + match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? { + b'}' => { + self.eat_char(); + Ok(value) + } + _ => Err(Error::ExpectedSomeValue), + } + } _ => Err(Error::ExpectedSomeValue), } } @@ -961,6 +982,41 @@ mod tests { assert!(crate::from_str::(r#"{ "temperature": -1 }"#).is_err()); } + #[test] + fn test_unit() { + assert_eq!(crate::from_str::<()>(r#"null"#), Ok(((), 4))); + } + + #[test] + fn newtype_struct() { + #[derive(Deserialize, Debug, PartialEq)] + struct A(pub u32); + + assert_eq!(crate::from_str::(r#"54"#), Ok((A(54), 2))); + } + + #[test] + fn test_newtype_variant() { + #[derive(Deserialize, Debug, PartialEq)] + enum A { + A(u32), + } + let a = A::A(54); + let x = crate::from_str::(r#"{"A":54}"#); + assert_eq!(x, Ok((a, 8))); + } + + #[test] + fn test_struct_variant() { + #[derive(Deserialize, Debug, PartialEq)] + enum A { + A { x: u32, y: u16 }, + } + let a = A::A { x: 54, y: 720 }; + let x = crate::from_str::(r#"{"A": {"x":54,"y":720 } }"#); + assert_eq!(x, Ok((a, 25))); + } + #[test] #[cfg(not(feature = "custom-error-messages"))] fn struct_tuple() { diff --git a/src/ser/mod.rs b/src/ser/mod.rs index c19138b06..a1e2ea148 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -3,12 +3,13 @@ use core::{fmt, fmt::Write}; use serde::ser; +use serde::ser::SerializeStruct as _; use heapless::{consts::*, String, Vec}; use self::map::SerializeMap; use self::seq::SerializeSeq; -use self::struct_::SerializeStruct; +use self::struct_::{SerializeStruct, SerializeStructVariant}; mod map; mod seq; @@ -19,11 +20,10 @@ pub type Result = ::core::result::Result; /// This type represents all possible errors that can occur when serializing JSON data #[derive(Debug)] +#[non_exhaustive] pub enum Error { /// Buffer is full BufferFull, - #[doc(hidden)] - __Extensible, } impl From<()> for Error { @@ -84,7 +84,7 @@ impl<'a> Serializer<'a> { Err(Error::BufferFull) } else { for c in other { - unsafe { self.push_unchecked(c.clone()) }; + unsafe { self.push_unchecked(*c) }; } Ok(()) } @@ -178,7 +178,7 @@ impl<'a, 'b: 'a> ser::Serializer for &'a mut Serializer<'b> { type SerializeTupleVariant = Unreachable; type SerializeMap = SerializeMap<'a, 'b>; type SerializeStruct = SerializeStruct<'a, 'b>; - type SerializeStructVariant = Unreachable; + type SerializeStructVariant = SerializeStructVariant<'a, 'b>; fn serialize_bool(self, v: bool) -> Result { if v { @@ -320,11 +320,11 @@ impl<'a, 'b: 'a> ser::Serializer for &'a mut Serializer<'b> { } fn serialize_unit(self) -> Result { - unreachable!() + self.serialize_none() } fn serialize_unit_struct(self, _name: &'static str) -> Result { - unreachable!() + self.serialize_unit() } fn serialize_unit_variant( @@ -336,28 +336,28 @@ impl<'a, 'b: 'a> ser::Serializer for &'a mut Serializer<'b> { self.serialize_str(variant) } - fn serialize_newtype_struct( - self, - _name: &'static str, - _value: &T, - ) -> Result + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result where T: ser::Serialize, { - unreachable!() + value.serialize(self) } fn serialize_newtype_variant( - self, + mut self, _name: &'static str, _variant_index: u32, - _variant: &'static str, - _value: &T, + variant: &'static str, + value: &T, ) -> Result where T: ser::Serialize, { - unreachable!() + self.push(b'{')?; + let mut s = SerializeStruct::new(&mut self); + s.serialize_field(variant, value)?; + s.end()?; + Ok(()) } fn serialize_seq(self, _len: Option) -> Result { @@ -404,10 +404,14 @@ impl<'a, 'b: 'a> ser::Serializer for &'a mut Serializer<'b> { self, _name: &'static str, _variant_index: u32, - _variant: &'static str, + variant: &'static str, _len: usize, ) -> Result { - unreachable!() + self.extend_from_slice(b"{\"")?; + self.extend_from_slice(variant.as_bytes())?; + self.extend_from_slice(b"\":{")?; + + Ok(SerializeStructVariant::new(self)) } fn collect_str(self, _value: &T) -> Result @@ -510,22 +514,6 @@ impl ser::SerializeMap for Unreachable { } } -impl ser::SerializeStructVariant for Unreachable { - type Ok = (); - type Error = Error; - - fn serialize_field(&mut self, _key: &'static str, _value: &T) -> Result<()> - where - T: ser::Serialize, - { - unreachable!() - } - - fn end(self) -> Result { - unreachable!() - } -} - #[cfg(test)] mod tests { use serde_derive::Serialize; @@ -768,4 +756,43 @@ mod tests { r#"{"a":true,"b":false}"# ); } + + #[test] + fn test_unit() { + let a = (); + assert_eq!(&*crate::to_string::(&a).unwrap(), r#"null"#); + } + + #[test] + fn test_newtype_struct() { + #[derive(Serialize)] + struct A(pub u32); + let a = A(54); + assert_eq!(&*crate::to_string::(&a).unwrap(), r#"54"#); + } + + #[test] + fn test_newtype_variant() { + #[derive(Serialize)] + enum A { + A(u32), + } + let a = A::A(54); + + assert_eq!(&*crate::to_string::(&a).unwrap(), r#"{"A":54}"#); + } + + #[test] + fn test_struct_variant() { + #[derive(Serialize)] + enum A { + A { x: u32, y: u16 }, + } + let a = A::A { x: 54, y: 720 }; + + assert_eq!( + &*crate::to_string::(&a).unwrap(), + r#"{"A":{"x":54,"y":720}}"# + ); + } } diff --git a/src/ser/struct_.rs b/src/ser/struct_.rs index 71558f667..a6c31a8ca 100644 --- a/src/ser/struct_.rs +++ b/src/ser/struct_.rs @@ -41,3 +41,43 @@ impl<'a, 'b: 'a> ser::SerializeStruct for SerializeStruct<'a, 'b> { Ok(()) } } + +pub struct SerializeStructVariant<'a, 'b> { + ser: &'a mut Serializer<'b>, + first: bool, +} + +impl<'a, 'b: 'a> SerializeStructVariant<'a, 'b> { + pub(crate) fn new(ser: &'a mut Serializer<'b>) -> Self { + SerializeStructVariant { ser, first: true } + } +} + +impl<'a, 'b: 'a> ser::SerializeStructVariant for SerializeStructVariant<'a, 'b> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ser::Serialize, + { + // XXX if `value` is `None` we not produce any output for this field + if !self.first { + self.ser.push(b',')?; + } + self.first = false; + + self.ser.push(b'"')?; + self.ser.extend_from_slice(key.as_bytes())?; + self.ser.extend_from_slice(b"\":")?; + + value.serialize(&mut *self.ser)?; + + Ok(()) + } + + fn end(self) -> Result { + self.ser.extend_from_slice(b"}}")?; + Ok(()) + } +}