sapper/router_m/
router.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4
5use request::SapperRequest;
6use response::SapperResponse;
7use handler::SapperHandler;
8use app::Result;
9use app::Error;
10use app::PathParams;
11use app::Key;
12use hyper::method::Method;
13
14use recognizer::Router as Recognizer;
15use recognizer::{Match, Params};
16
17impl Key for PathParams { type Value = Params; }
18
19
20/// `Router` provides an interface for creating complex routes as middleware
21/// for the Iron framework.
22pub struct Router {
23    // The routers, specialized by method.
24    routers: HashMap<Method, Recognizer<Arc<Box<dyn SapperHandler>>>>,
25    // Routes that accept any method.
26    wildcard: Recognizer<Arc<Box<dyn SapperHandler>>>
27}
28
29impl Router {
30    pub fn new() -> Router {
31	Router {
32	    routers: HashMap::new(),
33	    wildcard: Recognizer::new()
34	}
35    }
36
37    pub fn route<S>(&mut self, method: Method,
38		       glob: S, handler: Arc<Box<dyn SapperHandler>>) -> &mut Router
39    where S: AsRef<str> {
40	self.routers.entry(method).or_insert(Recognizer::new())
41		    .add(glob.as_ref(), handler);
42	self
43    }
44
45
46    fn recognize(&self, method: &Method, path: &str)
47		     -> Option<Match<&Arc<Box<dyn SapperHandler>>>> {
48	self.routers.get(method).and_then(|router| router.recognize(path).ok())
49	    .or(self.wildcard.recognize(path).ok())
50    }
51
52    // fn handle_options(&self, path: &str) -> Response {
53    //     static METHODS: &'static [method::Method] =
54    //         &[method::Get, method::Post, method::Post, method::Put,
55    //           method::Delete, method::Head, method::Patch];
56
57    //     // Get all the available methods and return them.
58    //     let mut options = vec![];
59
60    //     for method in METHODS.iter() {
61    //         self.routers.get(method).map(|router| {
62    //             if let Some(_) = router.recognize(path).ok() {
63    //                 options.push(method.clone());
64    //             }
65    //         });
66    //     }
67    //     // If GET is there, HEAD is also there.
68    //     if options.contains(&method::Get) && !options.contains(&method::Head) {
69    //         options.push(method::Head);
70    //     }
71
72    //     let mut res = Response::with(status::StatusCode::Ok);
73    //     res.headers.set(headers::Allow(options));
74    //     res
75    // }
76
77    // Tests for a match by adding or removing a trailing slash.
78    // fn redirect_slash(&self, req : &Request) -> Option<Error> {
79    //     let mut url = req.url.clone();
80    //     let mut path = url.path.join("/");
81
82    //     if let Some(last_char) = path.chars().last() {
83    //         if last_char == '/' {
84    //             path.pop();
85    //             url.path.pop();
86    //         } else {
87    //             path.push('/');
88    //             url.path.push(String::new());
89    //         }
90    //     }
91
92    //     self.recognize(&req.method(), &path).and(
93    //         Some(Error::new(TrailingSlash,
94    //                             (status::MovedPermanently, Redirect(url))))
95    //     )
96    // }
97
98    pub fn handle_method(&self, req: &mut SapperRequest, path: &str) -> Result<SapperResponse> {
99	if let Some(matched) = self.recognize(req.method(), path) {
100	    req.ext_mut().insert::<PathParams>(matched.params);
101	    matched.handler.handle(req)
102	} else {
103	    // panic!("router not matched!");
104	    // self.redirect_slash(req).and_then(|redirect| Some(Err(redirect)))
105	    Err(Error::NotFound)
106	}
107    }
108}