diff options
author | Sam Scott <sam.scott89@gmail.com> | 2017-04-25 11:11:39 +0100 |
---|---|---|
committer | Sam Scott <sam.scott89@gmail.com> | 2017-04-25 11:14:43 +0100 |
commit | 7f4764dd6b63c0c0cf0d27268dfa812c0f2334fc (patch) | |
tree | 7196fad8b398c5e235bf149739285c9102d02759 | |
parent | 53b0c87687fc6dd2f8bca5c4277a41e3e281025d (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.rs | 25 | ||||
-rw-r--r-- | src/de.rs | 53 | ||||
-rw-r--r-- | tests/test_deserialize.rs | 60 |
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); } @@ -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 }); +} |