From 0798c861874b4f5afe266ab79322f82b1ff4c9a4 Mon Sep 17 00:00:00 2001 From: Chrislearn Young Date: Fri, 4 Oct 2024 17:14:01 +0800 Subject: [PATCH] Parse url query array like `ids=[1,2,3]` --- crates/core/src/extract/metadata.rs | 2 + crates/core/src/serde/cow_value.rs | 111 ++++++++++ crates/core/src/serde/mod.rs | 258 +---------------------- crates/core/src/serde/request.rs | 10 +- crates/core/src/serde/url_query_value.rs | 163 ++++++++++++++ crates/core/src/serde/vec_value.rs | 148 +++++++++++++ 6 files changed, 440 insertions(+), 252 deletions(-) create mode 100644 crates/core/src/serde/cow_value.rs create mode 100644 crates/core/src/serde/url_query_value.rs create mode 100644 crates/core/src/serde/vec_value.rs diff --git a/crates/core/src/extract/metadata.rs b/crates/core/src/extract/metadata.rs index eeac85786..1e304eaaa 100644 --- a/crates/core/src/extract/metadata.rs +++ b/crates/core/src/extract/metadata.rs @@ -49,6 +49,8 @@ pub enum SourceParser { MultiMap, /// Json parser. Json, + /// Url parser. + UrlQuery, /// Smart parser. Smart, } diff --git a/crates/core/src/serde/cow_value.rs b/crates/core/src/serde/cow_value.rs new file mode 100644 index 000000000..fc133d474 --- /dev/null +++ b/crates/core/src/serde/cow_value.rs @@ -0,0 +1,111 @@ +use std::borrow::Cow; + +use serde::de::value::Error as ValError; +use serde::de::{Deserializer, Error as DeError, IntoDeserializer, Visitor}; +use serde::forward_to_deserialize_any; + +use super::ValueEnumAccess; + +macro_rules! forward_cow_parsed_value { + ($($ty:ident => $method:ident,)*) => { + $( + fn $method(self, visitor: V) -> Result + where V: Visitor<'de> + { + match self.0.parse::<$ty>() { + Ok(val) => val.into_deserializer().$method(visitor), + Err(e) => Err(DeError::custom(e)) + } + } + )* + } +} + +#[derive(Debug)] +pub(super) struct CowValue<'de>(pub(super) Cow<'de, str>); +impl<'de> IntoDeserializer<'de> for CowValue<'de> { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de> Deserializer<'de> for CowValue<'de> { + type Error = ValError; + + #[inline] + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.0 { + Cow::Borrowed(value) => visitor.visit_borrowed_str(value), + Cow::Owned(value) => visitor.visit_string(value), + } + } + + #[inline] + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_some(self) + } + + #[inline] + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_enum(ValueEnumAccess(self.0)) + } + + #[inline] + fn deserialize_newtype_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + forward_to_deserialize_any! { + char + str + string + unit + bytes + byte_buf + unit_struct + tuple_struct + struct + identifier + tuple + ignored_any + seq + map + } + + forward_cow_parsed_value! { + bool => deserialize_bool, + u8 => deserialize_u8, + u16 => deserialize_u16, + u32 => deserialize_u32, + u64 => deserialize_u64, + i8 => deserialize_i8, + i16 => deserialize_i16, + i32 => deserialize_i32, + i64 => deserialize_i64, + f32 => deserialize_f32, + f64 => deserialize_f64, + } +} diff --git a/crates/core/src/serde/mod.rs b/crates/core/src/serde/mod.rs index d1b89b4d7..1da780266 100644 --- a/crates/core/src/serde/mod.rs +++ b/crates/core/src/serde/mod.rs @@ -3,13 +3,18 @@ use std::hash::Hash; pub use serde::de::value::{Error as ValError, MapDeserializer, SeqDeserializer}; use serde::de::{ - Deserialize, DeserializeSeed, Deserializer, EnumAccess, Error as DeError, IntoDeserializer, - VariantAccess, Visitor, + Deserialize, DeserializeSeed, EnumAccess, Error as DeError, IntoDeserializer, VariantAccess, + Visitor, }; -use serde::forward_to_deserialize_any; mod request; pub use request::from_request; +mod cow_value; +use cow_value::CowValue; +mod vec_value; +use vec_value::VecValue; +mod url_query_value; +use url_query_value::UrlQueryValue; #[inline] pub fn from_str_map<'de, I, T, K, V>(input: I) -> Result @@ -62,40 +67,6 @@ where T::deserialize(CowValue(input.into())) } -macro_rules! forward_cow_parsed_value { - ($($ty:ident => $method:ident,)*) => { - $( - fn $method(self, visitor: V) -> Result - where V: Visitor<'de> - { - match self.0.parse::<$ty>() { - Ok(val) => val.into_deserializer().$method(visitor), - Err(e) => Err(DeError::custom(e)) - } - } - )* - } -} - -macro_rules! forward_vec_parsed_value { - ($($ty:ident => $method:ident,)*) => { - $( - fn $method(self, visitor: V) -> Result - where V: Visitor<'de> - { - if let Some(item) = self.0.into_iter().next() { - match item.0.parse::<$ty>() { - Ok(val) => val.into_deserializer().$method(visitor), - Err(e) => Err(DeError::custom(e)) - } - } else { - Err(DeError::custom("expected vec not empty")) - } - } - )* - } -} - struct ValueEnumAccess<'de>(Cow<'de, str>); impl<'de> EnumAccess<'de> for ValueEnumAccess<'de> { @@ -151,219 +122,6 @@ impl<'de> VariantAccess<'de> for UnitOnlyVariantAccess { } } -#[derive(Debug)] -struct CowValue<'de>(Cow<'de, str>); -impl<'de> IntoDeserializer<'de> for CowValue<'de> { - type Deserializer = Self; - - fn into_deserializer(self) -> Self::Deserializer { - self - } -} - -impl<'de> Deserializer<'de> for CowValue<'de> { - type Error = ValError; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.0 { - Cow::Borrowed(value) => visitor.visit_borrowed_str(value), - Cow::Owned(value) => visitor.visit_string(value), - } - } - - #[inline] - fn deserialize_option(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_some(self) - } - - #[inline] - fn deserialize_enum( - self, - _name: &'static str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - visitor.visit_enum(ValueEnumAccess(self.0)) - } - - #[inline] - fn deserialize_newtype_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - visitor.visit_newtype_struct(self) - } - - forward_to_deserialize_any! { - char - str - string - unit - bytes - byte_buf - unit_struct - tuple_struct - struct - identifier - tuple - ignored_any - seq - map - } - - forward_cow_parsed_value! { - bool => deserialize_bool, - u8 => deserialize_u8, - u16 => deserialize_u16, - u32 => deserialize_u32, - u64 => deserialize_u64, - i8 => deserialize_i8, - i16 => deserialize_i16, - i32 => deserialize_i32, - i64 => deserialize_i64, - f32 => deserialize_f32, - f64 => deserialize_f64, - } -} - -struct VecValue(I); -impl<'de, I> IntoDeserializer<'de> for VecValue -where - I: Iterator>, -{ - type Deserializer = Self; - - #[inline] - fn into_deserializer(self) -> Self::Deserializer { - self - } -} - -impl<'de, I> Deserializer<'de> for VecValue -where - I: IntoIterator>, -{ - type Error = ValError; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - if let Some(item) = self.0.into_iter().next() { - item.deserialize_any(visitor) - } else { - Err(DeError::custom("expected vec not empty")) - } - } - - #[inline] - fn deserialize_option(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_some(self) - } - - #[inline] - fn deserialize_enum( - self, - _name: &'static str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - if let Some(item) = self.0.into_iter().next() { - visitor.visit_enum(ValueEnumAccess(item.0)) - } else { - Err(DeError::custom("expected vec not empty")) - } - } - - #[inline] - fn deserialize_newtype_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - visitor.visit_newtype_struct(self) - } - - #[inline] - fn deserialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_seq(visitor) - } - #[inline] - fn deserialize_tuple(self, _len: usize, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_seq(visitor) - } - #[inline] - fn deserialize_seq(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_seq(SeqDeserializer::new(self.0.into_iter())) - } - - forward_to_deserialize_any! { - char - str - string - unit - bytes - byte_buf - unit_struct - struct - identifier - ignored_any - map - } - - forward_vec_parsed_value! { - bool => deserialize_bool, - u8 => deserialize_u8, - u16 => deserialize_u16, - u32 => deserialize_u32, - u64 => deserialize_u64, - i8 => deserialize_i8, - i16 => deserialize_i16, - i32 => deserialize_i32, - i64 => deserialize_i64, - f32 => deserialize_f32, - f64 => deserialize_f64, - } -} - #[cfg(test)] mod tests { use std::collections::HashMap; diff --git a/crates/core/src/serde/request.rs b/crates/core/src/serde/request.rs index fe2c84798..10e657b64 100644 --- a/crates/core/src/serde/request.rs +++ b/crates/core/src/serde/request.rs @@ -15,7 +15,7 @@ use crate::http::header::HeaderMap; use crate::http::ParseError; use crate::Request; -use super::{CowValue, VecValue}; +use super::{CowValue, UrlQueryValue, VecValue}; pub async fn from_request<'de, T>( req: &'de mut Request, @@ -141,6 +141,8 @@ impl<'de> RequestDeserializer<'de> { } else { parser = SourceParser::MultiMap; } + } else if source.from == SourceFrom::Query { + parser = SourceParser::UrlQuery; } else { parser = SourceParser::MultiMap; } @@ -192,7 +194,11 @@ impl<'de> RequestDeserializer<'de> { } else if let Some(value) = self.field_str_value.take() { seed.deserialize(CowValue(value.into())) } else if let Some(value) = self.field_vec_value.take() { - seed.deserialize(VecValue(value.into_iter())) + if source.from == SourceFrom::Query { + seed.deserialize(UrlQueryValue(value)) + } else { + seed.deserialize(VecValue(value.into_iter())) + } } else { Err(ValError::custom("parse value error")) } diff --git a/crates/core/src/serde/url_query_value.rs b/crates/core/src/serde/url_query_value.rs new file mode 100644 index 000000000..c96babfad --- /dev/null +++ b/crates/core/src/serde/url_query_value.rs @@ -0,0 +1,163 @@ +use std::borrow::Cow; + +use serde::de::value::{Error as ValError, SeqDeserializer}; +use serde::de::{ + Deserializer, Error as DeError, IntoDeserializer, + Visitor, +}; +use serde::forward_to_deserialize_any; + +use super::{CowValue, ValueEnumAccess}; + +macro_rules! forward_url_query_parsed_value { + ($($ty:ident => $method:ident,)*) => { + $( + fn $method(self, visitor: V) -> Result + where V: Visitor<'de> + { + if let Some(item) = self.0.into_iter().next() { + match item.0.parse::<$ty>() { + Ok(val) => val.into_deserializer().$method(visitor), + Err(e) => Err(DeError::custom(e)) + } + } else { + Err(DeError::custom("expected vec not empty")) + } + } + )* + } +} + +pub(super) struct UrlQueryValue<'de>(pub(super) Vec>); +impl<'de> IntoDeserializer<'de> for UrlQueryValue<'de> { + type Deserializer = Self; + + #[inline] + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de> Deserializer<'de> for UrlQueryValue<'de> { + type Error = ValError; + + #[inline] + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if let Some(item) = self.0.into_iter().next() { + item.deserialize_any(visitor) + } else { + Err(DeError::custom("expected url query not empty")) + } + } + + #[inline] + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_some(self) + } + + #[inline] + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + if let Some(item) = self.0.into_iter().next() { + visitor.visit_enum(ValueEnumAccess(item.0)) + } else { + Err(DeError::custom("expected vec not empty")) + } + } + + #[inline] + fn deserialize_newtype_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + #[inline] + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + #[inline] + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + #[inline] + fn deserialize_seq(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let mut items = std::mem::take(&mut self.0); + let single_mode = if items.len() == 1 { + if let Some(item) = items.get(0) { + item.0.starts_with('[') && item.0.ends_with(']') + } else { + false + } + } else { + false + }; + if !single_mode { + visitor.visit_seq(SeqDeserializer::new(items.into_iter())) + } else { + let vec_value: Vec> = serde_json::from_str(&items.remove(0).0) + .map_err(|e| ValError::custom(e.to_string()))?; + visitor.visit_seq(SeqDeserializer::new(vec_value.into_iter().map(CowValue))) + } + } + + forward_to_deserialize_any! { + char + str + string + unit + bytes + byte_buf + unit_struct + struct + identifier + ignored_any + map + } + + forward_url_query_parsed_value! { + bool => deserialize_bool, + u8 => deserialize_u8, + u16 => deserialize_u16, + u32 => deserialize_u32, + u64 => deserialize_u64, + i8 => deserialize_i8, + i16 => deserialize_i16, + i32 => deserialize_i32, + i64 => deserialize_i64, + f32 => deserialize_f32, + f64 => deserialize_f64, + } +} diff --git a/crates/core/src/serde/vec_value.rs b/crates/core/src/serde/vec_value.rs new file mode 100644 index 000000000..46baa01ea --- /dev/null +++ b/crates/core/src/serde/vec_value.rs @@ -0,0 +1,148 @@ +use serde::de::value::{Error as ValError, SeqDeserializer}; +use serde::de::{Deserializer, Error as DeError, IntoDeserializer, Visitor}; +use serde::forward_to_deserialize_any; + +use super::{CowValue, ValueEnumAccess}; + +macro_rules! forward_vec_parsed_value { + ($($ty:ident => $method:ident,)*) => { + $( + fn $method(self, visitor: V) -> Result + where V: Visitor<'de> + { + if let Some(item) = self.0.into_iter().next() { + match item.0.parse::<$ty>() { + Ok(val) => val.into_deserializer().$method(visitor), + Err(e) => Err(DeError::custom(e)) + } + } else { + Err(DeError::custom("expected vec not empty")) + } + } + )* + } +} + +pub(super) struct VecValue(pub(super) I); +impl<'de, I> IntoDeserializer<'de> for VecValue +where + I: Iterator>, +{ + type Deserializer = Self; + + #[inline] + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de, I> Deserializer<'de> for VecValue +where + I: IntoIterator>, +{ + type Error = ValError; + + #[inline] + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if let Some(item) = self.0.into_iter().next() { + item.deserialize_any(visitor) + } else { + Err(DeError::custom("expected vec not empty")) + } + } + + #[inline] + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_some(self) + } + + #[inline] + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + if let Some(item) = self.0.into_iter().next() { + visitor.visit_enum(ValueEnumAccess(item.0)) + } else { + Err(DeError::custom("expected vec not empty")) + } + } + + #[inline] + fn deserialize_newtype_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + #[inline] + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + #[inline] + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + #[inline] + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_seq(SeqDeserializer::new(self.0.into_iter())) + } + + forward_to_deserialize_any! { + char + str + string + unit + bytes + byte_buf + unit_struct + struct + identifier + ignored_any + map + } + + forward_vec_parsed_value! { + bool => deserialize_bool, + u8 => deserialize_u8, + u16 => deserialize_u16, + u32 => deserialize_u32, + u64 => deserialize_u64, + i8 => deserialize_i8, + i16 => deserialize_i16, + i32 => deserialize_i32, + i64 => deserialize_i64, + f32 => deserialize_f32, + f64 => deserialize_f64, + } +}