Query string deserialization: Allow multiple values for one key

This commit is contained in:
Jonas Platte 2020-05-18 01:10:05 +02:00
parent 0e627a8ac1
commit e05b30d008
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
3 changed files with 291 additions and 22 deletions

View File

@ -1,6 +1,10 @@
//! Deserialization support for the `application/x-www-form-urlencoded` format. //! Deserialization support for the `application/x-www-form-urlencoded` format.
use std::{borrow::Cow, io::Read}; use std::{
borrow::Cow,
collections::btree_map::{self, BTreeMap},
io::Read,
};
use serde::{ use serde::{
de::{self, value::MapDeserializer, Error as de_Error, IntoDeserializer}, de::{self, value::MapDeserializer, Error as de_Error, IntoDeserializer},
@ -11,14 +15,18 @@ use url::form_urlencoded::{parse, Parse as UrlEncodedParse};
#[doc(inline)] #[doc(inline)]
pub use serde::de::value::Error; pub use serde::de::value::Error;
mod val_or_vec;
use val_or_vec::ValOrVec;
/// Deserializes a `application/x-www-form-urlencoded` value from a `&[u8]`. /// Deserializes a `application/x-www-form-urlencoded` value from a `&[u8]`.
/// ///
/// ``` /// ```
/// let meal = vec![ /// let meal = vec![
/// ("bread".to_owned(), "baguette".to_owned()), /// ("bread".to_owned(), "baguette".to_owned()),
/// ("cheese".to_owned(), "comté".to_owned()), /// ("cheese".to_owned(), "comté".to_owned()),
/// ("meat".to_owned(), "ham".to_owned()),
/// ("fat".to_owned(), "butter".to_owned()), /// ("fat".to_owned(), "butter".to_owned()),
/// ("meat".to_owned(), "ham".to_owned()),
/// ]; /// ];
/// ///
/// assert_eq!( /// assert_eq!(
@ -39,8 +47,8 @@ where
/// let meal = vec![ /// let meal = vec![
/// ("bread".to_owned(), "baguette".to_owned()), /// ("bread".to_owned(), "baguette".to_owned()),
/// ("cheese".to_owned(), "comté".to_owned()), /// ("cheese".to_owned(), "comté".to_owned()),
/// ("meat".to_owned(), "ham".to_owned()),
/// ("fat".to_owned(), "butter".to_owned()), /// ("fat".to_owned(), "butter".to_owned()),
/// ("meat".to_owned(), "ham".to_owned()),
/// ]; /// ];
/// ///
/// assert_eq!( /// assert_eq!(
@ -79,14 +87,14 @@ where
/// * 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<'de> { pub struct Deserializer<'de> {
inner: MapDeserializer<'de, PartIterator<'de>, Error>, inner: MapDeserializer<'de, EntryIterator<'de>, Error>,
} }
impl<'de> Deserializer<'de> { impl<'de> Deserializer<'de> {
/// Returns a new `Deserializer`. /// Returns a new `Deserializer`.
pub fn new(parser: UrlEncodedParse<'de>) -> Self { pub fn new(parse: UrlEncodedParse<'de>) -> Self {
Deserializer { Deserializer {
inner: MapDeserializer::new(PartIterator(parser)), inner: MapDeserializer::new(group_entries(parse).into_iter()),
} }
} }
} }
@ -152,16 +160,53 @@ impl<'de> de::Deserializer<'de> for Deserializer<'de> {
} }
} }
struct PartIterator<'de>(UrlEncodedParse<'de>); fn group_entries<'de>(
parse: UrlEncodedParse<'de>,
) -> BTreeMap<Part<'de>, ValOrVec<Part<'de>>> {
use btree_map::Entry::*;
impl<'de> Iterator for PartIterator<'de> { let mut res = BTreeMap::new();
type Item = (Part<'de>, Part<'de>);
fn next(&mut self) -> Option<Self::Item> { for (key, value) in parse {
self.0.next().map(|(k, v)| (Part(k), Part(v))) match res.entry(Part(key)) {
Vacant(v) => {
v.insert(ValOrVec::Val(Part(value)));
}
Occupied(mut o) => {
o.get_mut().push(Part(value));
}
} }
} }
res
}
/*
input: a=b&c=d&a=c
vvvvv
next(): a => Wrapper([b, c])
next(): c => Wrapper([d])
struct Foo {
a: Vec<String>,
c: Vec<String>,
}
struct Bar {
a: Vec<String>,
c: String,
}
struct Baz {
a: String,
}
*/
type EntryIterator<'de> = btree_map::IntoIter<Part<'de>, ValOrVec<Part<'de>>>;
#[derive(PartialEq, PartialOrd, Eq, Ord)]
struct Part<'de>(Cow<'de, str>); struct Part<'de>(Cow<'de, str>);
impl<'de> IntoDeserializer<'de> for Part<'de> { impl<'de> IntoDeserializer<'de> for Part<'de> {

View File

@ -0,0 +1,229 @@
use std::{iter, ptr, vec};
use serde::de::{
self,
value::{Error, SeqDeserializer},
Deserializer, IntoDeserializer,
};
#[derive(Debug)]
pub enum ValOrVec<T> {
Val(T),
Vec(Vec<T>),
}
impl<T> ValOrVec<T> {
pub fn push(&mut self, new_val: T) {
match self {
Self::Val(val) => {
let mut vec = Vec::with_capacity(2);
// Safety:
//
// since the vec is pre-allocated, push can't panic, so there
// is no opportunity for a panic in the unsafe code.
unsafe {
let existing_val = ptr::read(val);
vec.push(existing_val);
vec.push(new_val);
ptr::write(self, Self::Vec(vec))
}
}
Self::Vec(vec) => vec.push(new_val),
}
}
}
impl<T> IntoIterator for ValOrVec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
pub enum IntoIter<T> {
Val(iter::Once<T>),
Vec(vec::IntoIter<T>),
}
impl<T> IntoIter<T> {
fn new(vv: ValOrVec<T>) -> Self {
match vv {
ValOrVec::Val(val) => Self::Val(iter::once(val)),
ValOrVec::Vec(vec) => Self::Vec(vec.into_iter()),
}
}
}
impl<T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Val(iter) => iter.next(),
Self::Vec(iter) => iter.next(),
}
}
}
impl<'de, T> IntoDeserializer<'de> for ValOrVec<T>
where
T: IntoDeserializer<'de> + Deserializer<'de, Error = Error>,
{
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
macro_rules! forward_to_part {
($($method:ident,)*) => {
$(
fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: de::Visitor<'de>
{
match self {
Self::Val(val) => val.$method(visitor),
Self::Vec(_) => Err(de::Error::custom("TODO: Error message")),
}
}
)*
}
}
impl<'de, T> Deserializer<'de> for ValOrVec<T>
where
T: IntoDeserializer<'de> + Deserializer<'de, Error = Error>,
{
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
Self::Val(val) => val.deserialize_any(visitor),
Self::Vec(_) => self.deserialize_seq(visitor),
}
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(SeqDeserializer::new(self.into_iter()))
}
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
Self::Val(val) => val.deserialize_enum(name, variants, visitor),
Self::Vec(_) => Err(de::Error::custom("TODO: Error message")),
}
}
fn deserialize_tuple<V>(
self,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
Self::Val(val) => val.deserialize_tuple(len, visitor),
Self::Vec(_) => Err(de::Error::custom("TODO: Error message")),
}
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
Self::Val(val) => val.deserialize_struct(name, fields, visitor),
Self::Vec(_) => Err(de::Error::custom("TODO: Error message")),
}
}
fn deserialize_unit_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
Self::Val(val) => val.deserialize_unit_struct(name, visitor),
Self::Vec(_) => Err(de::Error::custom("TODO: Error message")),
}
}
fn deserialize_tuple_struct<V>(
self,
name: &'static str,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
Self::Val(val) => val.deserialize_tuple_struct(name, len, visitor),
Self::Vec(_) => Err(de::Error::custom("TODO: Error message")),
}
}
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
Self::Val(val) => val.deserialize_newtype_struct(name, visitor),
Self::Vec(_) => Err(de::Error::custom("TODO: Error message")),
}
}
forward_to_part! {
deserialize_bool,
deserialize_char,
deserialize_str,
deserialize_string,
deserialize_bytes,
deserialize_byte_buf,
deserialize_unit,
deserialize_u8,
deserialize_u16,
deserialize_u32,
deserialize_u64,
deserialize_i8,
deserialize_i16,
deserialize_i32,
deserialize_i64,
deserialize_f32,
deserialize_f64,
deserialize_option,
deserialize_identifier,
deserialize_ignored_any,
deserialize_map,
}
}

