1use std::collections::HashMap;
2use std::error::Error;
3use std::fmt;
4use std::sync::Arc;
5
6use iron::{Request, Response, BeforeMiddleware, Handler, IronResult, IronError};
7use iron::{status, method, headers};
8use iron::typemap::Key;
9use iron::modifiers::Redirect;
10
11use route_recognizer::Router as Recognizer;
12use route_recognizer::{Match, Params};
13
14#[derive(Clone)]
15pub struct MetaRouter {
16 routers: HashMap<method::Method, Recognizer<Box<String>>>,
17 wildcard: Recognizer<Box<String>>,
18}
19
20pub struct RouterInner {
21 pub routers: HashMap<method::Method, Recognizer<Box<Handler>>>,
23 pub wildcard: Recognizer<Box<Handler>>,
25 pub route_ids: HashMap<String, String>,
27 pub meta_router: MetaRouter,
29}
30
31pub struct Router {
34 inner: Arc<RouterInner>
35}
36
37pub struct RouteMatch {
38 pub params: Params,
39 pub route_id: String
40}
41
42impl MetaRouter {
43 fn recognize(&self, method: &method::Method, path: &str)
44 -> Option<Match<&Box<String>>> {
45 self.routers.get(method).and_then(|router| router.recognize(path).ok())
46 .or(self.wildcard.recognize(path).ok())
47 }
48
49 pub fn analyse_method(&self, req: &mut Request, path: &str) -> Option<RouteMatch> {
50 if let Some(matched) = self.recognize(&req.method, path) {
51 let ctx = RouteMatch {
52 params: matched.params,
53 route_id: matched.route_id
54 };
55 Some(ctx)
56 } else {
57 None
58 }
59 }
60}
61
62impl Router {
63 pub fn new() -> Router {
70 Router {
71 inner: Arc::new(RouterInner {
72 routers: HashMap::new(),
73 wildcard: Recognizer::new(),
74 route_ids: HashMap::new(),
75 meta_router: MetaRouter {
76 routers: HashMap::new(),
77 wildcard: Recognizer::new()
78 }
79 })
80 }
81 }
82
83 fn mut_inner(&mut self) -> &mut RouterInner {
84 Arc::get_mut(&mut self.inner).expect("Cannot modify router at this point.")
85 }
86
87 pub fn route<S: AsRef<str>, H: Handler>(&mut self, method: method::Method, glob: S, handler: H, route_id: &str) -> &mut Router {
111 self.mut_inner().routers
112 .entry(method.clone())
113 .or_insert(Recognizer::new())
114 .add(glob.as_ref(), route_id, Box::new(handler));
115 self.route_id(route_id.as_ref(), glob.as_ref());
116 self.mut_inner().meta_router.routers
117 .entry(method)
118 .or_insert(Recognizer::new())
119 .add(glob.as_ref(), route_id, Box::new("".to_string()));
120 self
121 }
122
123 fn route_id(&mut self, id: &str, glob: &str) {
124 let inner = self.mut_inner();
125 let ref mut route_ids = inner.route_ids;
126
127 match route_ids.get(id) {
128 Some(other_glob) if glob != other_glob => panic!("Duplicate route_id: {}", id),
129 _ => ()
130 };
131
132 route_ids.insert(id.to_owned(), glob.to_owned());
133 }
134
135 pub fn get<S: AsRef<str>, H: Handler>(&mut self, glob: S, handler: H, route_id: &str) -> &mut Router {
137 self.route(method::Get, glob, handler, route_id)
138 }
139
140 pub fn post<S: AsRef<str>, H: Handler>(&mut self, glob: S, handler: H, route_id: &str) -> &mut Router {
142 self.route(method::Post, glob, handler, route_id)
143 }
144
145 pub fn put<S: AsRef<str>, H: Handler>(&mut self, glob: S, handler: H, route_id: &str) -> &mut Router {
147 self.route(method::Put, glob, handler, route_id)
148 }
149
150 pub fn delete<S: AsRef<str>, H: Handler>(&mut self, glob: S, handler: H, route_id: &str) -> &mut Router {
152 self.route(method::Delete, glob, handler, route_id)
153 }
154
155 pub fn head<S: AsRef<str>, H: Handler>(&mut self, glob: S, handler: H, route_id: &str) -> &mut Router {
157 self.route(method::Head, glob, handler, route_id)
158 }
159
160 pub fn patch<S: AsRef<str>, H: Handler>(&mut self, glob: S, handler: H, route_id: &str) -> &mut Router {
162 self.route(method::Patch, glob, handler, route_id)
163 }
164
165 pub fn options<S: AsRef<str>, H: Handler>(&mut self, glob: S, handler: H, route_id: &str) -> &mut Router {
167 self.route(method::Options, glob, handler, route_id)
168 }
169
170 pub fn any<S: AsRef<str>, H: Handler>(&mut self, glob: S, handler: H, route_id: &str) -> &mut Router {
173 self.mut_inner().wildcard.add(glob.as_ref(), route_id, Box::new(handler));
174 self.route_id(route_id.as_ref(), glob.as_ref());
175 self
176 }
177
178 fn recognize(&self, method: &method::Method, path: &str)
179 -> Option<Match<&Box<Handler>>> {
180 self.inner.routers.get(method).and_then(|router| router.recognize(path).ok())
181 .or(self.inner.wildcard.recognize(path).ok())
182 }
183
184 fn handle_options(&self, path: &str) -> Response {
185 static METHODS: &'static [method::Method] =
186 &[method::Get, method::Post, method::Put,
187 method::Delete, method::Head, method::Patch];
188
189 let mut options = vec![];
191
192 for method in METHODS.iter() {
193 self.inner.routers.get(method).map(|router| {
194 if let Some(_) = router.recognize(path).ok() {
195 options.push(method.clone());
196 }
197 });
198 }
199 if options.contains(&method::Get) && !options.contains(&method::Head) {
201 options.push(method::Head);
202 }
203
204 let mut res = Response::with(status::Ok);
205 res.headers.set(headers::Allow(options));
206 res
207 }
208
209 fn redirect_slash(&self, req : &Request) -> Option<IronError> {
211 let mut url = req.url.clone();
212 let mut path = url.path().join("/");
213
214 if let Some(last_char) = path.chars().last() {
215 {
216 let mut path_segments = url.as_mut().path_segments_mut().unwrap();
217 if last_char == '/' {
218 path.pop();
220 path_segments.pop();
221 } else {
222 path.push('/');
224 path_segments.push("");
225 }
226 }
227 }
228
229 self.recognize(&req.method, &path).and(
230 Some(IronError::new(TrailingSlash,
231 (status::MovedPermanently, Redirect(url))))
232 )
233 }
234
235 fn handle_method(&self, req: &mut Request, path: &str) -> Option<IronResult<Response>> {
236 if let Some(matched) = self.recognize(&req.method, path) {
237 let ctx = RouteMatch {
238 params: matched.params,
239 route_id: matched.route_id
240 };
241 req.extensions.insert::<Router>(ctx);
242 req.extensions.insert::<RouterInner>(self.inner.clone());
243 Some(matched.handler.handle(req))
244 } else { self.redirect_slash(req).and_then(|redirect| Some(Err(redirect))) }
245 }
246
247 pub fn to_meta(&self) -> MetaRouter {
248 self.inner.meta_router.clone()
249 }
250}
251
252impl Key for Router { type Value = RouteMatch; }
253
254impl Key for RouterInner { type Value = Arc<RouterInner>; }
255
256impl Key for MetaRouter { type Value = RouteMatch; }
257
258impl Handler for Router {
259 fn handle(&self, req: &mut Request) -> IronResult<Response> {
260 let path = req.url.path().join("/");
261
262 self.handle_method(req, &path).unwrap_or_else(||
263 match req.method {
264 method::Options => Ok(self.handle_options(&path)),
265 method::Head => {
267 req.method = method::Get;
268 self.handle_method(req, &path).unwrap_or(
269 Err(IronError::new(NoRoute, status::NotFound))
270 )
271 }
272 _ => Err(IronError::new(NoRoute, status::NotFound))
273 }
274 )
275 }
276}
277
278impl BeforeMiddleware for MetaRouter {
279 fn before(&self, req: &mut Request) -> IronResult<()> {
280 let path = req.url.path().join("/");
281
282 match self.analyse_method(req, &path) {
283 None => Ok(()),
284 Some(ctx) => {
285 req.extensions.insert::<MetaRouter>(ctx);
286 Ok(())
287 }
288 }
289 }
290}
291
292#[derive(Debug, PartialEq, Eq)]
295pub struct NoRoute;
296
297impl fmt::Display for NoRoute {
298 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
299 f.write_str("No matching route found.")
300 }
301}
302
303impl Error for NoRoute {
304 fn description(&self) -> &str { "No Route" }
305}
306
307#[derive(Debug, PartialEq, Eq)]
310pub struct TrailingSlash;
311
312impl fmt::Display for TrailingSlash {
313 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314 f.write_str("The request had a trailing slash.")
315 }
316}
317
318impl Error for TrailingSlash {
319 fn description(&self) -> &str { "Trailing Slash" }
320}
321
322#[cfg(test)]
323mod test {
324 use super::Router;
325 use iron::{headers, method, status, Request, Response};
326
327 #[test]
328 fn test_route_id_correct() {
329 let mut router = Router::new();
330 router.post("/api/xyz", |_: &mut Request| {
331 Ok(Response::with((status::Ok, "")))
332 }, "a");
333
334 let mut router = Router::new();
335 router.post("/api/v2/xyz", |_: &mut Request| {
336 Ok(Response::with((status::Ok, "")))
337 }, "b");
338
339 let res = router.recognize(&method::Method::Post, "api/v2/xyz");
340 assert!(res.is_some());
341 let res = res.unwrap();
342 assert_eq!(res.route_id, "b");
343 }
344
345 #[test]
346 fn test_meta_route_id_correct() {
347 let mut router = Router::new();
348 router.get("/api/xyz", |_: &mut Request| {
349 Ok(Response::with((status::Ok, "")))
350 }, "a");
351
352 let mut router = Router::new();
353 router.get("/api/v2/xyz", |_: &mut Request| {
354 Ok(Response::with((status::Ok, "")))
355 }, "b");
356
357 let meta = router.to_meta();
358
359 let res = meta.recognize(&method::Method::Get, "api/v2/xyz");
360 assert!(res.is_some());
361 let res = res.unwrap();
362 assert_eq!(res.route_id, "b");
363 }
364
365 #[test]
366 fn test_handle_options_post() {
367 let mut router = Router::new();
368 router.post("/", |_: &mut Request| {
369 Ok(Response::with((status::Ok, "")))
370 }, "");
371 let resp = router.handle_options("/");
372 let headers = resp.headers.get::<headers::Allow>().unwrap();
373 let expected = headers::Allow(vec![method::Method::Post]);
374 assert_eq!(&expected, headers);
375 }
376
377 #[test]
378 fn test_handle_options_get_head() {
379 let mut router = Router::new();
380 router.get("/", |_: &mut Request| {
381 Ok(Response::with((status::Ok, "")))
382 }, "");
383 let resp = router.handle_options("/");
384 let headers = resp.headers.get::<headers::Allow>().unwrap();
385 let expected = headers::Allow(vec![method::Method::Get, method::Method::Head]);
386 assert_eq!(&expected, headers);
387 }
388
389 #[test]
390 fn test_handle_any_ok() {
391 let mut router = Router::new();
392 router.post("/post", |_: &mut Request| {
393 Ok(Response::with((status::Ok, "")))
394 }, "");
395 router.any("/post", |_: &mut Request| {
396 Ok(Response::with((status::Ok, "")))
397 }, "");
398 router.put("/post", |_: &mut Request| {
399 Ok(Response::with((status::Ok, "")))
400 }, "");
401 router.any("/get", |_: &mut Request| {
402 Ok(Response::with((status::Ok, "")))
403 }, "any");
404
405 assert!(router.recognize(&method::Get, "/post").is_some());
406 assert!(router.recognize(&method::Get, "/get").is_some());
407 }
408
409 #[test]
410 fn test_request() {
411 let mut router = Router::new();
412 router.post("/post", |_: &mut Request| {
413 Ok(Response::with((status::Ok, "")))
414 }, "");
415 router.get("/post", |_: &mut Request| {
416 Ok(Response::with((status::Ok, "")))
417 }, "");
418
419 assert!(router.recognize(&method::Post, "/post").is_some());
420 assert!(router.recognize(&method::Get, "/post").is_some());
421 assert!(router.recognize(&method::Put, "/post").is_none());
422 assert!(router.recognize(&method::Get, "/post/").is_none());
423 }
424
425 #[test]
426 fn test_not_found() {
427 let mut router = Router::new();
428 router.put("/put", |_: &mut Request| {
429 Ok(Response::with((status::Ok, "")))
430 }, "");
431 assert!(router.recognize(&method::Patch, "/patch").is_none());
432 }
433
434 #[test]
435 #[should_panic]
436 fn test_same_route_id() {
437 let mut router = Router::new();
438 router.put("/put", |_: &mut Request| {
439 Ok(Response::with((status::Ok, "")))
440 }, "my_route_id");
441 router.get("/get", |_: &mut Request| {
442 Ok(Response::with((status::Ok, "")))
443 }, "my_route_id");
444 }
445
446 #[test]
447 fn test_wildcard_regression() {
448 let mut router = Router::new();
449 router.options("*", |_: &mut Request| Ok(Response::with((status::Ok, ""))), "id1");
450 router.put("/upload/*filename", |_: &mut Request| Ok(Response::with((status::Ok, ""))), "id2");
451 assert!(router.recognize(&method::Options, "/foo").is_some());
452 assert!(router.recognize(&method::Put, "/foo").is_none());
453 assert!(router.recognize(&method::Put, "/upload/foo").is_some());
454 }
455}