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
use hyper::Method;
use std::collections::{BTreeMap, HashMap};
use request;
use request::Request;
use response::Response;
pub type Callback<T> = fn(&T, &mut Request, Response);
#[derive(Debug, Clone)]
enum Segment {
Fixed(String),
Variable(String)
}
#[derive(Debug)]
pub struct Route {
segments: Vec<Segment>
}
pub struct Router<T> {
routes: HashMap<Method, Vec<(Route, Callback<T>)>>
}
impl<T> Router<T> {
pub fn new() -> Router<T> {
Router {
routes: HashMap::new()
}
}
pub fn find_callback(&self, req: &mut Request) -> Option<Callback<T>> {
println!("path: {:?}", req.path());
if let Some(routes) = self.routes.get(req.method()) {
let mut params = BTreeMap::new();
'top: for &(ref route, ref callback) in routes.iter() {
println!("route: {:?}", route);
let mut it_route = route.segments.iter();
for actual in req.path() {
match it_route.next() {
Some(&Segment::Fixed(ref fixed)) if fixed != actual => continue 'top,
Some(&Segment::Variable(ref name)) => {
params.insert(name.to_owned(), actual.to_string());
},
_ => ()
}
}
if it_route.next().is_none() {
request::set_params(req, params);
return Some(*callback);
}
params.clear();
}
println!("no route matching method {} path {:?}", req.method(), req.path());
} else {
println!("no routes registered for method {}", req.method());
}
None
}
pub fn insert(&mut self, method: Method, route: Route, callback: Callback<T>) {
println!("register callback for route: {:?}", route);
self.routes.entry(method).or_insert(Vec::new()).push((route, callback));
}
}
impl<'a> Into<Route> for &'a str {
fn into(self) -> Route {
if self.len() == 0 {
panic!("route must not be empty");
}
if &self[0..1] != "/" {
panic!("route must begin with a slash");
}
let stripped = &self[1..];
let route = Route {
segments:
stripped.split('/').map(|segment| if segment.len() > 0 && &segment[0..1] == ":" {
Segment::Variable(segment[1..].to_owned())
} else {
Segment::Fixed(segment.to_owned())
}
).collect::<Vec<Segment>>()
};
println!("into from {} to {:?}", self, route);
route
}
}