1use std::collections::HashMap;
2use std::error::Error;
3use std::fmt;
4use std::sync::Arc;
5
6use ferrum::{Request, Response, Handler, FerrumResult, FerrumError};
7use ferrum::{header, Method, StatusCode};
8use ferrum::typemap::Key;
9
10use recognizer::{Glob, GlobTypes, Recognizer, Recognize, RouteMatch, Params};
11
12pub mod id;
13pub use self::id::*;
14
15pub struct RouterInner {
16 pub routers: HashMap<Method, Vec<Arc<Recognizer>>>,
18
19 pub wildcard: Vec<Arc<Recognizer>>,
21
22 pub route_ids: HashMap<Id, (String, Arc<Recognizer>)>,
24}
25
26pub struct Router {
29 inner: Arc<RouterInner>
30}
31
32impl Router {
33 pub fn new() -> Router {
40 Router {
41 inner: Arc::new(RouterInner {
42 routers: HashMap::new(),
43 wildcard: Vec::new(),
44 route_ids: HashMap::new(),
45 })
46 }
47 }
48
49 fn mut_inner(&mut self) -> &mut RouterInner {
50 Arc::get_mut(&mut self.inner).expect("Cannot modify router at this point.")
51 }
52
53 pub fn route<G, H, S, T>(&mut self, method: Method, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
77 where G: Into<Glob<S, T>>,
78 H: Handler,
79 S: AsRef<[u8]>,
80 T: GlobTypes,
81 {
82 let glob = glob.into();
83 let types = glob.types().map(|types| types.store());
84 let recognizer = Arc::new(
85 Recognizer::new(glob.path(), Box::new(handler), types).unwrap()
86 );
87
88 if let Some(route_id) = route_id {
89 self.route_id(route_id, glob.path(), recognizer.clone());
90 }
91
92 self.mut_inner().routers
93 .entry(method)
94 .or_insert(Vec::new())
95 .push(recognizer);
96 self
97 }
98
99 fn route_id(&mut self, id: Id, glob_path: &[u8], recognizer: Arc<Recognizer>) {
100 let inner = self.mut_inner();
101 let ref mut route_ids = inner.route_ids;
102
103 match route_ids.get(&id) {
104 Some(&(ref other_glob_path, _)) if glob_path != other_glob_path.as_bytes() =>
105 panic!("Duplicate route_id: {}", id),
106 _ => ()
107 };
108
109 route_ids.insert(id, (String::from_utf8_lossy(glob_path).to_string(), recognizer));
110 }
111
112 pub fn get<G, H, S, T>(&mut self, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
114 where G: Into<Glob<S, T>>,
115 H: Handler,
116 S: AsRef<[u8]>,
117 T: GlobTypes,
118 {
119 self.route(Method::Get, glob, handler, route_id)
120 }
121
122 pub fn post<G, H, S, T>(&mut self, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
124 where G: Into<Glob<S, T>>,
125 H: Handler,
126 S: AsRef<[u8]>,
127 T: GlobTypes,
128 {
129 self.route(Method::Post, glob, handler, route_id)
130 }
131
132 pub fn put<G, H, S, T>(&mut self, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
134 where G: Into<Glob<S, T>>,
135 H: Handler,
136 S: AsRef<[u8]>,
137 T: GlobTypes,
138 {
139 self.route(Method::Put, glob, handler, route_id)
140 }
141
142 pub fn delete<G, H, S, T>(&mut self, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
144 where G: Into<Glob<S, T>>,
145 H: Handler,
146 S: AsRef<[u8]>,
147 T: GlobTypes,
148 {
149 self.route(Method::Delete, glob, handler, route_id)
150 }
151
152 pub fn head<G, H, S, T>(&mut self, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
154 where G: Into<Glob<S, T>>,
155 H: Handler,
156 S: AsRef<[u8]>,
157 T: GlobTypes,
158 {
159 self.route(Method::Head, glob, handler, route_id)
160 }
161
162 pub fn patch<G, H, S, T>(&mut self, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
164 where G: Into<Glob<S, T>>,
165 H: Handler,
166 S: AsRef<[u8]>,
167 T: GlobTypes,
168 {
169 self.route(Method::Patch, glob, handler, route_id)
170 }
171
172 pub fn options<G, H, S, T>(&mut self, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
174 where G: Into<Glob<S, T>>,
175 H: Handler,
176 S: AsRef<[u8]>,
177 T: GlobTypes,
178 {
179 self.route(Method::Options, glob, handler, route_id)
180 }
181
182 pub fn any<G, H, S, T>(&mut self, glob: G, handler: H, route_id: Option<Id>) -> &mut Router
185 where G: Into<Glob<S, T>>,
186 H: Handler,
187 S: AsRef<[u8]>,
188 T: GlobTypes,
189 {
190 let glob = glob.into();
191 let types = glob.types().map(|types| types.store());
192 let recognizer = Arc::new(
193 Recognizer::new(glob.path(), Box::new(handler), types).unwrap()
194 );
195
196 if let Some(route_id) = route_id {
197 self.route_id(route_id, glob.path(), recognizer.clone());
198 }
199
200 self.mut_inner().wildcard.push(recognizer);
201 self
202 }
203
204 fn recognize(&self, method: &Method, path: &str) -> Option<RouteMatch> {
205 self.inner.routers
206 .get(method)
207 .and_then(|recognizers| recognizers.recognize(path))
208 .or(self.inner.wildcard.recognize(path))
209 }
210
211 fn handle_options(&self, path: &str) -> Response {
212 static METHODS: &'static [Method] = &[
213 Method::Get,
214 Method::Post,
215 Method::Put,
216 Method::Delete,
217 Method::Head,
218 Method::Patch
219 ];
220
221 let mut options = vec![];
223
224 for method in METHODS.iter() {
225 self.inner.routers.get(method).map(|recognizers| {
226 if let Some(_) = recognizers.recognize(path) {
227 options.push(method.clone());
228 }
229 });
230 }
231 if options.contains(&Method::Get) && !options.contains(&Method::Head) {
233 options.push(Method::Head);
234 }
235
236 let mut response = Response::new().with_status(StatusCode::Ok);
237 response.headers.set(header::Allow(options));
238 response
239 }
240
241 fn handle_method(&self, request: &mut Request) -> Option<FerrumResult<Response>> {
242 if let Some(matched) = self.recognize(&request.method, request.uri.path()) {
243 request.extensions.insert::<Router>(matched.params);
244 request.extensions.insert::<RouterInner>(self.inner.clone());
245 Some(matched.handler.handle(request))
246 } else {
247 None
248 }
249 }
250}
251
252impl Key for Router {
253 type Value = Params;
254}
255
256impl Key for RouterInner {
257 type Value = Arc<RouterInner>;
258}
259
260impl Handler for Router {
261 fn handle(&self, request: &mut Request) -> FerrumResult<Response> {
262 self.handle_method(request).unwrap_or_else(||
263 match request.method {
264 Method::Options => Ok(self.handle_options(request.uri.path())),
265 Method::Head => {
267 request.method = Method::Get;
268 self.handle_method(request).unwrap_or(
269 Err(
270 FerrumError::new(
271 NoRoute,
272 Some(Response::new()
273 .with_status(StatusCode::NotFound))
274 )
275 )
276 )
277 }
278 _ => Err(
279 FerrumError::new(
280 NoRoute,
281 Some(Response::new()
282 .with_status(StatusCode::NotFound))
283 )
284 )
285 }
286 )
287 }
288}
289
290#[derive(Debug, PartialEq, Eq)]
293pub struct NoRoute;
294
295impl fmt::Display for NoRoute {
296 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297 f.write_str("No matching route found.")
298 }
299}
300
301impl Error for NoRoute {
302 fn description(&self) -> &str { "No Route" }
303}
304
305#[cfg(test)]
306mod tests;