1use std::future::Future;
3use std::sync::RwLock;
4use std::{collections::HashMap, sync::Arc};
5
6use http::{Method, Request, Response};
7use matchit::Router as MatchRouter;
8
9use crate::handler::*;
10
11pub use crate::service::ResponseFuture;
12
13mod service;
14
15#[derive(Default)]
16struct Route<Body, Data, Error> {
17 handlers: HashMap<Method, AsyncUnsyncHandler<Body, Data, Error>>,
18 catchall: Option<AsyncUnsyncHandler<Body, Data, Error>>,
19}
20
21#[derive(Default)]
43pub struct Router<Body, Data: Clone, Error> {
44 inner: Arc<RwLock<MatchRouter<Route<Body, Data, Error>>>>,
45 data: Data,
46}
47
48impl<Body, Error> Router<Body, (), Error> {
49 pub fn new() -> Self {
51 Self {
52 inner: Default::default(),
53 data: (),
54 }
55 }
56}
57
58impl<Body, Data, Error> Router<Body, Data, Error>
59where
60 Body: 'static,
61 Data: Clone + 'static,
62 Error: 'static,
63{
64 pub fn with_data(data: Data) -> Self {
87 Self {
88 inner: Default::default(),
89 data,
90 }
91 }
92
93 pub fn get<HandlerFn, Fut>(self, path: impl AsRef<str>, handler: HandlerFn) -> Self
95 where
96 HandlerFn: Fn(Request<Body>, RouteContext<Data>) -> Fut,
97 HandlerFn: 'static,
98 Fut: Future<Output = Result<Response<Body>, Error>> + 'static,
99 {
100 self.insert_handler(path, Method::GET, handler)
101 }
102
103 pub fn post<HandlerFn, Fut>(self, path: impl AsRef<str>, handler: HandlerFn) -> Self
105 where
106 HandlerFn: Fn(Request<Body>, RouteContext<Data>) -> Fut,
107 HandlerFn: 'static,
108 Fut: Future<Output = Result<Response<Body>, Error>> + 'static,
109 {
110 self.insert_handler(path, Method::POST, handler)
111 }
112
113 pub fn put<HandlerFn, Fut>(self, path: impl AsRef<str>, handler: HandlerFn) -> Self
115 where
116 HandlerFn: Fn(Request<Body>, RouteContext<Data>) -> Fut,
117 HandlerFn: 'static,
118 Fut: Future<Output = Result<Response<Body>, Error>> + 'static,
119 {
120 self.insert_handler(path, Method::PUT, handler)
121 }
122
123 pub fn delete<HandlerFn, Fut>(self, path: impl AsRef<str>, handler: HandlerFn) -> Self
125 where
126 HandlerFn: Fn(Request<Body>, RouteContext<Data>) -> Fut,
127 HandlerFn: 'static,
128 Fut: Future<Output = Result<Response<Body>, Error>> + 'static,
129 {
130 self.insert_handler(path, Method::DELETE, handler)
131 }
132
133 pub fn head<HandlerFn, Fut>(self, path: impl AsRef<str>, handler: HandlerFn) -> Self
135 where
136 HandlerFn: Fn(Request<Body>, RouteContext<Data>) -> Fut,
137 HandlerFn: 'static,
138 Fut: Future<Output = Result<Response<Body>, Error>> + 'static,
139 {
140 self.insert_handler(path, Method::HEAD, handler)
141 }
142
143 pub fn options<HandlerFn, Fut>(self, path: impl AsRef<str>, handler: HandlerFn) -> Self
145 where
146 HandlerFn: Fn(Request<Body>, RouteContext<Data>) -> Fut,
147 HandlerFn: 'static,
148 Fut: Future<Output = Result<Response<Body>, Error>> + 'static,
149 {
150 self.insert_handler(path, Method::DELETE, handler)
151 }
152
153 pub fn patch<HandlerFn, Fut>(self, path: impl AsRef<str>, handler: HandlerFn) -> Self
155 where
156 HandlerFn: Fn(Request<Body>, RouteContext<Data>) -> Fut,
157 HandlerFn: 'static,
158 Fut: Future<Output = Result<Response<Body>, Error>> + 'static,
159 {
160 self.insert_handler(path, Method::PATCH, handler)
161 }
162
163 pub fn any<HandlerFn, Fut>(self, path: impl AsRef<str>, handler: HandlerFn) -> Self
165 where
166 HandlerFn: Fn(Request<Body>, RouteContext<Data>) -> Fut,
167 HandlerFn: 'static,
168 Fut: Future<Output = Result<Response<Body>, Error>> + 'static,
169 {
170 let mut inner = self.inner.write().unwrap();
171
172 if let Ok(existing) = inner.at_mut(path.as_ref()) {
173 existing.value.catchall = Some(handler.into());
174 } else {
175 inner
176 .insert(
177 path.as_ref(),
178 Route {
179 handlers: HashMap::new(),
180 catchall: Some(handler.into()),
181 },
182 )
183 .expect("unable to add route to router");
184 }
185
186 drop(inner);
187
188 self
189 }
190
191 fn insert_handler<H>(self, path: impl AsRef<str>, method: Method, handler: H) -> Self
192 where
193 H: Into<AsyncUnsyncHandler<Body, Data, Error>>,
194 {
195 let mut inner = self.inner.write().unwrap();
196 if let Ok(existing) = inner.at_mut(path.as_ref()) {
197 existing.value.handlers.insert(method, handler.into());
198 } else {
199 let mut handlers: HashMap<Method, AsyncUnsyncHandler<Body, Data, Error>> =
200 HashMap::new();
201 handlers.insert(method, handler.into());
202
203 inner
204 .insert(
205 path.as_ref(),
206 Route {
207 handlers,
208 catchall: None,
209 },
210 )
211 .expect("unable to add route to router");
212 }
213
214 drop(inner);
215
216 self
217 }
218}
219
220impl<Body, Data, Error> Clone for Router<Body, Data, Error>
221where
222 Data: Clone,
223{
224 fn clone(&self) -> Self {
225 Self {
226 inner: self.inner.clone(),
227 data: self.data.clone(),
228 }
229 }
230}
231
232#[derive(Debug)]
234pub struct RouteContext<T> {
235 pub data: T,
237 params: HashMap<String, String>,
238}
239
240impl<T> RouteContext<T> {
241 pub fn param(&self, name: impl AsRef<str>) -> Option<&str> {
243 self.params.get(name.as_ref()).map(|s| s.as_str())
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use std::{
250 convert::Infallible,
251 sync::{
252 atomic::{AtomicBool, Ordering},
253 Arc,
254 },
255 };
256
257 use http::{Method, Request, Response};
258 use tower::Service;
259
260 use crate::unsync::Router;
261
262 #[test]
263 fn not_found() {
264 futures::executor::block_on(async move {
265 let mut router: Router<(), (), Infallible> = Router::new();
266 let req = Request::builder()
267 .uri("/not-found")
268 .method(Method::GET)
269 .body(())
270 .unwrap();
271
272 let resp = router.call(req).await;
273 let resp = resp.unwrap();
274
275 assert_eq!(resp.status(), 404);
276 });
277 }
278
279 #[test]
280 fn receives_data() {
281 futures::executor::block_on(async move {
282 let data = Arc::new(AtomicBool::new(false));
283 let mut router: Router<(), Arc<AtomicBool>, Infallible> =
284 Router::with_data(data.clone()).get("/example", |_req, ctx| async move {
285 ctx.data.store(true, Ordering::SeqCst);
286 Ok(Response::builder().body(()).unwrap())
287 });
288
289 let req = Request::builder()
290 .uri("/example")
291 .method(Method::GET)
292 .body(())
293 .unwrap();
294
295 let resp = router.call(req).await.unwrap();
296 assert_eq!(resp.status(), 200);
297 assert!(data.load(Ordering::SeqCst));
298 });
299 }
300
301 #[test]
302 fn receives_data_async() {
303 futures::executor::block_on(async move {
304 let data = Arc::new(AtomicBool::new(false));
305 let mut router: Router<_, _, Infallible> =
306 Router::with_data(data.clone()).get("/example", |_req, ctx| async move {
307 ctx.data.store(true, Ordering::SeqCst);
308 Ok(Response::builder().body(()).unwrap())
309 });
310
311 let req = Request::builder()
312 .uri("/example")
313 .method(Method::GET)
314 .body(())
315 .unwrap();
316
317 let resp = router.call(req).await.unwrap();
318 assert_eq!(resp.status(), 200);
319 assert!(data.load(Ordering::SeqCst));
320 });
321 }
322}