Skip to content

Commit 4f0912d

Browse files
nitn3lavrobjtedeJohnTitor
authored
PathDeserializer: use deserialize_str for deserialize_any (#2881)
* PathDeserializer: use `deserialize_str` for `deserialize_any` * fix `deserialize_any` for `seq` and `map` * add tests for `deserialize_any` * parse numeric values as well --------- Co-authored-by: Rob Ede <[email protected]> Co-authored-by: Yuki Okushi <[email protected]>
1 parent 5548fad commit 4f0912d

File tree

2 files changed

+154
-3
lines changed

2 files changed

+154
-3
lines changed

actix-router/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
## Unreleased
44

55
- Minimum supported Rust version (MSRV) is now 1.88.
6+
- Support `deserialize_any` in `PathDeserializer` (enables derived `#[serde(untagged)]` enums in path segments). [#2881]
7+
8+
[#2881]: https://github.com/actix/actix-web/pull/2881
69

710
## 0.5.3
811

actix-router/src/de.rs

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ macro_rules! unsupported_type {
2727

2828
macro_rules! parse_single_value {
2929
($trait_fn:ident) => {
30+
parse_single_value!($trait_fn, $trait_fn);
31+
};
32+
($trait_fn:ident, $visit_fn:ident) => {
3033
fn $trait_fn<V>(self, visitor: V) -> Result<V::Value, Self::Error>
3134
where
3235
V: Visitor<'de>,
@@ -43,7 +46,7 @@ macro_rules! parse_single_value {
4346
Value {
4447
value: &self.path[0],
4548
}
46-
.$trait_fn(visitor)
49+
.$visit_fn(visitor)
4750
}
4851
}
4952
};
@@ -205,11 +208,11 @@ impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T>
205208
})
206209
}
207210

208-
unsupported_type!(deserialize_any, "'any'");
209211
unsupported_type!(deserialize_option, "Option<T>");
210212
unsupported_type!(deserialize_identifier, "identifier");
211213
unsupported_type!(deserialize_ignored_any, "ignored_any");
212214

215+
parse_single_value!(deserialize_any);
213216
parse_single_value!(deserialize_bool);
214217
parse_single_value!(deserialize_i8);
215218
parse_single_value!(deserialize_i16);
@@ -427,7 +430,39 @@ impl<'de> Deserializer<'de> for Value<'de> {
427430
Err(de::value::Error::custom("unsupported type: tuple struct"))
428431
}
429432

430-
unsupported_type!(deserialize_any, "any");
433+
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
434+
where
435+
V: Visitor<'de>,
436+
{
437+
let decoded = FULL_QUOTER
438+
.with(|q| q.requote_str_lossy(self.value))
439+
.map(Cow::Owned)
440+
.unwrap_or(Cow::Borrowed(self.value));
441+
442+
let s = decoded.as_ref();
443+
// We have to do it manually here on behalf of serde.
444+
if let Ok(v) = s.parse::<u64>() {
445+
if let Ok(v) = u32::try_from(v) {
446+
return visitor.visit_u32(v);
447+
}
448+
449+
return visitor.visit_u64(v);
450+
}
451+
452+
if let Ok(v) = s.parse::<i64>() {
453+
if let Ok(v) = i32::try_from(v) {
454+
return visitor.visit_i32(v);
455+
}
456+
457+
return visitor.visit_i64(v);
458+
}
459+
460+
match decoded {
461+
Cow::Borrowed(value) => visitor.visit_borrowed_str(value),
462+
Cow::Owned(value) => visitor.visit_string(value),
463+
}
464+
}
465+
431466
unsupported_type!(deserialize_seq, "seq");
432467
unsupported_type!(deserialize_map, "map");
433468
unsupported_type!(deserialize_identifier, "identifier");
@@ -704,6 +739,119 @@ mod tests {
704739
assert_eq!(vals.value, "/");
705740
}
706741

