From 93a7283991aa9c9b1087803c274953d3072c3dc0 Mon Sep 17 00:00:00 2001 From: Devin R Date: Mon, 20 Apr 2020 17:22:08 -0400 Subject: [PATCH] Add support for matrix-style multiple query parameters This adds support for query strings in which a sequence field of a `Deserialize` / `Serialize` struct is (de)serialized as `field=val1&field=val2&field=val3`, instead of the more common `field[]=val1&field[]=val2&field[]=val3` syntax. --- src/de.rs | 72 ++++++++--- src/error.rs | 69 +++++++++++ src/lib.rs | 1 + src/ser/key.rs | 1 + src/ser/mod.rs | 243 +++++++++++++++++--------------------- src/ser/pair.rs | 213 +++++++++++++++++---------------- src/ser/part.rs | 2 +- src/ser/seq.rs | 0 src/ser/value.rs | 17 +-- tests/test_deserialize.rs | 111 +++++++++++++++++ tests/test_serialize.rs | 119 +++++++++++++++++-- 11 files changed, 577 insertions(+), 271 deletions(-) create mode 100644 src/error.rs delete mode 100644 src/ser/seq.rs diff --git a/src/de.rs b/src/de.rs index fe2e0086..7861f5d4 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1,7 +1,12 @@ //! Deserialization support for the `application/x-www-form-urlencoded` format. + use std::{borrow::Cow, io::Read}; -use serde::de::{self, value::MapDeserializer, Error as de_Error, IntoDeserializer}; +use serde::de::{ + self, + value::{MapDeserializer, SeqDeserializer}, + Error as de_Error, IntoDeserializer, +}; use url::form_urlencoded::{parse, Parse as UrlEncodedParse}; #[doc(inline)] @@ -65,9 +70,9 @@ where from_bytes(&buf) } -/// A deserializer for the `application/x-www-form-urlencoded` format. +/// A deserializer for the Matrix query string format. /// -/// * Supported top-level outputs are structs, maps and sequences of pairs, +/// * Supported top-level outputs are structs, maps and sequences, /// with or without a given length. /// /// * Main `deserialize` methods defers to `deserialize_map`. @@ -75,15 +80,12 @@ where /// * Everything else but `deserialize_seq` and `deserialize_seq_fixed_size` /// defers to `deserialize`. pub struct Deserializer<'de> { - inner: MapDeserializer<'de, PartIterator<'de>, Error>, + parser: UrlEncodedParse<'de>, } impl<'de> Deserializer<'de> { - /// Returns a new `Deserializer`. pub fn new(parser: UrlEncodedParse<'de>) -> Self { - Deserializer { - inner: MapDeserializer::new(PartIterator(parser)), - } + Self { parser } } } @@ -101,21 +103,53 @@ impl<'de> de::Deserializer<'de> for Deserializer<'de> { where V: de::Visitor<'de>, { - visitor.visit_map(self.inner) + visitor.visit_map(MapDeserializer::new(PartIterator(self.parser))) } fn deserialize_seq(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_seq(self.inner) + visitor.visit_seq(MapDeserializer::new(PartIterator(self.parser))) + } + + fn deserialize_struct( + self, + _name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + let pairs = self.parser.collect::>(); + let mut map = std::collections::VecDeque::new(); + for field in fields { + let values = pairs + .iter() + .filter(|(f, _)| f == field) + .map(|(_f, v)| v.clone()) + .collect::>(); + map.push_back((*field, values)); + } + let parts = fields + .into_iter() + .map(|f| Part(Cow::Borrowed(f), None)) + .zip(PartIterator(self.parser).into_iter().map(|(_, mut v)| { + if let Some((_, val)) = map.pop_front() { + v.1 = Some(val); + } + v + })) + .map(|(field, value)| (field, value)); + visitor.visit_map(MapDeserializer::new(parts)) } fn deserialize_unit(self, visitor: V) -> Result where V: de::Visitor<'de>, { - self.inner.end()?; + MapDeserializer::new(PartIterator(self.parser)).end()?; visitor.visit_unit() } @@ -140,7 +174,6 @@ impl<'de> de::Deserializer<'de> for Deserializer<'de> { unit_struct newtype_struct tuple_struct - struct identifier tuple enum @@ -154,11 +187,11 @@ impl<'de> Iterator for PartIterator<'de> { type Item = (Part<'de>, Part<'de>); fn next(&mut self) -> Option { - self.0.next().map(|(k, v)| (Part(k), Part(v))) + self.0.next().map(|(k, v)| (Part(k, None), Part(v, None))) } } -struct Part<'de>(Cow<'de, str>); +struct Part<'de>(Cow<'de, str>, Option>>); impl<'de> IntoDeserializer<'de> for Part<'de> { type Deserializer = Self; @@ -226,6 +259,16 @@ impl<'de> de::Deserializer<'de> for Part<'de> { visitor.visit_newtype_struct(self) } + fn deserialize_seq(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + let iter = self.1.ok_or(Error::custom("expected sequence"))?; + visitor.visit_seq(SeqDeserializer::new( + iter.into_iter().map(|v| Part(v, None)), + )) + } + serde::forward_to_deserialize_any! { char str @@ -239,7 +282,6 @@ impl<'de> de::Deserializer<'de> for Part<'de> { identifier tuple ignored_any - seq map } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..32a387cd --- /dev/null +++ b/src/error.rs @@ -0,0 +1,69 @@ +use std::{borrow::Cow, error, fmt, str}; + +use serde::ser; + +pub type Result = std::result::Result; + +/// Errors returned during serializing to `application/x-www-form-urlencoded`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + Custom(Cow<'static, str>), + Utf8(str::Utf8Error), +} + +impl Error { + pub fn done() -> Self { + Error::Custom("this pair has already been serialized".into()) + } + + pub fn not_done() -> Self { + Error::Custom("this pair has not yet been serialized".into()) + } + + pub fn unsupported_pair() -> Self { + Error::Custom("unsupported pair".into()) + } + + pub fn top_level() -> Self { + let msg = "top-level serializer supports only maps and structs"; + Error::Custom(msg.into()) + } + + pub fn no_key() -> Self { + let msg = "tried to serialize a value before serializing key"; + Error::Custom(msg.into()) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::Custom(ref msg) => msg.fmt(f), + Error::Utf8(ref err) => write!(f, "invalid UTF-8: {}", err), + } + } +} + +impl error::Error for Error { + /// The lower-level cause of this error, in the case of a `Utf8` error. + fn cause(&self) -> Option<&dyn error::Error> { + match *self { + Error::Custom(_) => None, + Error::Utf8(ref err) => Some(err), + } + } + + /// The lower-level source of this error, in the case of a `Utf8` error. + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + Error::Custom(_) => None, + Error::Utf8(ref err) => Some(err), + } + } +} + +impl ser::Error for Error { + fn custom(msg: T) -> Self { + Error::Custom(format!("{}", msg).into()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 9eb39786..ee9ef0c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! `x-www-form-urlencoded` meets Serde pub mod de; +pub mod error; pub mod ser; #[doc(inline)] diff --git a/src/ser/key.rs b/src/ser/key.rs index 5a5e1fc7..1906d4b4 100644 --- a/src/ser/key.rs +++ b/src/ser/key.rs @@ -3,6 +3,7 @@ use std::{borrow::Cow, ops::Deref}; use serde::Serialize; use crate::ser::{part::Sink, Error}; + pub enum Key<'key> { Static(&'static str), Dynamic(Cow<'key, str>), diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 1a816d5a..5f4d820f 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -5,10 +5,14 @@ mod pair; mod part; mod value; -use std::{borrow::Cow, error, fmt, str}; +use std::{borrow::Cow, str}; use serde::ser; -use url::form_urlencoded::{Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget}; +use url::form_urlencoded::{ + Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget, +}; + +use crate::error::{Error, Result}; /// Serializes a value into a `application/x-www-form-urlencoded` `String` buffer. /// @@ -24,7 +28,7 @@ use url::form_urlencoded::{Serializer as UrlEncodedSerializer, Target as UrlEnco /// serde_urlencoded::to_string(meal), /// Ok("bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter".to_owned())); /// ``` -pub fn to_string(input: T) -> Result { +pub fn to_string(input: T) -> Result { let mut urlencoder = UrlEncodedSerializer::new("".to_owned()); input.serialize(Serializer::new(&mut urlencoder))?; Ok(urlencoder.finish()) @@ -43,85 +47,50 @@ pub struct Serializer<'input, 'output, Target: 'output + UrlEncodedTarget> { urlencoder: &'output mut UrlEncodedSerializer<'input, Target>, } -impl<'input, 'output, Target: 'output + UrlEncodedTarget> Serializer<'input, 'output, Target> { +impl<'input, 'output, Target: 'output + UrlEncodedTarget> + Serializer<'input, 'output, Target> +{ /// Returns a new `Serializer`. - pub fn new(urlencoder: &'output mut UrlEncodedSerializer<'input, Target>) -> Self { + pub fn new( + urlencoder: &'output mut UrlEncodedSerializer<'input, Target>, + ) -> Self { Serializer { urlencoder: urlencoder, } } } -/// Errors returned during serializing to `application/x-www-form-urlencoded`. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Error { - Custom(Cow<'static, str>), - Utf8(str::Utf8Error), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Custom(ref msg) => msg.fmt(f), - Error::Utf8(ref err) => write!(f, "invalid UTF-8: {}", err), - } - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::Custom(ref msg) => msg, - Error::Utf8(ref err) => error::Error::description(err), - } - } - - /// The lower-level cause of this error, in the case of a `Utf8` error. - fn cause(&self) -> Option<&error::Error> { - match *self { - Error::Custom(_) => None, - Error::Utf8(ref err) => Some(err), - } - } - - /// The lower-level source of this error, in the case of a `Utf8` error. - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - Error::Custom(_) => None, - Error::Utf8(ref err) => Some(err), - } - } -} - -impl ser::Error for Error { - fn custom(msg: T) -> Self { - Error::Custom(format!("{}", msg).into()) - } -} - /// Sequence serializer. pub struct SeqSerializer<'input, 'output, Target: 'output + UrlEncodedTarget> { urlencoder: &'output mut UrlEncodedSerializer<'input, Target>, + key: Option>, + count: usize, } /// Tuple serializer. /// /// Mostly used for arrays. -pub struct TupleSerializer<'input, 'output, Target: 'output + UrlEncodedTarget> { +pub struct TupleSerializer<'input, 'output, Target: 'output + UrlEncodedTarget> +{ urlencoder: &'output mut UrlEncodedSerializer<'input, Target>, } /// Tuple struct serializer. /// /// Never instantiated, tuple structs are not supported. -pub struct TupleStructSerializer<'input, 'output, T: 'output + UrlEncodedTarget> { +pub struct TupleStructSerializer<'input, 'output, T: 'output + UrlEncodedTarget> +{ inner: ser::Impossible<&'output mut UrlEncodedSerializer<'input, T>, Error>, } /// Tuple variant serializer. /// /// Never instantiated, tuple variants are not supported. -pub struct TupleVariantSerializer<'input, 'output, T: 'output + UrlEncodedTarget> { +pub struct TupleVariantSerializer< + 'input, + 'output, + T: 'output + UrlEncodedTarget, +> { inner: ser::Impossible<&'output mut UrlEncodedSerializer<'input, T>, Error>, } @@ -132,18 +101,24 @@ pub struct MapSerializer<'input, 'output, Target: 'output + UrlEncodedTarget> { } /// Struct serializer. -pub struct StructSerializer<'input, 'output, Target: 'output + UrlEncodedTarget> { +pub struct StructSerializer<'input, 'output, Target: 'output + UrlEncodedTarget> +{ urlencoder: &'output mut UrlEncodedSerializer<'input, Target>, } /// Struct variant serializer. /// /// Never instantiated, struct variants are not supported. -pub struct StructVariantSerializer<'input, 'output, T: 'output + UrlEncodedTarget> { +pub struct StructVariantSerializer< + 'input, + 'output, + T: 'output + UrlEncodedTarget, +> { inner: ser::Impossible<&'output mut UrlEncodedSerializer<'input, T>, Error>, } -impl<'input, 'output, Target> ser::Serializer for Serializer<'input, 'output, Target> +impl<'input, 'output, Target> ser::Serializer + for Serializer<'input, 'output, Target> where Target: 'output + UrlEncodedTarget, { @@ -152,91 +127,90 @@ where type SerializeSeq = SeqSerializer<'input, 'output, Target>; type SerializeTuple = TupleSerializer<'input, 'output, Target>; type SerializeTupleStruct = TupleStructSerializer<'input, 'output, Target>; - type SerializeTupleVariant = TupleVariantSerializer<'input, 'output, Target>; + type SerializeTupleVariant = + TupleVariantSerializer<'input, 'output, Target>; type SerializeMap = MapSerializer<'input, 'output, Target>; type SerializeStruct = StructSerializer<'input, 'output, Target>; - type SerializeStructVariant = StructVariantSerializer<'input, 'output, Target>; + type SerializeStructVariant = + StructVariantSerializer<'input, 'output, Target>; /// Returns an error. - fn serialize_bool(self, _v: bool) -> Result { + fn serialize_bool(self, _v: bool) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_i8(self, _v: i8) -> Result { + fn serialize_i8(self, _v: i8) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_i16(self, _v: i16) -> Result { + fn serialize_i16(self, _v: i16) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_i32(self, _v: i32) -> Result { + fn serialize_i32(self, _v: i32) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_i64(self, _v: i64) -> Result { + fn serialize_i64(self, _v: i64) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_u8(self, _v: u8) -> Result { + fn serialize_u8(self, _v: u8) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_u16(self, _v: u16) -> Result { + fn serialize_u16(self, _v: u16) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_u32(self, _v: u32) -> Result { + fn serialize_u32(self, _v: u32) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_u64(self, _v: u64) -> Result { + fn serialize_u64(self, _v: u64) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_f32(self, _v: f32) -> Result { + fn serialize_f32(self, _v: f32) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_f64(self, _v: f64) -> Result { + fn serialize_f64(self, _v: f64) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_char(self, _v: char) -> Result { + fn serialize_char(self, _v: char) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_str(self, _value: &str) -> Result { + fn serialize_str(self, _value: &str) -> Result { Err(Error::top_level()) } /// Returns an error. - fn serialize_bytes(self, _value: &[u8]) -> Result { + fn serialize_bytes(self, _value: &[u8]) -> Result { Err(Error::top_level()) } /// Returns `Ok`. - fn serialize_unit(self) -> Result { + fn serialize_unit(self) -> Result { Ok(self.urlencoder) } /// Returns `Ok`. - fn serialize_unit_struct( - self, - _name: &'static str, - ) -> Result { + fn serialize_unit_struct(self, _name: &'static str) -> Result { Ok(self.urlencoder) } @@ -246,7 +220,7 @@ where _name: &'static str, _variant_index: u32, _variant: &'static str, - ) -> Result { + ) -> Result { Err(Error::top_level()) } @@ -255,7 +229,7 @@ where self, _name: &'static str, value: &T, - ) -> Result { + ) -> Result { value.serialize(self) } @@ -266,12 +240,12 @@ where _variant_index: u32, _variant: &'static str, _value: &T, - ) -> Result { + ) -> Result { Err(Error::top_level()) } /// Returns `Ok`. - fn serialize_none(self) -> Result { + fn serialize_none(self) -> Result { Ok(self.urlencoder) } @@ -279,25 +253,21 @@ where fn serialize_some( self, value: &T, - ) -> Result { + ) -> Result { value.serialize(self) } /// Serialize a sequence, given length (if any) is ignored. - fn serialize_seq( - self, - _len: Option, - ) -> Result { + fn serialize_seq(self, _len: Option) -> Result { Ok(SeqSerializer { urlencoder: self.urlencoder, + key: None, + count: 0, }) } /// Returns an error. - fn serialize_tuple( - self, - _len: usize, - ) -> Result { + fn serialize_tuple(self, _len: usize) -> Result { Ok(TupleSerializer { urlencoder: self.urlencoder, }) @@ -308,7 +278,7 @@ where self, _name: &'static str, _len: usize, - ) -> Result { + ) -> Result { Err(Error::top_level()) } @@ -319,15 +289,12 @@ where _variant_index: u32, _variant: &'static str, _len: usize, - ) -> Result { + ) -> Result { Err(Error::top_level()) } /// Serializes a map, given length is ignored. - fn serialize_map( - self, - _len: Option, - ) -> Result { + fn serialize_map(self, _len: Option) -> Result { Ok(MapSerializer { urlencoder: self.urlencoder, key: None, @@ -339,7 +306,7 @@ where self, _name: &'static str, _len: usize, - ) -> Result { + ) -> Result { Ok(StructSerializer { urlencoder: self.urlencoder, }) @@ -352,12 +319,13 @@ where _variant_index: u32, _variant: &'static str, _len: usize, - ) -> Result { + ) -> Result { Err(Error::top_level()) } } -impl<'input, 'output, Target> ser::SerializeSeq for SeqSerializer<'input, 'output, Target> +impl<'input, 'output, Target> ser::SerializeSeq + for SeqSerializer<'input, 'output, Target> where Target: 'output + UrlEncodedTarget, { @@ -367,16 +335,21 @@ where fn serialize_element( &mut self, value: &T, - ) -> Result<(), Error> { - value.serialize(pair::PairSerializer::new(self.urlencoder)) + ) -> Result<()> { + value.serialize(pair::PairSerializer::new( + self.urlencoder, + self.key.as_ref(), + &mut self.count, + )) } - fn end(self) -> Result { + fn end(self) -> Result { Ok(self.urlencoder) } } -impl<'input, 'output, Target> ser::SerializeTuple for TupleSerializer<'input, 'output, Target> +impl<'input, 'output, Target> ser::SerializeTuple + for TupleSerializer<'input, 'output, Target> where Target: 'output + UrlEncodedTarget, { @@ -386,11 +359,15 @@ where fn serialize_element( &mut self, value: &T, - ) -> Result<(), Error> { - value.serialize(pair::PairSerializer::new(self.urlencoder)) + ) -> Result<()> { + value.serialize(pair::PairSerializer::new( + self.urlencoder, + None, + &mut 0, + )) } - fn end(self) -> Result { + fn end(self) -> Result { Ok(self.urlencoder) } } @@ -406,11 +383,11 @@ where fn serialize_field( &mut self, value: &T, - ) -> Result<(), Error> { + ) -> Result<()> { self.inner.serialize_field(value) } - fn end(self) -> Result { + fn end(self) -> Result { self.inner.end() } } @@ -426,16 +403,17 @@ where fn serialize_field( &mut self, value: &T, - ) -> Result<(), Error> { + ) -> Result<()> { self.inner.serialize_field(value) } - fn end(self) -> Result { + fn end(self) -> Result { self.inner.end() } } -impl<'input, 'output, Target> ser::SerializeMap for MapSerializer<'input, 'output, Target> +impl<'input, 'output, Target> ser::SerializeMap + for MapSerializer<'input, 'output, Target> where Target: 'output + UrlEncodedTarget, { @@ -449,7 +427,7 @@ where &mut self, key: &K, value: &V, - ) -> Result<(), Error> { + ) -> Result<()> { let key_sink = key::KeySink::new(|key| { let value_sink = value::ValueSink::new(self.urlencoder, &key); value.serialize(part::PartSerializer::new(value_sink))?; @@ -463,7 +441,7 @@ where fn serialize_key( &mut self, key: &T, - ) -> Result<(), Error> { + ) -> Result<()> { let key_sink = key::KeySink::new(|key| Ok(key.into())); let key_serializer = part::PartSerializer::new(key_sink); self.key = Some(key.serialize(key_serializer)?); @@ -473,7 +451,7 @@ where fn serialize_value( &mut self, value: &T, - ) -> Result<(), Error> { + ) -> Result<()> { { let key = self.key.as_ref().ok_or_else(|| Error::no_key())?; let value_sink = value::ValueSink::new(self.urlencoder, &key); @@ -483,12 +461,13 @@ where Ok(()) } - fn end(self) -> Result { + fn end(self) -> Result { Ok(self.urlencoder) } } -impl<'input, 'output, Target> ser::SerializeStruct for StructSerializer<'input, 'output, Target> +impl<'input, 'output, Target> ser::SerializeStruct + for StructSerializer<'input, 'output, Target> where Target: 'output + UrlEncodedTarget, { @@ -499,12 +478,16 @@ where &mut self, key: &'static str, value: &T, - ) -> Result<(), Error> { - let value_sink = value::ValueSink::new(self.urlencoder, key); - value.serialize(part::PartSerializer::new(value_sink)) + ) -> Result<()> { + let key = Cow::Borrowed(key); + let mut count = 0; + let value_sink = + pair::PairSerializer::new(self.urlencoder, Some(&key), &mut count); + value.serialize(value_sink)?; + Ok(()) } - fn end(self) -> Result { + fn end(self) -> Result { Ok(self.urlencoder) } } @@ -521,23 +504,11 @@ where &mut self, key: &'static str, value: &T, - ) -> Result<(), Error> { + ) -> Result<()> { self.inner.serialize_field(key, value) } - fn end(self) -> Result { + fn end(self) -> Result { self.inner.end() } } - -impl Error { - fn top_level() -> Self { - let msg = "top-level serializer supports only maps and structs"; - Error::Custom(msg.into()) - } - - fn no_key() -> Self { - let msg = "tried to serialize a value before serializing key"; - Error::Custom(msg.into()) - } -} diff --git a/src/ser/pair.rs b/src/ser/pair.rs index 95c06e90..82ba3481 100644 --- a/src/ser/pair.rs +++ b/src/ser/pair.rs @@ -1,34 +1,67 @@ -use serde::ser; use std::borrow::Cow; use std::mem; -use url::form_urlencoded::Serializer as UrlEncodedSerializer; -use url::form_urlencoded::Target as UrlEncodedTarget; -use crate::ser::{Error, part::PartSerializer, key::KeySink, value::ValueSink}; +use serde::ser; + +use url::form_urlencoded::{ + Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget, +}; + +use crate::{ + error::{Error, Result}, + ser::{key::KeySink, part::PartSerializer, value::ValueSink}, +}; + +macro_rules! serialize_pair { + ($($ty:ty => $name:ident,)*) => { + $( + fn $name(self, value: $ty) -> Result<()> { + let key = if let Some(key) = self.key { + key.clone() + } else { + return Err(Error::no_key()); + }; + let value_sink = ValueSink::new(self.urlencoder, &key); + let value_serializer = PartSerializer::new(value_sink); + value_serializer.$name(value) + } + )* + }; +} + pub struct PairSerializer<'input, 'target, Target: 'target + UrlEncodedTarget> { urlencoder: &'target mut UrlEncodedSerializer<'input, Target>, state: PairState, + key: Option<&'target Cow<'target, str>>, + count: &'target mut usize, } impl<'input, 'target, Target> PairSerializer<'input, 'target, Target> where Target: 'target + UrlEncodedTarget, { - pub fn new(urlencoder: &'target mut UrlEncodedSerializer<'input, Target>) -> Self { + pub fn new( + urlencoder: &'target mut UrlEncodedSerializer<'input, Target>, + key: Option<&'target Cow<'target, str>>, + count: &'target mut usize, + ) -> Self { PairSerializer { - urlencoder: urlencoder, + urlencoder, state: PairState::WaitingForKey, + key, + count, } } } -impl<'input, 'target, Target> ser::Serializer for PairSerializer<'input, 'target, Target> +impl<'input, 'target, Target> ser::Serializer + for PairSerializer<'input, 'target, Target> where Target: 'target + UrlEncodedTarget, { type Ok = (); type Error = Error; - type SerializeSeq = ser::Impossible<(), Error>; + type SerializeSeq = Self; type SerializeTuple = Self; type SerializeTupleStruct = ser::Impossible<(), Error>; type SerializeTupleVariant = ser::Impossible<(), Error>; @@ -36,67 +69,31 @@ where type SerializeStruct = ser::Impossible<(), Error>; type SerializeStructVariant = ser::Impossible<(), Error>; - fn serialize_bool(self, _v: bool) -> Result<(), Error> { + serialize_pair! { + bool => serialize_bool, + u8 => serialize_u8, + u16 => serialize_u16, + u32 => serialize_u32, + u64 => serialize_u64, + i8 => serialize_i8, + i16 => serialize_i16, + i32 => serialize_i32, + i64 => serialize_i64, + f32 => serialize_f32, + f64 => serialize_f64, + char => serialize_char, + &str => serialize_str, + } + + fn serialize_bytes(self, _value: &[u8]) -> Result<()> { Err(Error::unsupported_pair()) } - fn serialize_i8(self, _v: i8) -> Result<(), Error> { + fn serialize_unit(self) -> Result<()> { Err(Error::unsupported_pair()) } - fn serialize_i16(self, _v: i16) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_i32(self, _v: i32) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_i64(self, _v: i64) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_u8(self, _v: u8) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_u16(self, _v: u16) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_u32(self, _v: u32) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_u64(self, _v: u64) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_f32(self, _v: f32) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_f64(self, _v: f64) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_char(self, _v: char) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_str(self, _value: &str) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_bytes(self, _value: &[u8]) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_unit(self) -> Result<(), Error> { - Err(Error::unsupported_pair()) - } - - fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> { + fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { Err(Error::unsupported_pair()) } @@ -104,16 +101,25 @@ where self, _name: &'static str, _variant_index: u32, - _variant: &'static str, - ) -> Result<(), Error> { - Err(Error::unsupported_pair()) + variant: &'static str, + ) -> Result<()> { + let key = if let Some(key) = self.key { + key.clone() + } else { + let key = Cow::Owned(self.count.to_string()); + *self.count += 1; + key + }; + let value_sink = ValueSink::new(self.urlencoder, &key); + let value_serializer = PartSerializer::new(value_sink); + value_serializer.serialize_str(variant) } fn serialize_newtype_struct( self, _name: &'static str, value: &T, - ) -> Result<(), Error> { + ) -> Result<()> { value.serialize(self) } @@ -123,29 +129,26 @@ where _variant_index: u32, _variant: &'static str, _value: &T, - ) -> Result<(), Error> { + ) -> Result<()> { Err(Error::unsupported_pair()) } - fn serialize_none(self) -> Result<(), Error> { + fn serialize_none(self) -> Result<()> { Ok(()) } fn serialize_some( self, value: &T, - ) -> Result<(), Error> { + ) -> Result<()> { value.serialize(self) } - fn serialize_seq( - self, - _len: Option, - ) -> Result { - Err(Error::unsupported_pair()) + fn serialize_seq(self, _len: Option) -> Result { + Ok(self) } - fn serialize_tuple(self, len: usize) -> Result { + fn serialize_tuple(self, len: usize) -> Result { if len == 2 { Ok(self) } else { @@ -157,7 +160,7 @@ where self, _name: &'static str, _len: usize, - ) -> Result { + ) -> Result { Err(Error::unsupported_pair()) } @@ -167,14 +170,11 @@ where _variant_index: u32, _variant: &'static str, _len: usize, - ) -> Result { + ) -> Result { Err(Error::unsupported_pair()) } - fn serialize_map( - self, - _len: Option, - ) -> Result { + fn serialize_map(self, _len: Option) -> Result { Err(Error::unsupported_pair()) } @@ -182,7 +182,7 @@ where self, _name: &'static str, _len: usize, - ) -> Result { + ) -> Result { Err(Error::unsupported_pair()) } @@ -192,12 +192,13 @@ where _variant_index: u32, _variant: &'static str, _len: usize, - ) -> Result { + ) -> Result { Err(Error::unsupported_pair()) } } -impl<'input, 'target, Target> ser::SerializeTuple for PairSerializer<'input, 'target, Target> +impl<'input, 'target, Target> ser::SerializeSeq + for PairSerializer<'input, 'target, Target> where Target: 'target + UrlEncodedTarget, { @@ -207,7 +208,31 @@ where fn serialize_element( &mut self, value: &T, - ) -> Result<(), Error> { + ) -> Result<()> { + value.serialize(PairSerializer::new( + self.urlencoder, + self.key, + &mut self.count, + )) + } + + fn end(self) -> Result { + Ok(()) + } +} + +impl<'input, 'target, Target> ser::SerializeTuple + for PairSerializer<'input, 'target, Target> +where + Target: 'target + UrlEncodedTarget, +{ + type Ok = (); + type Error = Error; + + fn serialize_element( + &mut self, + value: &T, + ) -> Result<()> { match mem::replace(&mut self.state, PairState::Done) { PairState::WaitingForKey => { let key_sink = KeySink::new(|key| Ok(key.into())); @@ -234,7 +259,7 @@ where } } - fn end(self) -> Result<(), Error> { + fn end(self) -> Result<()> { if let PairState::Done = self.state { Ok(()) } else { @@ -248,17 +273,3 @@ enum PairState { WaitingForValue { key: Cow<'static, str> }, Done, } - -impl Error { - fn done() -> Self { - Error::Custom("this pair has already been serialized".into()) - } - - fn not_done() -> Self { - Error::Custom("this pair has not yet been serialized".into()) - } - - fn unsupported_pair() -> Self { - Error::Custom("unsupported pair".into()) - } -} diff --git a/src/ser/part.rs b/src/ser/part.rs index 5e0d64c9..514d4a27 100644 --- a/src/ser/part.rs +++ b/src/ser/part.rs @@ -4,7 +4,7 @@ use dtoa; use itoa; use serde::ser; -use crate::ser::Error; +use crate::error::Error; pub struct PartSerializer { sink: S, } diff --git a/src/ser/seq.rs b/src/ser/seq.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ser/value.rs b/src/ser/value.rs index 1ac02df2..a34238ad 100644 --- a/src/ser/value.rs +++ b/src/ser/value.rs @@ -1,9 +1,14 @@ use std::str; use serde::Serialize; -use url::form_urlencoded::{Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget}; +use url::form_urlencoded::{ + Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget, +}; -use crate::ser::{part::{PartSerializer, Sink}, Error}; +use crate::{ + error::Error, + ser::part::{PartSerializer, Sink}, +}; pub struct ValueSink<'input, 'key, 'target, Target> where @@ -21,14 +26,12 @@ where urlencoder: &'target mut UrlEncodedSerializer<'input, Target>, key: &'key str, ) -> Self { - ValueSink { - urlencoder: urlencoder, - key: key, - } + ValueSink { urlencoder, key } } } -impl<'input, 'key, 'target, Target> Sink for ValueSink<'input, 'key, 'target, Target> +impl<'input, 'key, 'target, Target> Sink + for ValueSink<'input, 'key, 'target, Target> where Target: 'target + UrlEncodedTarget, { diff --git a/tests/test_deserialize.rs b/tests/test_deserialize.rs index c40a03cc..082468a5 100644 --- a/tests/test_deserialize.rs +++ b/tests/test_deserialize.rs @@ -86,3 +86,114 @@ fn deserialize_unit_enum() { fn deserialize_unit_type() { assert_eq!(serde_urlencoded::from_str(""), Ok(())); } + +#[derive(Debug, Deserialize, PartialEq)] +struct Wrapper { + item: T, +} + +#[derive(Debug, PartialEq, Deserialize)] +struct NewStruct { + list: Vec, +} + +#[derive(Debug, PartialEq, Deserialize)] +struct Struct { + list: Vec>, +} + +#[derive(Debug, PartialEq, Deserialize)] +struct NumList { + list: Vec, +} + +#[derive(Debug, PartialEq, Deserialize)] +struct ListStruct { + list: Vec>, +} + +#[derive(Debug, PartialEq, Deserialize)] +struct MapStruct { + a: usize, + b: String, + c: Option, +} + +#[test] +fn deserialize_mapstruct() { + let de = MapStruct { + a: 10, + b: "Hello".into(), + c: None, + }; + assert_eq!( + de, + serde_urlencoded::from_str::("a=10&b=Hello").unwrap() + ); +} + +#[test] +fn deserialize_newstruct() { + let de = NewStruct { + list: vec!["hello".into(), "world".into()], + }; + assert_eq!( + de, + serde_urlencoded::from_str::("list=hello&list=world") + .unwrap() + ); +} + +#[test] +fn deserialize_numlist() { + let de = NumList { + list: vec![1, 2, 3, 4], + }; + assert_eq!( + de, + serde_urlencoded::from_str::("list=1&list=2&list=3&list=4") + .unwrap() + ); +} + +#[test] +fn deserialize_vec_bool() { + assert_eq!( + Wrapper { + item: vec![true, false, false] + }, + serde_urlencoded::from_str::>( + "item=true&item=false&item=false" + ) + .unwrap() + ); +} + +#[test] +fn deserialize_vec_string() { + assert_eq!( + Wrapper { + item: vec![ + "hello".to_string(), + "matrix".to_string(), + "hello".to_string() + ], + }, + serde_urlencoded::from_str::>( + "item=hello&item=matrix&item=hello" + ) + .unwrap() + ); +} + +#[test] +fn deserialize_struct_unit_enum() { + let result = Wrapper { + item: vec![X::A, X::B, X::C], + }; + + assert_eq!( + serde_urlencoded::from_str("item=A&item=B&item=C"), + Ok(result) + ); +} diff --git a/tests/test_serialize.rs b/tests/test_serialize.rs index 630b6d9f..7a0f6571 100644 --- a/tests/test_serialize.rs +++ b/tests/test_serialize.rs @@ -3,17 +3,6 @@ use serde::Serialize; #[derive(Serialize)] struct NewType(T); -#[derive(Serialize)] -struct NewStruct { - list: Vec, -} - -#[test] -fn serialize_newstruct() { - let s = NewStruct { list: vec![ "hello".into() ] }; - println!("{:?}", serde_urlencoded::to_string(s)); -} - #[test] fn serialize_newtype_i32() { let params = &[("field", Some(NewType(11)))]; @@ -95,3 +84,111 @@ fn serialize_unit_struct() { fn serialize_unit_type() { assert_eq!(serde_urlencoded::to_string(()), Ok("".to_owned())); } + +#[derive(Serialize)] +struct Wrapper { + item: T, +} + +#[derive(Serialize)] +struct NewStruct { + list: Vec, +} + +#[derive(Serialize)] +struct Struct { + list: Vec>, +} + +#[derive(Serialize)] +struct ListStruct { + list: Vec>, +} + +#[test] +fn serialize_newstruct() { + let s = NewStruct { + list: vec!["hello".into(), "world".into()], + }; + assert_eq!( + "list=hello&list=world".to_string(), + serde_urlencoded::to_string(s).unwrap() + ); +} + +#[test] +fn serialize_vec_bool() { + let params = Wrapper { + item: vec![true, false, false], + }; + assert_eq!( + serde_urlencoded::to_string(params).unwrap(), + "item=true&item=false&item=false".to_owned() + ); +} + +#[test] +fn serialize_vec_num() { + let params = Wrapper { + item: vec![0, 1, 2], + }; + assert_eq!( + serde_urlencoded::to_string(params).unwrap(), + "item=0&item=1&item=2".to_owned() + ); +} + +#[test] +fn serialize_vec_str() { + let params = Wrapper { + item: vec!["hello", "world", "hello"], + }; + assert_eq!( + serde_urlencoded::to_string(params).unwrap(), + "item=hello&item=world&item=hello".to_owned() + ); +} + +#[test] +fn serialize_struct_opt() { + let s = Struct { + list: vec![Some("hello".into()), Some("world".into())], + }; + assert_eq!( + "list=hello&list=world".to_string(), + serde_urlencoded::to_string(s).unwrap() + ); +} + +#[test] +fn serialize_struct_newtype() { + let s = ListStruct { + list: vec![NewType(0), NewType(1)], + }; + assert_eq!( + "list=0&list=1".to_string(), + serde_urlencoded::to_string(s).unwrap() + ); +} + +#[test] +fn serialize_struct_unit_enum() { + let params = Wrapper { + item: vec![X::A, X::B, X::C], + }; + assert_eq!( + serde_urlencoded::to_string(params), + Ok("item=A&item=B&item=C".to_owned()) + ); +} + +#[test] +fn serialize_map() { + let mut s = std::collections::BTreeMap::new(); + s.insert("hello", "world"); + s.insert("seri", "alize"); + s.insert("matrix", "ruma"); + + let encoded = serde_urlencoded::to_string(s).unwrap(); + assert_eq!("hello=world&matrix=ruma&seri=alize", encoded); +}