Support affixed typed path captures (#3782)

This commit is contained in:
Mochammad Farros Fatchur Roji
2026-06-10 21:24:35 +02:00
committed by GitHub
parent 42d6fdc80d
commit 5b52bcdcfa
2 changed files with 131 additions and 18 deletions
+52 -18
View File
@@ -362,8 +362,7 @@ fn format_str_from_path(segments: &[Segment]) -> String {
Segment::Capture(capture, _) => format!("{{{capture}}}"),
Segment::Static(segment) => segment.to_owned(),
})
.collect::<Vec<_>>()
.join("/")
.collect()
}
fn captures_from_path(segments: &[Segment]) -> Vec<syn::Ident> {
@@ -387,23 +386,58 @@ fn parse_path(path: &LitStr) -> syn::Result<Vec<Segment>> {
return Err(syn::Error::new_spanned(path, "paths must start with a `/`"));
}
path.value()
.split('/')
.map(|segment| {
if let Some(capture) = segment
.strip_prefix('{')
.and_then(|segment| segment.strip_suffix('}'))
.and_then(|segment| {
(!segment.starts_with('{') && !segment.ends_with('}')).then_some(segment)
})
.map(|capture| capture.strip_prefix('*').unwrap_or(capture))
{
Ok(Segment::Capture(capture.to_owned(), path.span()))
} else {
Ok(Segment::Static(segment.to_owned()))
let mut segments = Vec::new();
let mut rest = value.as_str();
while let Some(start) = find_first_not_double(b'{', rest.as_bytes()) {
if start > 0 {
segments.push(Segment::Static(rest[..start].to_owned()));
}
rest = &rest[start + 1..];
if let Some(end) = rest.find('}') {
let capture = &rest[..end];
if capture.is_empty() || capture.contains('{') {
return Err(syn::Error::new_spanned(path, "invalid capture in path"));
}
})
.collect()
segments.push(Segment::Capture(
capture.strip_prefix('*').unwrap_or(capture).to_owned(),
path.span(),
));
rest = &rest[end + 1..];
} else {
segments.push(Segment::Static(format!("{{{rest}")));
rest = "";
}
}
if !rest.is_empty() {
segments.push(Segment::Static(rest.to_owned()));
}
Ok(segments)
}
fn find_first_not_double(needle: u8, haystack: &[u8]) -> Option<usize> {
let mut possible_capture = 0;
while let Some(index) = haystack
.get(possible_capture..)
.and_then(|haystack| haystack.iter().position(|byte| byte == &needle))
{
let index = index + possible_capture;
if haystack.get(index + 1) == Some(&needle) {
possible_capture = index + 2;
continue;
}
return Some(index);
}
None
}
enum Segment {