742+
#[test]
743+
fn deserialize_path_decode_any() {
744+
#[derive(Debug, PartialEq)]
745+
pub enum AnyEnumCustom {
746+
String(String),
747+
Int(u32),
748+
Other,
749+
}
750+
751+
impl<'de> Deserialize<'de> for AnyEnumCustom {
752+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
753+
where
754+
D: serde::Deserializer<'de>,
755+
{
756+
struct Vis;
757+
impl<'de> Visitor<'de> for Vis {
758+
type Value = AnyEnumCustom;
759+
760+
fn expecting<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {
761+
write!(f, "my thing")
762+
}
763+
764+
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
765+
where
766+
E: serde::de::Error,
767+
{
768+
Ok(AnyEnumCustom::Int(v))
769+
}
770+
771+
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
772+
where
773+
E: serde::de::Error,
774+
{
775+
match u32::try_from(v) {
776+
Ok(v) => Ok(AnyEnumCustom::Int(v)),
777+
Err(_) => Ok(AnyEnumCustom::String(format!("some str: {v}"))),
778+
}
779+
}
780+
781+
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
782+
where
783+
E: serde::de::Error,
784+
{
785+
match u32::try_from(v) {
786+
Ok(v) => Ok(AnyEnumCustom::Int(v)),
787+
Err(_) => Ok(AnyEnumCustom::String(format!("some str: {v}"))),
788+
}
789+
}
790+
791+
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
792+
v.parse().map(AnyEnumCustom::Int).or_else(|_| {
793+
Ok(match v {
794+
"other" => AnyEnumCustom::Other,
795+
_ => AnyEnumCustom::String(format!("some str: {v}")),
796+
})
797+
})
798+
}
799+
}
800+
801+
deserializer.deserialize_any(Vis)
802+
}
803+
}
804+
805+
#[derive(Debug, Deserialize, PartialEq)]
806+
#[serde(untagged)]
807+
pub enum AnyEnumDerive {
808+
String(String),
809+
Int(u32),
810+
Other,
811+
}
812+
813+
// single
814+
let rdef = ResourceDef::new("/{key}");
815+
816+
let mut path = Path::new("/%25");
817+
rdef.capture_match_info(&mut path);
818+
let de = PathDeserializer::new(&path);
819+
let segment: AnyEnumCustom = serde::Deserialize::deserialize(de).unwrap();
820+
assert_eq!(segment, AnyEnumCustom::String("some str: %".to_string()));
821+
822+
let mut path = Path::new("/%25");
823+
rdef.capture_match_info(&mut path);
824+
let de = PathDeserializer::new(&path);
825+
let segment: AnyEnumDerive = serde::Deserialize::deserialize(de).unwrap();
826+
assert_eq!(segment, AnyEnumDerive::String("%".to_string()));
827+
828+
// seq
829+
let rdef = ResourceDef::new("/{key}/{value}");
830+
831+
let mut path = Path::new("/other/123");
832+
rdef.capture_match_info(&mut path);
833+
let de = PathDeserializer::new(&path);
834+
let segment: (AnyEnumCustom, AnyEnumDerive) = serde::Deserialize::deserialize(de).unwrap();
835+
assert_eq!(segment.0, AnyEnumCustom::Other);
836+
assert_eq!(segment.1, AnyEnumDerive::Int(123));
837+
838+
// map
839+
#[derive(Deserialize)]
840+
struct Vals {
841+
key: AnyEnumCustom,
842+
value: AnyEnumDerive,
843+
}
844+
845+
let rdef = ResourceDef::new("/{key}/{value}");
846+
847+
let mut path = Path::new("/123/%2F");
848+
rdef.capture_match_info(&mut path);
849+
let de = PathDeserializer::new(&path);
850+
let vals: Vals = serde::Deserialize::deserialize(de).unwrap();
851+
assert_eq!(vals.key, AnyEnumCustom::Int(123));
852+
assert_eq!(vals.value, AnyEnumDerive::String("/".to_string()));
853+
}
854+
707855
#[test]
708856
fn deserialize_borrowed() {
709857
#[derive(Debug, Deserialize)]

0 commit comments

Comments
 (0)