diff --git a/src/urlencoded/de.rs b/src/urlencoded/de.rs index ebdaf315..003ccf4a 100644 --- a/src/urlencoded/de.rs +++ b/src/urlencoded/de.rs @@ -233,7 +233,11 @@ impl<'de> de::Deserializer<'de> for Part<'de> { where V: de::Visitor<'de>, { - visitor.visit_some(self) + if self.0 == "null" { + visitor.visit_none() + } else { + visitor.visit_some(self) + } } fn deserialize_enum( @@ -269,6 +273,29 @@ impl<'de> de::Deserializer<'de> for Part<'de> { )) } + fn deserialize_struct( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + let pairs = self + .1 + .ok_or_else(|| Error::custom("percent decoding may have failed"))?; + + let raw_json = pairs + .get(0) + .ok_or_else(|| Error::custom("no value found for nested struct"))?; + + let mut de = + serde_json::de::Deserializer::from_reader(raw_json.as_bytes()); + de.deserialize_struct(name, fields, visitor) + .map_err(|e| Error::custom(e.to_string())) + } + serde::forward_to_deserialize_any! { char str @@ -278,7 +305,6 @@ impl<'de> de::Deserializer<'de> for Part<'de> { byte_buf unit_struct tuple_struct - struct identifier tuple ignored_any diff --git a/src/urlencoded/ser/pair.rs b/src/urlencoded/ser/pair.rs index a8bc7c06..ba2e521c 100644 --- a/src/urlencoded/ser/pair.rs +++ b/src/urlencoded/ser/pair.rs @@ -32,6 +32,8 @@ pub struct PairSerializer<'input, 'target, Target: 'target + UrlEncodedTarget> { state: PairState, key: Option<&'target Cow<'target, str>>, count: &'target mut usize, + len: usize, + json_buf: String, } impl<'input, 'target, Target> PairSerializer<'input, 'target, Target> @@ -48,6 +50,8 @@ where state: PairState::WaitingForKey, key, count, + len: 0, + json_buf: String::new(), } } } @@ -64,7 +68,7 @@ where type SerializeTupleStruct = ser::Impossible<(), Error>; type SerializeTupleVariant = ser::Impossible<(), Error>; type SerializeMap = ser::Impossible<(), Error>; - type SerializeStruct = ser::Impossible<(), Error>; + type SerializeStruct = Self; type SerializeStructVariant = ser::Impossible<(), Error>; serialize_pair! { @@ -177,11 +181,12 @@ where } fn serialize_struct( - self, + mut self, _name: &'static str, - _len: usize, + len: usize, ) -> Result { - Err(Error::unsupported_pair()) + self.len = len; + Ok(self) } fn serialize_struct_variant( @@ -195,6 +200,51 @@ where } } +impl<'input, 'target, Target> ser::SerializeStruct + for PairSerializer<'input, 'target, Target> +where + Target: 'target + UrlEncodedTarget, +{ + type Ok = (); + type Error = Error; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<()> + where + T: ser::Serialize, + { + *self.count += 1; + if self.json_buf.is_empty() { + self.json_buf.push_str("{"); + } + let json_blob = serde_json::to_string(value) + .map_err(|e| Error::Custom(e.to_string().into()))?; + + if *self.count == self.len { + self.json_buf + .push_str(&format!("\"{}\":{}", key, json_blob)); + } else { + self.json_buf + .push_str(&format!("\"{}\":{},", key, json_blob)); + } + Ok(()) + } + + fn end(mut self) -> Result { + use serde::ser::Serialize; + + self.json_buf.push_str("}"); + self.json_buf.serialize(PairSerializer::new( + self.urlencoder, + self.key, + &mut self.count, + )) + } +} + impl<'input, 'target, Target> ser::SerializeSeq for PairSerializer<'input, 'target, Target> where diff --git a/tests/url_deserialize.rs b/tests/url_deserialize.rs index 291b6bf6..4728bfc3 100644 --- a/tests/url_deserialize.rs +++ b/tests/url_deserialize.rs @@ -1,5 +1,6 @@ use ruma_serde::urlencoded; use serde::Deserialize; +use url::form_urlencoded::Serializer as Encoder; #[derive(Deserialize, Debug, PartialEq)] struct NewType(T); @@ -183,3 +184,81 @@ fn deserialize_struct_unit_enum() { assert_eq!(urlencoded::from_str("item=A&item=B&item=C"), Ok(result)); } + +#[derive(Debug, Deserialize, PartialEq)] +struct Nested { + item: T, +} + +#[derive(Debug, Deserialize, PartialEq)] +struct Inner { + c: String, + a: usize, + b: String, +} + +#[derive(Debug, Deserialize, PartialEq)] +struct InnerList { + list: Vec, +} + +#[test] +fn deserialize_nested() { + let mut encoder = Encoder::new(String::new()); + + let nested = Nested { + item: Inner { + c: "hello".into(), + a: 10, + b: "bye".into(), + }, + }; + assert_eq!( + nested, + urlencoded::from_str::>( + &encoder + .append_pair("item", r#"{"c":"hello","a":10,"b":"bye"}"#) + .finish(), + ) + .unwrap() + ); +} + +#[test] +fn deserialize_nested_list() { + let mut encoder = Encoder::new(String::new()); + + let nested = Nested { + item: InnerList { + list: vec![1, 2, 3], + }, + }; + + assert_eq!( + nested, + urlencoded::from_str::>>( + &encoder.append_pair("item", r#"{"list":[1,2,3]}"#).finish(), + ) + .unwrap() + ); +} + +#[test] +fn deserialize_nested_list_option() { + let mut encoder = Encoder::new(String::new()); + + let nested = Nested { + item: InnerList { + list: vec![Some(1), Some(2), None], + }, + }; + assert_eq!( + nested, + urlencoded::from_str::>>>( + &encoder + .append_pair("item", r#"{"list":[1,2,null]}"#) + .finish(), + ) + .unwrap() + ); +} diff --git a/tests/url_serialize.rs b/tests/url_serialize.rs index ead5dd93..b9381420 100644 --- a/tests/url_serialize.rs +++ b/tests/url_serialize.rs @@ -1,5 +1,6 @@ use ruma_serde::urlencoded; use serde::Serialize; +use url::form_urlencoded::Serializer as Encoder; #[derive(Serialize)] struct NewType(T); @@ -190,3 +191,71 @@ fn serialize_map() { let encoded = urlencoded::to_string(s).unwrap(); assert_eq!("hello=world&matrix=ruma&seri=alize", encoded); } + +#[derive(Serialize)] +struct Nested { + item: T, +} + +#[derive(Serialize)] +struct Inner { + c: String, + a: usize, + b: String, +} + +#[derive(Debug, Serialize, PartialEq)] +struct InnerList { + list: Vec, +} + +#[test] +fn serialize_nested() { + let mut encoder = Encoder::new(String::new()); + + let s = Nested { + item: Inner { + c: "hello".into(), + a: 10, + b: "bye".into(), + }, + }; + assert_eq!( + encoder + .append_pair("item", r#"{"c":"hello","a":10,"b":"bye"}"#) + .finish(), + urlencoded::to_string(s).unwrap() + ); +} + +#[test] +fn serialize_nested_list() { + let mut encoder = Encoder::new(String::new()); + + let s = Nested { + item: InnerList { + list: vec![1, 2, 3], + }, + }; + assert_eq!( + encoder.append_pair("item", r#"{"list":[1,2,3]}"#).finish(), + urlencoded::to_string(s).unwrap() + ); +} + +#[test] +fn serialize_nested_list_option() { + let mut encoder = Encoder::new(String::new()); + + let s = Nested { + item: InnerList { + list: vec![Some(1), Some(2), None], + }, + }; + assert_eq!( + encoder + .append_pair("item", r#"{"list":[1,2,null]}"#) + .finish(), + urlencoded::to_string(s).unwrap() + ); +}