1#[cfg(feature = "openapi")]
4pub mod openapi;
5
6use std::fmt::{Debug, Formatter};
7
8use cot_core::handler::{BoxRequestHandler, into_box_request_handler};
9
10use crate::error::MethodNotAllowed;
11use crate::request::Request;
12use crate::response::Response;
13use crate::{Method, RequestHandler};
14
15#[derive(Debug)]
78#[must_use]
79pub struct MethodRouter {
80 inner: InnerMethodRouter<InnerHandler>,
81}
82
83macro_rules! define_method {
84 ($name:ident => $method:ident) => {
85 #[doc = concat!("Set a handler for the [`",
86 stringify!($method),
87 "`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/",
88 stringify!($method),
89 ") HTTP method.")]
90 #[doc = concat!(
103 "let method_router = MethodRouter::new().",
104 stringify!($name),
105 "(test_handler);"
106 )]
107 #[doc = concat!(
113 "# let request = cot::test::TestRequestBuilder::with_method(\"/\", cot::Method::",
114 stringify!($method),
115 ")"
116 )]
117 pub fn $name<HandlerParams, H>(mut self, handler: H) -> Self
132 where
133 HandlerParams: 'static,
134 H: RequestHandler<HandlerParams> + Send + Sync + 'static,
135 {
136 self.inner.$name = Some(InnerHandler::new(handler));
137 self
138 }
139 };
140}
141
142impl Default for MethodRouter {
143 fn default() -> Self {
144 Self::new()
145 }
146}
147
148impl MethodRouter {
149 pub fn new() -> Self {
187 Self {
188 inner: InnerMethodRouter::new(),
189 }
190 }
191
192 define_method!(get => GET);
193 define_method!(head => HEAD);
194 define_method!(delete => DELETE);
195 define_method!(options => OPTIONS);
196 define_method!(patch => PATCH);
197 define_method!(post => POST);
198 define_method!(put => PUT);
199 define_method!(trace => TRACE);
200 define_method!(connect => CONNECT);
201
202 pub fn fallback<HandlerParams, H>(mut self, handler: H) -> Self
238 where
239 HandlerParams: 'static,
240 H: RequestHandler<HandlerParams> + Send + Sync + 'static,
241 {
242 self.inner.fallback = InnerHandler::new(handler);
243 self
244 }
245}
246
247impl RequestHandler for MethodRouter {
248 fn handle(&self, request: Request) -> impl Future<Output = cot::Result<Response>> + Send {
249 self.inner.handle(request)
250 }
251}
252
253#[derive(Debug)]
254#[must_use]
255struct InnerMethodRouter<T> {
256 pub(self) get: Option<T>,
257 pub(self) head: Option<T>,
258 pub(self) delete: Option<T>,
259 pub(self) options: Option<T>,
260 pub(self) patch: Option<T>,
261 pub(self) post: Option<T>,
262 pub(self) put: Option<T>,
263 pub(self) trace: Option<T>,
264 pub(self) connect: Option<InnerHandler>,
266 pub(self) fallback: InnerHandler,
267}
268
269impl<T> InnerMethodRouter<T> {
270 pub(crate) fn new() -> Self {
271 Self {
272 get: None,
273 head: None,
274 delete: None,
275 options: None,
276 patch: None,
277 post: None,
278 put: None,
279 trace: None,
280 connect: None,
281 fallback: InnerHandler::new(default_fallback),
282 }
283 }
284}
285
286impl<T: RequestHandler + Send + Sync> RequestHandler for InnerMethodRouter<T> {
287 async fn handle(&self, request: Request) -> cot::Result<Response> {
288 macro_rules! handle_method {
289 ($name:ident => $method:ident) => {
290 if request.method() == Method::$method {
291 if let Some(handler) = &self.$name {
292 return handler.handle(request).await;
293 }
294 }
295 };
296 }
297
298 handle_method!(get => GET);
299 handle_method!(head => HEAD);
300 handle_method!(delete => DELETE);
301 handle_method!(options => OPTIONS);
302 handle_method!(patch => PATCH);
303 handle_method!(post => POST);
304 handle_method!(put => PUT);
305 handle_method!(trace => TRACE);
306 handle_method!(connect => CONNECT);
307
308 if request.method() == Method::HEAD {
309 if let Some(handler) = &self.get {
313 return handler.handle(request).await;
314 }
315 }
316
317 self.fallback.handle(request).await
318 }
319}
320
321struct InnerHandler(Box<dyn BoxRequestHandler + Send + Sync>);
322
323impl InnerHandler {
324 fn new<HandlerParams, H>(handler: H) -> Self
325 where
326 HandlerParams: 'static,
327 H: RequestHandler<HandlerParams> + Send + Sync + 'static,
328 {
329 Self(Box::new(into_box_request_handler(handler)))
330 }
331}
332
333impl Debug for InnerHandler {
334 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
335 f.debug_tuple("InnerHandler").finish_non_exhaustive()
336 }
337}
338
339impl RequestHandler for InnerHandler {
340 fn handle(&self, request: Request) -> impl Future<Output = cot::Result<Response>> + Send {
341 self.0.handle(request)
342 }
343}
344
345macro_rules! define_method_router {
346 ($name:ident => $method:ident) => {
347 #[doc = concat!(
348 "Create a new [`MethodRouter`] with a [`",
349 stringify!($method),
350 "`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/",
351 stringify!($method),
352 ") handler."
353 )]
354 #[doc = concat!(
356 "This is a shorthand to calling [`MethodRouter::new`] and then [`MethodRouter::",
357 stringify!($name),
358 "`]."
359 )]
360 #[doc = concat!("use cot::router::method::", stringify!($name), ";")]
365 #[doc = concat!("let method_router = ", stringify!($name), "(test_handler);")]
373 #[doc = concat!(
379 "# let request = cot::test::TestRequestBuilder::with_method(\"/\", cot::Method::",
380 stringify!($method),
381 ")"
382 )]
383 pub fn $name<HandlerParams, H>(handler: H) -> MethodRouter
398 where
399 HandlerParams: 'static,
400 H: RequestHandler<HandlerParams> + Send + Sync + 'static,
401 {
402 MethodRouter::new().$name(handler)
403 }
404 };
405}
406
407define_method_router!(get => GET);
408define_method_router!(head => HEAD);
409define_method_router!(delete => DELETE);
410define_method_router!(options => OPTIONS);
411define_method_router!(patch => PATCH);
412define_method_router!(post => POST);
413define_method_router!(put => PUT);
414define_method_router!(trace => TRACE);
415define_method_router!(connect => CONNECT);
416
417async fn default_fallback(method: Method) -> crate::Error {
418 MethodNotAllowed::new(method).into()
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424 use crate::StatusCode;
425 use crate::html::Html;
426 use crate::test::TestRequestBuilder;
427
428 async fn test_handler(method: Method) -> Html {
429 Html::new(method.as_str())
430 }
431
432 #[test]
433 fn inner_handler_debug() {
434 let handler = InnerHandler::new(test_handler);
435
436 let debug_str = format!("{handler:?}");
437
438 assert_eq!(debug_str, "InnerHandler(..)");
439 }
440
441 #[cot::test]
442 async fn method_router_fallback() {
443 let router = MethodRouter::new();
444
445 let request = TestRequestBuilder::get("/").build();
446 let response = router.handle(request).await.unwrap_err();
447 let inner = response.inner();
448
449 assert_eq!(inner.status_code(), StatusCode::METHOD_NOT_ALLOWED);
450 assert!(inner.is::<MethodNotAllowed>());
451 }
452
453 #[cot::test]
454 async fn method_router_default_fallback() {
455 let router = MethodRouter::default();
456
457 let request = TestRequestBuilder::get("/").build();
458 let response = router.handle(request).await.unwrap_err();
459 let inner = response.inner();
460
461 assert_eq!(inner.status_code(), StatusCode::METHOD_NOT_ALLOWED);
462 assert!(inner.is::<MethodNotAllowed>());
463 }
464
465 #[cot::test]
466 async fn method_router_custom_fallback() {
467 let router = MethodRouter::new().fallback(test_handler);
468
469 let request = TestRequestBuilder::get("/").build();
470 let response = router.handle(request).await.unwrap();
471
472 assert_eq!(response.status(), StatusCode::OK);
473 assert_eq!(response.into_body().into_bytes().await.unwrap(), "GET");
474 }
475
476 #[cot::test]
477 async fn method_router_get() {
478 let router = get(test_handler);
479
480 let request = TestRequestBuilder::get("/").build();
481 let response = router.handle(request).await.unwrap();
482
483 assert_eq!(response.status(), StatusCode::OK);
484
485 let methods = [
487 Method::DELETE,
488 Method::OPTIONS,
489 Method::PATCH,
490 Method::POST,
491 Method::PUT,
492 Method::TRACE,
493 Method::CONNECT,
494 ];
495 for method in methods {
496 let request = TestRequestBuilder::with_method("/", method).build();
497 let response = router.handle(request).await.unwrap_err();
498 let inner = response.inner();
499
500 assert_eq!(inner.status_code(), StatusCode::METHOD_NOT_ALLOWED);
501 assert!(inner.is::<MethodNotAllowed>());
502 }
503 }
504
505 macro_rules! test_method_router {
506 ($test_name:ident, $constructor_name:ident, $method_name:ident) => {
507 #[cot::test]
508 async fn $test_name() {
509 let router = $constructor_name(test_handler);
510
511 let request = TestRequestBuilder::with_method("/", Method::$method_name).build();
512 let response = router.handle(request).await.unwrap();
513
514 assert_eq!(response.status(), StatusCode::OK);
515 }
516 };
517 }
518
519 test_method_router!(method_router_head, head, HEAD);
520 test_method_router!(method_router_delete, delete, DELETE);
521 test_method_router!(method_router_options, options, OPTIONS);
522 test_method_router!(method_router_patch, patch, PATCH);
523 test_method_router!(method_router_post, post, POST);
524 test_method_router!(method_router_put, put, PUT);
525 test_method_router!(method_router_trace, trace, TRACE);
526 test_method_router!(method_router_connect, connect, CONNECT);
527
528 #[cot::test]
529 async fn method_router_default_head() {
530 let router = MethodRouter::new();
532
533 let request = TestRequestBuilder::with_method("/", Method::HEAD).build();
534 let response = router.handle(request).await.unwrap_err();
535 let inner = response.inner();
536
537 assert_eq!(inner.status_code(), StatusCode::METHOD_NOT_ALLOWED);
538 assert!(inner.is::<MethodNotAllowed>());
539
540 let router = get(test_handler);
542
543 let request = TestRequestBuilder::with_method("/", Method::HEAD).build();
544 let response = router.handle(request).await.unwrap();
545
546 assert_eq!(response.status(), StatusCode::OK);
547 }
548
549 #[cot::test]
550 async fn method_router_multiple() {
551 let router = MethodRouter::new()
552 .get(test_handler)
553 .head(test_handler)
554 .delete(test_handler)
555 .options(test_handler)
556 .patch(test_handler)
557 .post(test_handler)
558 .put(test_handler)
559 .trace(test_handler)
560 .connect(test_handler);
561
562 for (method, expected_string) in [
563 (Method::GET, "GET"),
564 (Method::HEAD, "HEAD"),
565 (Method::DELETE, "DELETE"),
566 (Method::OPTIONS, "OPTIONS"),
567 (Method::PATCH, "PATCH"),
568 (Method::POST, "POST"),
569 (Method::PUT, "PUT"),
570 (Method::TRACE, "TRACE"),
571 (Method::CONNECT, "CONNECT"),
572 ] {
573 let request = TestRequestBuilder::with_method("/", method).build();
574 let response = router.handle(request).await.unwrap();
575
576 assert_eq!(response.status(), StatusCode::OK);
577 assert_eq!(
578 response.into_body().into_bytes().await.unwrap(),
579 expected_string
580 );
581 }
582 }
583}