diff options
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | examples/introduction.rs | 25 | ||||
-rw-r--r-- | src/de/mod.rs | 26 | ||||
-rw-r--r-- | src/de/parse.rs | 8 | ||||
-rw-r--r-- | src/lib.rs | 13 | ||||
-rw-r--r-- | tests/test_deserialize.rs | 8 |
6 files changed, 56 insertions, 26 deletions
@@ -7,7 +7,7 @@ keywords = ["serde", "serialization", "querystring"] license = "MIT/Apache-2.0" name = "serde_qs" repository = "https://github.com/samscott89/serde_qs" -version = "0.1.0" +version = "0.2.0" [badges] diff --git a/examples/introduction.rs b/examples/introduction.rs index fb70665..4e69f16 100644 --- a/examples/introduction.rs +++ b/examples/introduction.rs @@ -60,12 +60,12 @@ fn main() { // In this form, can also simply use `serde_urlencoded`: let encoded = urlencoded::to_string(&map).unwrap(); println!("`serde_urlencoded` to_string for map:\n\t{}", encoded); + println!(""); // Given this encoded string, you can recover the original map // as a list of pairs using serde_urlencoded: let pairs: Vec<(String, String)> = urlencoded::from_str(&encoded).unwrap(); println!("`serde_urlencoded` from_str to pairs:\n\t{:?}", pairs); - // However, the best way is to use serde_qs to deserialize the entire thing // into a struct: let params: QueryParams = qs::from_str(&encoded).unwrap(); @@ -75,6 +75,8 @@ fn main() { // Similarly, we can serialize this structure using `serde_qs`: let encoded = qs::to_string(¶ms).unwrap(); println!("`serde_qs` to_string for struct:\n\t{:?}", encoded); + println!(""); + // One nice feature is that this gives deterministic encodings: let encoded2 = qs::to_string(¶ms).unwrap(); @@ -128,11 +130,11 @@ fn main() { let params: QueryParams = qs::from_str(encoded).unwrap(); assert_eq!(params, example_params); - // Enums are supported, but only adjacently tagged enums - // (see https://serde.rs/enum-representations.html for more information). + // Enums are now fully supported! Most formats should work with varying + // results. #[derive(Deserialize, Debug, PartialEq, Serialize)] - // #[serde(tag = "type", content = "value")] enum AdjTaggedEnum { + A, B(bool), S(String), V { id: u8, v: String }, @@ -147,9 +149,22 @@ fn main() { e: AdjTaggedEnum::B(false), }; // encodes as: - // "e[type]=B&e[value]=false" + // "e[B]=false" let encoded = qs::to_string(&example_params).unwrap(); println!("`serde_qs` to_string for enum:\n\t{:?}", encoded); let params: EnumQuery = qs::from_str(&encoded).unwrap(); println!("`serde_qs` from_str for enum:\n\t{:?}", params); + println!(""); + + let example_params = EnumQuery { + e: AdjTaggedEnum::A, + }; + // encodes as: + // "e=A" + let encoded = qs::to_string(&example_params).unwrap(); + println!("`serde_qs` to_string for enum:\n\t{:?}", encoded); + let params: EnumQuery = qs::from_str(&encoded).unwrap(); + println!("`serde_qs` from_str for enum:\n\t{:?}", params); + println!(""); + } diff --git a/src/de/mod.rs b/src/de/mod.rs index 267b4cb..94c66e1 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -5,11 +5,9 @@ mod parse; pub use de::parse::Config; use data_encoding; -use data_encoding::base64; use serde::de; use serde::de::IntoDeserializer; -use serde::de::value::MapDeserializer; use url::percent_encoding; @@ -118,7 +116,7 @@ pub fn from_reader<'de, T, R>(mut reader: R) -> Result<T> /// A deserializer for the querystring format. /// /// Supported top-level outputs are structs and maps. -pub struct Deserializer { +pub struct QsDeserializer { iter: IntoIter<String, Level>, value: Option<Level>, } @@ -131,15 +129,15 @@ pub enum Level { Invalid(&'static str), } -impl Deserializer { +impl QsDeserializer { fn with_map(map: BTreeMap<String, Level>) -> Self { - Deserializer { + QsDeserializer { iter: map.into_iter(), value: None, } } - /// Returns a new `Deserializer`. + /// Returns a new `QsDeserializer`. fn with_config(config: &Config, input: &[u8]) -> Self { let decoded = percent_encoding::percent_decode(input); parse::Parser::new(decoded, vec![], None, config.max_depth()).to_deserializer() @@ -147,7 +145,7 @@ impl Deserializer { } } -impl<'de> de::Deserializer<'de> for Deserializer { +impl<'de> de::Deserializer<'de> for QsDeserializer { type Error = Error; fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> @@ -266,7 +264,7 @@ impl<'de> de::Deserializer<'de> for Deserializer { // use serde::de::IntoDeserializer; // use serde::de::value::SeqDeserializer; -impl<'de> de::MapAccess<'de> for Deserializer { +impl<'de> de::MapAccess<'de> for QsDeserializer { type Error = Error; @@ -292,7 +290,7 @@ impl<'de> de::MapAccess<'de> for Deserializer { } } -impl<'de> de::EnumAccess<'de> for Deserializer { +impl<'de> de::EnumAccess<'de> for QsDeserializer { type Error = Error; type Variant = LevelDeserializer; @@ -406,16 +404,16 @@ macro_rules! deserialize_primitive { } impl LevelDeserializer { - fn to_deserializer(self) -> Result<Deserializer> { + fn to_deserializer(self) -> Result<QsDeserializer> { match self.0 { Level::Nested(map) => { - Ok(Deserializer::with_map(map)) + Ok(QsDeserializer::with_map(map)) }, Level::Invalid(e) => { Err(de::Error::custom(e)) } l => { - Err(de::Error::custom(format!("could not convert {:?} to Deserializer", l))) + Err(de::Error::custom(format!("could not convert {:?} to QsDeserializer", l))) }, } } @@ -429,7 +427,7 @@ impl<'de> de::Deserializer<'de> for LevelDeserializer { { match self.0 { Level::Nested(_) => { - // Deserializer::with_map(map).deserialize_map(visitor) + // QsDeserializer::with_map(map).deserialize_map(visitor) self.deserialize_map(visitor) }, Level::Sequence(_) => { @@ -487,7 +485,7 @@ impl<'de> de::Deserializer<'de> for LevelDeserializer { { match self.0 { Level::Nested(map) => { - Deserializer::with_map(map).deserialize_enum(name, variants, visitor) + QsDeserializer::with_map(map).deserialize_enum(name, variants, visitor) }, Level::Flat(_) => { visitor.visit_enum(self) diff --git a/src/de/parse.rs b/src/de/parse.rs index 7a647db..1f91f07 100644 --- a/src/de/parse.rs +++ b/src/de/parse.rs @@ -10,6 +10,8 @@ use super::*; /// to prevent denial of service attacks by providing incredibly deeply nested /// inputs. /// +/// The default value for `max_depth` is 5. +/// /// ``` /// use serde_qs::de::Config; /// use std::collections::HashMap; @@ -53,7 +55,7 @@ impl Config { pub fn deserialize_bytes<'de, T: de::Deserialize<'de>>(&self, input: &[u8]) -> Result<T> { - T::deserialize(Deserializer::with_config(self, input)) + T::deserialize(QsDeserializer::with_config(self, input)) } pub fn deserialize_str<'de, T: de::Deserialize<'de>>(&self, @@ -129,7 +131,7 @@ impl<I: Iterator<Item = u8>> Parser<I> { } } - pub fn to_deserializer(mut self) -> Deserializer { + pub fn to_deserializer(mut self) -> QsDeserializer { let map = BTreeMap::default(); let mut root = Level::Nested(map); while let Ok(x) = self.parse(&mut root) { @@ -141,7 +143,7 @@ impl<I: Iterator<Item = u8>> Parser<I> { Level::Nested(map) => map.into_iter(), _ => panic!(""), }; - Deserializer { + QsDeserializer { iter: iter, value: None, } @@ -12,8 +12,15 @@ //! recommended that the `serde_urlencoded` crate is used instead, which //! will almost certainly perform better for deserializing simple inputs. //! -//! The serialization implementation of this library is adapted from -//! `serde_urlencoded`. +//! ## Supported Types +//! +//! At the **top level**, `serde_qs` only supports `struct`, `map`, and `enum`. +//! These are the only top-level structs which can be de/serialized since +//! Querystrings rely on having a (key, value) pair for each field, which +//! necessitates this kind of structure. +//! +//! However, after the top level you should find all supported types can be +//! de/serialized. //! //! ## Usage //! @@ -72,7 +79,7 @@ pub mod de; pub mod ser; #[doc(inline)] -pub use de::{Deserializer, from_bytes, from_reader, from_str}; +pub use de::{QsDeserializer, from_bytes, from_reader, from_str}; #[doc(inline)] pub use de::Config; #[doc(inline)] diff --git a/tests/test_deserialize.rs b/tests/test_deserialize.rs index 5d6d2c6..3e0c843 100644 --- a/tests/test_deserialize.rs +++ b/tests/test_deserialize.rs @@ -367,4 +367,12 @@ fn deserialize_enum() { let params = "e[S]=other&u=1"; let rec_params: Query = qs::from_str(params).unwrap(); assert_eq!(rec_params, Query { e: E::S("other".to_string()), v: None, u: NewU8(1) }); + + let params = "B="; + let rec_params: E = qs::from_str(params).unwrap(); + assert_eq!(rec_params, E::B); + + let params = "S=Hello+World"; + let rec_params: E = qs::from_str(params).unwrap(); + assert_eq!(rec_params, E::S("Hello World".to_string())); } |