1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use lazy_static::lazy_static;
use phf::{phf_map, Map};
use regex::Regex;
use std::borrow::Cow;
lazy_static! {
static ref SEGMENT_MATCH: Regex = Regex::new(r"^\{(?::(?P<kind>[a-zA-Z]\w*))?\}$").unwrap();
}
static MATCH_KINDS: Map<&'static str, &'static str> = phf_map! {
"string" => r"([^/]+)",
"int" => r"([-+]?\d+)",
"uint" => r"(\d+)",
"uuid" => r"([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})",
};
#[derive(Debug, Clone)]
pub struct Route<M, H> {
pub(super) path: Cow<'static, str>,
pub(super) method: M,
pub(super) handler: H,
pub(super) pattern: Regex,
}
impl<M, H> Route<M, H> {
pub fn new<P>(path: P, method: M, handler: H) -> Route<M, H>
where
P: Into<Cow<'static, str>>,
{
let path = path.into();
let compile = parse(path.as_ref());
Route {
path,
method,
handler,
pattern: compile,
}
}
}
fn parse(path: &str) -> Regex {
let normalized = crate::normalize_url(path);
let split = normalized.split("/").skip(1);
let mut pattern = split
.map(|part| {
if let Some(cap) = SEGMENT_MATCH.captures(part) {
let name = cap.name("kind").map(|m| m.as_str()).unwrap_or("string");
Cow::Borrowed(MATCH_KINDS.get(name).map(|v| *v).unwrap_or(r"([^/]*)"))
} else {
Cow::Owned(regex::escape(part))
}
})
.fold(String::from("^"), |mut acc, el| {
acc.push('/');
acc.push_str(el.as_ref());
acc
});
pattern.push('$');
Regex::new(&pattern).unwrap()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_route_parse() {
fn assert_path(given: &str, expected: &str) {
assert_eq!(parse(given).as_str(), expected)
}
assert_path("/some/path", r"^/some/path$");
assert_path("/some/{:string}", r"^/some/([^/]+)$");
assert_path("/some/{:int}", r"^/some/([-+]?\d+)$");
assert_path("/some/{:uint}", r"^/some/(\d+)$");
assert_path(
"/some/{:uuid}",
r"^/some/([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$",
);
}
}