Query string deserialization: Allow multiple values for one key
This commit is contained in:
parent
0e627a8ac1
commit
e05b30d008
@ -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> {
|
||||||
|
229
src/urlencoded/de/val_or_vec.rs
Normal file
229
src/urlencoded/de/val_or_vec.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
@ -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],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user