Have a custom type for deserialising values (fixes #16)

This lets us handle Option values correctly.
This commit is contained in:
Anthony Ramine 2017-05-06 10:59:42 +02:00
parent f122a88561
commit 7ddde33a33
2 changed files with 95 additions and 3 deletions

View File

@ -1,11 +1,14 @@
//! Deserialization support for the `application/x-www-form-urlencoded` format. //! Deserialization support for the `application/x-www-form-urlencoded` format.
use serde::de; use serde::de;
use serde::de::value::{MapDeserializer, ValueDeserializer as SerdeValueDeserializer};
#[doc(inline)] #[doc(inline)]
pub use serde::de::value::Error; pub use serde::de::value::Error;
use serde::de::value::MapDeserializer; use std::borrow::Cow;
use std::io::Read; use std::io::Read;
use std::iter::Map;
use std::marker::PhantomData;
use url::form_urlencoded::Parse as UrlEncodedParse; use url::form_urlencoded::Parse as UrlEncodedParse;
use url::form_urlencoded::parse; use url::form_urlencoded::parse;
@ -71,13 +74,18 @@ pub fn from_reader<T, R>(mut reader: R) -> Result<T, Error>
/// * Everything else but `deserialize_seq` and `deserialize_seq_fixed_size` /// * Everything else but `deserialize_seq` and `deserialize_seq_fixed_size`
/// defers to `deserialize`. /// defers to `deserialize`.
pub struct Deserializer<'a> { pub struct Deserializer<'a> {
inner: MapDeserializer<UrlEncodedParse<'a>, Error>, inner: MapDeserializer<Map<UrlEncodedParse<'a>,
fn((Cow<'a, str>, Cow<'a, str>))
-> (Cow<'a, str>, Value<'a>)>,
Error>,
} }
impl<'a> Deserializer<'a> { impl<'a> Deserializer<'a> {
/// Returns a new `Deserializer`. /// Returns a new `Deserializer`.
pub fn new(parser: UrlEncodedParse<'a>) -> Self { pub fn new(parser: UrlEncodedParse<'a>) -> Self {
Deserializer { inner: MapDeserializer::new(parser) } Deserializer {
inner: MapDeserializer::new(parser.map(Value::wrap_pair)),
}
} }
} }
@ -140,3 +148,78 @@ impl<'a> de::Deserializer for Deserializer<'a> {
ignored_any 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<E> 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<E>,
}
impl<'a, E> de::Deserializer for ValueDeserializer<'a, E>
where E: de::Error,
{
type Error = E;
fn deserialize<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: de::Visitor,
{
self.value.into_deserializer().deserialize(visitor)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
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
}
}

View File

@ -23,3 +23,12 @@ fn deserialize_reader() {
assert_eq!(serde_urlencoded::from_reader(b"first=23&last=42" as &[_]), assert_eq!(serde_urlencoded::from_reader(b"first=23&last=42" as &[_]),
Ok(result)); 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));
}