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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use {
crate::{util::percent_decode, Method, Request, Response},
std::cmp::max,
std::collections::HashMap,
};
pub type Pattern = Vec<String>;
pub type Action = fn(Request) -> Response;
#[derive(Default)]
pub struct Router {
routes: HashMap<Method, Vec<(Pattern, Action)>>,
}
impl Router {
pub fn new() -> Router {
Router {
routes: HashMap::new(),
}
}
pub fn action_for(&self, req: &mut Request) -> Option<&Action> {
if let Some(routes) = self.routes.get(&req.method().into()) {
let req_parts = Self::pattern_to_vec(req.path());
'outer: for (pattern, action) in routes {
for i in 0..max(req_parts.len(), pattern.len()) {
if i >= pattern.len() {
continue 'outer;
}
let pattern_part = &pattern[i];
if i >= req_parts.len() {
continue 'outer;
}
let req_part = &req_parts[i];
if pattern_part.starts_with(':') && !req_part.is_empty() {
if let Some(decoded) = percent_decode(req_part) {
req.set_arg(pattern_part.trim_start_matches(':').into(), decoded);
}
continue;
} else if pattern_part.starts_with('*') && !req_part.is_empty() {
if let Some(idx) = req.path().find(&req_parts[i]) {
if let Some(decoded) = percent_decode(&req.path()[idx..]) {
req.set_arg(pattern_part.trim_start_matches('*').into(), decoded);
}
}
return Some(action);
} else if req_part == pattern_part {
continue;
} else {
continue 'outer;
}
}
return Some(action);
}
}
None
}
fn pattern_to_vec(pattern: &str) -> Pattern {
pattern
.trim_matches('/')
.split('/')
.flat_map(|s| {
if let Some(idx) = s.find('.') {
vec![s[..idx].to_string(), format!("{}", &s[idx..])]
} else {
vec![s.to_string()]
}
})
.collect::<Vec<_>>()
}
pub fn insert<T: Into<Method>>(&mut self, method: T, pattern: &'static str, action: Action) {
let method = method.into();
let pattern_parts = Self::pattern_to_vec(pattern);
if let Some(map) = self.routes.get_mut(&method) {
map.push((pattern_parts, action));
} else {
self.routes.insert(method, vec![(pattern_parts, action)]);
}
}
}