View File

@ -69,13 +69,13 @@ enum X {
#[test] #[test]
fn deserialize_unit_enum() { fn deserialize_unit_enum() {
let result = vec![ let result: Vec<(String, X)> =
("one".to_owned(), X::A), urlencoded::from_str("one=A&two=B&three=C").unwrap();
("two".to_owned(), X::B),
("three".to_owned(), X::C),
];
assert_eq!(urlencoded::from_str("one=A&two=B&three=C"), Ok(result)); assert_eq!(result.len(), 3);
assert!(result.contains(&("one".to_owned(), X::A)));
assert!(result.contains(&("two".to_owned(), X::B)));
assert!(result.contains(&("three".to_owned(), X::C)));
} }
#[test] #[test]
@ -129,7 +129,6 @@ struct ListStruct {
} }
#[test] #[test]
#[ignore]
fn deserialize_newstruct() { fn deserialize_newstruct() {
let de = NewStruct { let de = NewStruct {
list: vec!["hello", "world"], list: vec!["hello", "world"],
@ -138,7 +137,6 @@ fn deserialize_newstruct() {
} }
#[test] #[test]
#[ignore]
fn deserialize_numlist() { fn deserialize_numlist() {
let de = NumList { let de = NumList {
list: vec![1, 2, 3, 4], list: vec![1, 2, 3, 4],
@ -147,7 +145,6 @@ fn deserialize_numlist() {
} }
#[test] #[test]
#[ignore]
fn deserialize_vec_bool() { fn deserialize_vec_bool() {
assert_eq!( assert_eq!(
urlencoded::from_str("item=true&item=false&item=false"), urlencoded::from_str("item=true&item=false&item=false"),
@ -158,7 +155,6 @@ fn deserialize_vec_bool() {
} }
#[test] #[test]
#[ignore]
fn deserialize_vec_string() { fn deserialize_vec_string() {
assert_eq!( assert_eq!(
urlencoded::from_str("item=hello&item=matrix&item=hello"), urlencoded::from_str("item=hello&item=matrix&item=hello"),
@ -173,7 +169,6 @@ fn deserialize_vec_string() {
} }
#[test] #[test]
#[ignore]
fn deserialize_struct_unit_enum() { fn deserialize_struct_unit_enum() {
let result = Wrapper { let result = Wrapper {
item: vec![X::A, X::B, X::C], item: vec![X::A, X::B, X::C],