feat(path): deserialize wildcard captures into Vec by splitting on / - #3828
feat(path): deserialize wildcard captures into Vec by splitting on /#3828patelshudhanshu1999-maker wants to merge 1 commit into
Conversation
When a route has a single path parameter (typically a wildcard like
`{*path}`), the `Path` extractor can now deserialize it into a
`Vec<T>` by splitting the captured value on `/` and skipping empty
segments. This works for:
- Top-level `Path<Vec<String>>` and `Path<Vec<T>>`
- Struct fields: `struct Params { key: Vec<String> }`
- Tuple elements: `(String, Vec<String>)`
Previously a top-level `Vec` deserialized a single capture into one
element, and struct/tuple `Vec` fields failed with an unsupported-type
error.
Fixes tokio-rs#3621
Samielakkad
left a comment
There was a problem hiding this comment.
There is an inconsistency in the all-empty-segment case for a single capture.
The docs say sequence deserialization splits the decoded capture on / and skips empty segments, and the struct-field test follows that behavior:
struct Params { key: Vec<String> }
// key = "/" => key: vec![]But top-level Path<Vec<String>> currently falls back to the old ?one capture becomes one element? path when the split has no non-empty segment:
// value = "/" or "//" => vec!["/".to_owned()] / vec!["//".to_owned()]That comes from PathDeserializer::deserialize_seq: it only constructs SingleParamSeqAccess after finding a non-empty first segment, otherwise it falls through to SeqDeserializer.
This can show up with a decoded %2F / %2F%2F capture, and it means Path<Vec<T>> and Path<StructWithVec> disagree for the same captured value. I think the top-level sequence path should commit to split-mode whenever there is exactly one capture, even if the resulting segment list is empty, or the docs should explicitly call out the fallback behavior.
|
Why was previous work in #3680 not pointed out and what complexity is this solving on top of that? |
Fixes #3621
Motivation
When a route uses a wildcard capture like
/{*path}, there's no ergonomic way to extract the individual path segments. APath<Vec<String>>deserializes a single capture into a one-element vector containing the entire matched string, and using aVec<T>inside a struct field (e.g.,struct Params { key: Vec<String> }) fails with an unsupported-type rejection.Users working with file-system-like routes (
/files/{*path}) or S3-style APIs (/{bucket}/{*key}) need to manually split the captured string, which is boilerplate-heavy and error-prone.Solution
When a route has a single path parameter (typically a wildcard), the
Pathextractor now splits the captured value on/and deserializes each non-empty segment individually into the target sequence type. This works for:Path<Vec<String>>,Path<Vec<i32>>, etc.struct Params { bucket: String, key: Vec<String> }(String, Vec<String>)Key design decisions
Vec<(String, String)>is unaffected — it continues to deserialize into(name, value)pairs of all captures, preserving backward compatibility.matchitdelivers percent-decoded values, an encoded/(%2F) also acts as a separator./), the behavior depends on context: in a struct field the result is an emptyVec, while at the top level the original single-element behavior is preserved.