diff --git a/Cargo.toml b/Cargo.toml index a788d23e..c356ece7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_urlencoded" -version = "0.4.2" +version = "0.4.3" authors = ["Anthony Ramine "] license = "MIT/Apache-2.0" repository = "https://github.com/nox/serde_urlencoded" diff --git a/src/de.rs b/src/de.rs index 623bb491..10015487 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1,11 +1,14 @@ //! Deserialization support for the `application/x-www-form-urlencoded` format. use serde::de; +use serde::de::value::{MapDeserializer, ValueDeserializer as SerdeValueDeserializer}; #[doc(inline)] pub use serde::de::value::Error; -use serde::de::value::MapDeserializer; +use std::borrow::Cow; use std::io::Read; +use std::iter::Map; +use std::marker::PhantomData; use url::form_urlencoded::Parse as UrlEncodedParse; use url::form_urlencoded::parse; @@ -50,7 +53,8 @@ pub fn from_str(input: &str) -> Result { /// Convenience function that reads all bytes from `reader` and deserializes /// them with `from_bytes`. pub fn from_reader(mut reader: R) -> Result - where T: de::Deserialize, R: Read + where T: de::Deserialize, + R: Read, { let mut buf = vec![]; reader.read_to_end(&mut buf) @@ -70,13 +74,18 @@ pub fn from_reader(mut reader: R) -> Result /// * Everything else but `deserialize_seq` and `deserialize_seq_fixed_size` /// defers to `deserialize`. pub struct Deserializer<'a> { - inner: MapDeserializer, Error>, + inner: MapDeserializer, + fn((Cow<'a, str>, Cow<'a, str>)) + -> (Cow<'a, str>, Value<'a>)>, + Error>, } impl<'a> Deserializer<'a> { /// Returns a new `Deserializer`. pub fn new(parser: UrlEncodedParse<'a>) -> Self { - Deserializer { inner: MapDeserializer::new(parser) } + Deserializer { + inner: MapDeserializer::new(parser.map(Value::wrap_pair)), + } } } @@ -139,3 +148,78 @@ impl<'a> de::Deserializer for Deserializer<'a> { ignored_any } } + +struct Value<'a>(Cow<'a, str>); + +impl<'a> Value<'a> { + fn wrap_pair((k, v): (Cow<'a, str>, Cow<'a, str>)) -> (Cow<'a, str>, Self) { + (k, Value(v)) + } +} + +impl<'a, E> SerdeValueDeserializer for Value<'a> + where E: de::Error, +{ + type Deserializer = ValueDeserializer<'a, E>; + + fn into_deserializer(self) -> Self::Deserializer { + ValueDeserializer { + value: self.0, + marker: PhantomData, + } + } +} + +struct ValueDeserializer<'a, E> { + value: Cow<'a, str>, + marker: PhantomData, +} + +impl<'a, E> de::Deserializer for ValueDeserializer<'a, E> + where E: de::Error, +{ + type Error = E; + + fn deserialize(self, visitor: V) -> Result + where V: de::Visitor, + { + self.value.into_deserializer().deserialize(visitor) + } + + fn deserialize_option(self, visitor: V) -> Result + where V: de::Visitor, + { + visitor.visit_some(self.value.into_deserializer()) + } + + forward_to_deserialize! { + bool + u8 + u16 + u32 + u64 + i8 + i16 + i32 + i64 + f32 + f64 + char + str + string + unit + bytes + byte_buf + unit_struct + newtype_struct + tuple_struct + struct + struct_field + tuple + enum + ignored_any + seq + seq_fixed_size + map + } +} diff --git a/tests/test_deserialize.rs b/tests/test_deserialize.rs index eeb2ce62..bd92ba9e 100644 --- a/tests/test_deserialize.rs +++ b/tests/test_deserialize.rs @@ -23,3 +23,12 @@ fn deserialize_reader() { assert_eq!(serde_urlencoded::from_reader(b"first=23&last=42" as &[_]), Ok(result)); } + +#[test] +fn deserialize_option() { + let result = vec![ + ("first".to_owned(), Some(23)), + ("last".to_owned(), Some(42)), + ]; + assert_eq!(serde_urlencoded::from_str("first=23&last=42"), Ok(result)); +}