diff --git a/actix-router/src/de.rs b/actix-router/src/de.rs index 33abe75e6..e1719e628 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)]