1use std::future::Future;
16use crate::response::HttpResponse;
17use async_trait::async_trait;
18use fnv::FnvHashMap;
19use hyper::{Method, Response, StatusCode};
20use route_recognizer::{Params, Router as InternalRouter};
21
22type HyperResponse = hyper::Response<hyper::Body>;
23
24pub struct Router {
25 method_map: FnvHashMap<Method, InternalRouter<Box<dyn Handler>>>,
26}
27
28#[async_trait]
29pub trait Handler: Send + Sync + 'static {
30 async fn invoke(&self) -> HyperResponse;
31}
32
33#[async_trait]
34impl<F: Send + Sync + 'static, Fut> Handler for F
35where
36 F: Fn() -> Fut,
37 Fut: Future + Send + 'static,
38 Fut::Output: IntoResponse,
39{
40 async fn invoke(&self) -> HyperResponse {
41 (self)().await.into_response()
42 }
43}
44
45pub trait IntoResponse: Send + Sized {
46 fn into_response(self) -> HyperResponse;
47}
48
49impl IntoResponse for HyperResponse {
50 fn into_response(self) -> HyperResponse {
51 self
52 }
53}
54
55impl IntoResponse for HttpResponse {
56 fn into_response(self) -> HyperResponse {
57 self.res()
58 }
59}
60
61pub struct RouterMatch<'a> {
62 pub handler: &'a dyn Handler,
63 pub params: Params,
64}
65
66impl Default for Router {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl Router {
73 pub fn new() -> Self {
74 Self {
75 method_map: FnvHashMap::default(),
76 }
77 }
78
79 pub fn add(&mut self, path: &str, method: Method, handler: Box<dyn Handler>) {
80 self.method_map
81 .entry(method)
82 .or_insert_with(InternalRouter::new)
83 .add(path, handler);
84 }
85
86 pub fn route(&self, path: &str, method: &Method) -> RouterMatch {
87 if let Some(m) = self
88 .method_map
89 .get(method)
90 .and_then(|r| r.recognize(path).ok())
91 {
92 let mut params = Params::new();
93 params.clone_from(m.params());
94 RouterMatch {
95 handler: &***m.handler(),
96 params,
97 }
98 } else {
99 RouterMatch {
100 handler: ¬_found_handler,
101 params: Params::new(),
102 }
103 }
104 }
105}
106
107async fn not_found_handler() -> HyperResponse {
108 Response::builder()
109 .status(StatusCode::NOT_FOUND)
110 .body("NOT FOUND".into())
111 .unwrap()
112}