summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Scott <sam.scott89@gmail.com>2017-04-25 11:11:39 +0100
committerSam Scott <sam.scott89@gmail.com>2017-04-25 11:14:43 +0100
commit7f4764dd6b63c0c0cf0d27268dfa812c0f2334fc (patch)
tree7196fad8b398c5e235bf149739285c9102d02759
parent53b0c87687fc6dd2f8bca5c4277a41e3e281025d (diff)
Implement deserializing primitives properly.
This finishes the work to upgrade to serde 1.0. Thanks to @kardeiz for starting the work in issue #3. This also clairifies how enums work with serde_qs: only with adjacently tagged enums for the time being.
-rw-r--r--examples/introduction.rs25
-rw-r--r--src/de.rs53
-rw-r--r--tests/test_deserialize.rs60
3 files changed, 120 insertions, 18 deletions
diff --git a/examples/introduction.rs b/examples/introduction.rs
index 66283d3..a8e984f 100644
--- a/examples/introduction.rs
+++ b/examples/introduction.rs
@@ -127,4 +127,29 @@ fn main() {
user_ids[512]=4";
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).
+ #[derive(Deserialize, Debug, PartialEq, Serialize)]
+ #[serde(tag = "type", content = "value")]
+ enum AdjTaggedEnum {
+ B(bool),
+ S(String),
+ V { id: u8, v: String },
+ }
+
+ #[derive(Deserialize, Debug, PartialEq, Serialize)]
+ struct EnumQuery {
+ e: AdjTaggedEnum,
+ }
+
+ let example_params = EnumQuery {
+ e: AdjTaggedEnum::B(false),
+ };
+ // encodes as:
+ // "e[type]=B&e[value]=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);
}
diff --git a/src/de.rs b/src/de.rs
index bd9b08e..8019975 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -564,6 +564,32 @@ impl<'de> de::MapAccess<'de> for Deserializer {
struct LevelDeserializer(Level);
+macro_rules! deserialize_primitive {
+ ($ty:ident, $method:ident, $visit_method:ident) => (
+ fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+ where V: de::Visitor<'de>,
+ {
+ match self.0 {
+ Level::Nested(map) => {
+ Deserializer::with_map(map).deserialize_map(visitor)
+ },
+ Level::Sequence(seq) => {
+ SeqDeserializer::new(seq.into_iter()).deserialize_any(visitor)
+ },
+ Level::Flat(x) => {
+ visitor.$visit_method(str::FromStr::from_str(&x).unwrap())
+ // visitor.visit_u32(str::FromStr::from_str(&x).or_else(|_| {
+ // Err(de::Error::custom(format!("Unexpected string: {}", x)))
+ // }))
+ },
+ Level::Invalid(e) => {
+ Err(de::Error::custom(e))
+ }
+ }
+ }
+ )
+}
+
impl<'de> de::Deserializer<'de> for LevelDeserializer {
type Error = Error;
@@ -578,7 +604,7 @@ impl<'de> de::Deserializer<'de> for LevelDeserializer {
SeqDeserializer::new(seq.into_iter()).deserialize_any(visitor)
},
Level::Flat(x) => {
- x.into_deserializer().deserialize_any(visitor)
+ visitor.visit_string(x)
},
Level::Invalid(e) => {
Err(de::Error::custom(e))
@@ -642,30 +668,29 @@ impl<'de> de::Deserializer<'de> for LevelDeserializer {
}
}
+ deserialize_primitive!(bool, deserialize_bool, visit_bool);
+ deserialize_primitive!(i8, deserialize_i8, visit_i8);
+ deserialize_primitive!(i16, deserialize_i16, visit_i16);
+ deserialize_primitive!(i32, deserialize_i32, visit_i32);
+ deserialize_primitive!(i64, deserialize_i64, visit_i64);
+ deserialize_primitive!(u8, deserialize_u8, visit_u8);
+ deserialize_primitive!(u16, deserialize_u16, visit_u16);
+ deserialize_primitive!(u32, deserialize_u32, visit_u32);
+ deserialize_primitive!(u64, deserialize_u64, visit_u64);
+ deserialize_primitive!(f32, deserialize_f32, visit_f32);
+ deserialize_primitive!(f64, deserialize_f64, visit_f64);
+
forward_to_deserialize_any! {
- bool
- u8
- u16
- u32
- u64
- i8
- i16
- i32
- i64
- f32
- f64
char
str
string
unit
- // option
bytes
byte_buf
unit_struct
newtype_struct
tuple_struct
- // struct_field
identifier
tuple
enum
diff --git a/tests/test_deserialize.rs b/tests/test_deserialize.rs
index 3632c7d..cd22458 100644
--- a/tests/test_deserialize.rs
+++ b/tests/test_deserialize.rs
@@ -24,7 +24,7 @@ struct QueryParams {
macro_rules! map_test {
($string:expr, $($mapvars:tt)*) => {
let expected_map = hash_to_map!(New $($mapvars)*);
- let testmap: HashMap<_, _> = qs::from_str($string).unwrap();
+ let testmap: HashMap<String, _> = qs::from_str($string).unwrap();
assert_eq!(expected_map, testmap);
}
}
@@ -37,13 +37,13 @@ macro_rules! hash_to_map {
//{}
// This parses a single map entry, with a value explicitly an expression.
($map:expr, $k:tt[e $v:expr] $($rest:tt)*) => {{
- $map.insert($k.to_owned(), $v.to_owned());
+ $map.insert($k.to_string(), $v.to_owned());
hash_to_map!($map, $($rest)*);
}};
// This parses a single map entry, plus the rest of the values.
($map:expr, $k:tt[$v:tt] $($rest:tt)*) => {{
- $map.insert($k.to_owned(), $v.to_owned());
+ $map.insert($k.to_string(), $v.to_owned());
hash_to_map!($map, $($rest)*);
}};
@@ -52,7 +52,7 @@ macro_rules! hash_to_map {
($map:expr, $k:tt[$($inner:tt)*] $($rest:tt)*) => {{
let mut inner_map = HashMap::new();
hash_to_map!(inner_map, $($inner)*);
- $map.insert($k.to_owned(), inner_map);
+ $map.insert($k.to_string(), inner_map);
hash_to_map!($map, $($rest)*);
}};
@@ -253,3 +253,55 @@ fn optional_struct() {
let rec_params: Query = qs::from_str(params).unwrap();
assert_eq!(rec_params, query);
}
+
+#[test]
+fn deserialize_enum_untagged() {
+ #[derive(Deserialize, Debug, PartialEq)]
+ #[serde(untagged)]
+ enum E {
+ B(bool),
+ S(String),
+ }
+
+ #[derive(Deserialize, Debug, PartialEq)]
+ struct Query {
+ e: E,
+ }
+
+ let params = "e=true";
+ let rec_params: Query = qs::from_str(params).unwrap();
+ assert_eq!(rec_params, Query { e: E::S("true".to_string()) });
+}
+
+#[test]
+fn deserialize_enum_adjacently() {
+ #[derive(Deserialize, Debug, PartialEq)]
+ #[serde(tag = "type", content = "val")]
+ enum E {
+ B(bool),
+ S(String),
+ }
+
+ #[derive(Deserialize, Debug, PartialEq)]
+ #[serde(tag = "type", content = "val")]
+ enum V {
+ V1 { x: u8, y: u16 },
+ V2(String),
+ }
+
+ #[derive(Deserialize, Debug, PartialEq)]
+ struct Query {
+ e: E,
+ v: Option<V>
+ }
+
+ let params = "e[type]=B&e[val]=true&v[type]=V1&v[val][x]=12&v[val][y]=300";
+ let rec_params: Query = qs::from_str(params).unwrap();
+ assert_eq!(rec_params,
+ Query { e: E::B(true), v: Some(V::V1 { x: 12, y: 300 }) }
+ );
+
+ let params = "e[type]=S&e[val]=other";
+ let rec_params: Query = qs::from_str(params).unwrap();
+ assert_eq!(rec_params, Query { e: E::S("other".to_string()), v: None });
+}