From ae2ee7fff1fa97e236b35ab89f05f2f4862ea88b Mon Sep 17 00:00:00 2001 From: Valentin Leistner Date: Sun, 18 Sep 2022 02:33:27 +0200 Subject: [PATCH 1/3] PathDeserializer: use `deserialize_str` for `deserialize_any` --- actix-router/src/de.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/actix-router/src/de.rs b/actix-router/src/de.rs index 458e0893..659119d4 100644 --- a/actix-router/src/de.rs +++ b/actix-router/src/de.rs @@ -23,6 +23,9 @@ macro_rules! unsupported_type { macro_rules! parse_single_value { ($trait_fn:ident) => { + parse_single_value!($trait_fn, $trait_fn); + }; + ($trait_fn:ident, $visit_fn:ident) => { fn $trait_fn(self, visitor: V) -> Result where V: Visitor<'de>, @@ -39,7 +42,7 @@ macro_rules! parse_single_value { Value { value: &self.path[0], } - .$trait_fn(visitor) + .$visit_fn(visitor) } } }; @@ -201,11 +204,11 @@ impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T> }) } - unsupported_type!(deserialize_any, "'any'"); unsupported_type!(deserialize_option, "Option"); unsupported_type!(deserialize_identifier, "identifier"); unsupported_type!(deserialize_ignored_any, "ignored_any"); + parse_single_value!(deserialize_any, deserialize_str); parse_single_value!(deserialize_bool); parse_single_value!(deserialize_i8); parse_single_value!(deserialize_i16); From 6df38522eba462cc245af0b98987df06a25778ca Mon Sep 17 00:00:00 2001 From: Valentin Leistner Date: Tue, 20 Sep 2022 23:24:55 +0200 Subject: [PATCH 2/3] fix `deserialize_any` for `seq` and `map` --- actix-router/src/de.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/actix-router/src/de.rs b/actix-router/src/de.rs index 659119d4..33abe75e 100644 --- a/actix-router/src/de.rs +++ b/actix-router/src/de.rs @@ -426,7 +426,13 @@ impl<'de> Deserializer<'de> for Value<'de> { Err(de::value::Error::custom("unsupported type: tuple struct")) } - unsupported_type!(deserialize_any, "any"); + fn deserialize_any(self, v: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(v) + } + unsupported_type!(deserialize_seq, "seq"); unsupported_type!(deserialize_map, "map"); unsupported_type!(deserialize_identifier, "identifier"); From 8d6c23038aeb158672d53271f9a9551169132ff7 Mon Sep 17 00:00:00 2001 From: Valentin Leistner Date: Tue, 20 Sep 2022 23:27:05 +0200 Subject: [PATCH 3/3] add tests for `deserialize_any` --- actix-router/src/de.rs | 98 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/actix-router/src/de.rs b/actix-router/src/de.rs index 33abe75e..e1719e62 100644 --- a/actix-router/src/de.rs +++ b/actix-router/src/de.rs @@ -722,6 +722,104 @@ mod tests { assert_eq!(vals.value, "/"); } + #[test] + fn deserialize_path_decode_any() { + #[derive(Debug, PartialEq)] + pub enum AnyEnumCustom { + String(String), + Int(u32), + Other, + } + + impl<'de> Deserialize<'de> for AnyEnumCustom { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Vis; + impl<'de> Visitor<'de> for Vis { + type Value = AnyEnumCustom; + + fn expecting<'a>( + &self, + f: &mut std::fmt::Formatter<'a>, + ) -> std::fmt::Result { + write!(f, "my thing") + } + + fn visit_u32(self, v: u32) -> Result + where + E: serde::de::Error, + { + Ok(AnyEnumCustom::Int(v)) + } + + fn visit_str(self, v: &str) -> Result { + v.parse().map(AnyEnumCustom::Int).or_else(|_| { + Ok(match v { + "other" => AnyEnumCustom::Other, + _ => AnyEnumCustom::String(format!("some str: {v}")), + }) + }) + } + } + + deserializer.deserialize_any(Vis) + } + } + + #[derive(Debug, Deserialize, PartialEq)] + #[serde(untagged)] + pub enum AnyEnumDerive { + String(String), + Int(u32), + Other, + } + + // single + let rdef = ResourceDef::new("/{key}"); + + let mut path = Path::new("/%25"); + rdef.capture_match_info(&mut path); + let de = PathDeserializer::new(&path); + let segment: AnyEnumCustom = serde::Deserialize::deserialize(de).unwrap(); + assert_eq!(segment, AnyEnumCustom::String("some str: %".to_string())); + + let mut path = Path::new("/%25"); + rdef.capture_match_info(&mut path); + let de = PathDeserializer::new(&path); + let segment: AnyEnumDerive = serde::Deserialize::deserialize(de).unwrap(); + assert_eq!(segment, AnyEnumDerive::String("%".to_string())); + + // seq + let rdef = ResourceDef::new("/{key}/{value}"); + + let mut path = Path::new("/other/123"); + rdef.capture_match_info(&mut path); + let de = PathDeserializer::new(&path); + let segment: (AnyEnumCustom, AnyEnumDerive) = + serde::Deserialize::deserialize(de).unwrap(); + assert_eq!(segment.0, AnyEnumCustom::Other); + // Deserializes to `String` instead of `Int` because deserializing defaults to `str` and serde doesn't automatically implement parsing numbers from `&str`s + assert_eq!(segment.1, AnyEnumDerive::String("123".to_string())); + + // map + #[derive(Deserialize)] + struct Vals { + key: AnyEnumCustom, + value: AnyEnumDerive, + } + + let rdef = ResourceDef::new("/{key}/{value}"); + + let mut path = Path::new("/123/%2F"); + rdef.capture_match_info(&mut path); + let de = PathDeserializer::new(&path); + let vals: Vals = serde::Deserialize::deserialize(de).unwrap(); + assert_eq!(vals.key, AnyEnumCustom::Int(123)); + assert_eq!(vals.value, AnyEnumDerive::String("/".to_string())); + } + #[test] fn deserialize_borrowed() { #[derive(Debug, Deserialize)]