1extern crate iron;
59
60use iron::prelude::*;
61use iron::status;
62use iron::request::Body;
63use std::collections::HashMap;
64use std::hash::{Hash, Hasher};
65use std::cmp::{PartialEq, Eq};
66use std::option::Option::None;
67
68
69#[cfg(test)]
70mod tests {
71
72 use Router;
73 use std::collections::HashMap;
74 use iron::prelude::*;
75 use iron::status;
76 use iron::request::Body;
77 use std::io::Read;
78
79 fn test_handle(vars: HashMap<String, String>, body: &mut Body) -> IronResult<Response>
80 {
81 let mut string = "Vars:".to_owned();
82
83 for (x, y) in &vars {
84 string.push_str(&format!("\n{} -> {}", x, y));
85 }
86
87 string.push_str("\n");
88
89 let mut buf: String = "".to_owned();
91 let res = body.read_to_string(&mut buf);
92 string.push_str(&buf[..]);
93
94 Ok (
95 Response::with((status::Ok, string))
96 )
97
98 }
99
100 #[test]
101 fn test_router()
102 {
103
104 let mut r = Router::new();
105
106 r.get("/id/:album", test_handle);
107
108 let handle = move |_req: &mut Request| -> IronResult<Response> {
109 return r.handle(_req);
110 };
111
112 }
116}
117
118
119
120pub struct MatchableUrlResult {
121
122 matches: bool,
123 variables: Option<HashMap<String, String>>
124
125}
126
127pub struct MatchableUrl {
128 url: String,
129 components: Vec<(String, bool)>
130}
131
132impl Hash for MatchableUrl {
133 fn hash<H: Hasher>(&self, state: &mut H) {
134 self.url.hash(state);
135 self.components.hash(state);
136 }
137}
138
139impl PartialEq for MatchableUrl {
140 fn eq(&self, other: &MatchableUrl) -> bool {
141 return self.url == other.url;
142 }
143}
144
145impl Eq for MatchableUrl {}
146
147impl MatchableUrl {
148
149 pub fn new(url: &str) -> MatchableUrl
157 {
158
159 let split = url[1..].split("/");
160 let mut comp : Vec<(String, bool)> = Vec::new();
161
162 for s in split {
163 if s.find(":") == None {
164 comp.push((s.to_owned(), true));
165 } else {
166 comp.push((s.to_owned(), false));
167 }
168 }
169
170 return MatchableUrl { url: url.to_owned(), components: comp };
171
172 }
173
174 pub fn _match(&self, request_path: Vec<&str>) -> MatchableUrlResult
177 {
178
179 let size = request_path.len();
181
182 if size != self.components.len() {
183 return MatchableUrlResult { matches: false, variables: None }
184 }
185
186 let mut index = 0;
187 let mut vars : HashMap<String, String> = HashMap::new();
188
189 for s in request_path {
190 let (ref name, ref exac) = self.components[index];
191
192 if *exac {
193 if name != s {
194 return MatchableUrlResult { matches: false, variables: None }
195 }
196 } else {
197 vars.insert(name[1..].to_owned(), s.to_owned());
198 }
199
200 index += 1;
201 }
202
203 return MatchableUrlResult { matches: true, variables: Some(vars) };
204
205 }
206
207}
208
209pub struct Router {
210
211 routes: HashMap<MatchableUrl, fn(HashMap<String, String>, &mut Body) -> IronResult<Response>>
212
213}
214
215impl Router {
216
217 pub fn new() -> Router
219 {
220 return Router { routes: HashMap::new() };
221 }
222
223 pub fn handle(&self, _req: &mut Request) -> IronResult<Response>
226 {
227
228 for (m_url, f) in &self.routes {
229 let path = _req.url.path();
230 let res = m_url._match(path);
231 let b = &mut _req.body;
232 if res.matches {
233 return f(res.variables.unwrap(), b);
234 }
235 }
236
237 Ok(
238 Response::with((status:: Ok, "No match."))
239 )
240
241 }
242
243 pub fn get(&mut self, path: &str, f: fn(HashMap<String, String>, &mut Body) -> IronResult<Response>)
251 {
252 let url = MatchableUrl::new(path);
253 self.routes.insert(url, f);
254 }
255
256}