1use std::future::Future;
2
3use futures_util::{future::Either, FutureExt};
4use http::Method;
5use indexmap::IndexMap;
6use matchit::{InsertError, Match};
7use predawn_core::{error::Error, request::Request, response::Response};
8use snafu::ResultExt;
9
10use crate::{
11 handler::{DynHandler, Handler},
12 path_params::PathParams,
13 response_error::{MatchSnafu, MethodNotAllowedSnafu},
14};
15
16#[derive(Default)]
17pub struct MethodRouter {
18 methods: IndexMap<Method, DynHandler>,
19}
20
21impl From<IndexMap<Method, DynHandler>> for MethodRouter {
22 fn from(methods: IndexMap<Method, DynHandler>) -> Self {
23 Self { methods }
24 }
25}
26
27impl Handler for MethodRouter {
28 fn call(&self, mut req: Request) -> impl Future<Output = Result<Response, Error>> + Send {
29 let method = &mut req.head.method;
30
31 match self.methods.get(method) {
32 Some(handler) => Either::Left(handler.call(req)),
33 None => Either::Right(
34 if *method == Method::HEAD {
35 *method = Method::GET;
36
37 Either::Left(
38 async move {
39 let mut response = self.call(req).await?;
40 response.body_mut().clear();
41 Ok(response)
42 }
43 .boxed(),
44 )
45 } else {
46 Either::Right(async { Err(MethodNotAllowedSnafu.build().into()) })
47 },
48 ),
49 }
50 }
51}
52
53#[derive(Default)]
54pub struct Router {
55 router: matchit::Router<MethodRouter>,
56 routes: Vec<(Box<str>, Box<[Method]>)>,
57}
58
59impl Router {
60 pub fn insert<S>(&mut self, route: S, method_router: MethodRouter) -> Result<(), InsertError>
61 where
62 S: Into<String>,
63 {
64 fn inner_insert(
65 router: &mut Router,
66 route: String,
67 method_router: MethodRouter,
68 ) -> Result<(), InsertError> {
69 let methods = method_router.methods.keys().cloned().collect();
70
71 router.router.insert(route.clone(), method_router)?;
72 router.routes.push((route.into(), methods));
73
74 Ok(())
75 }
76
77 inner_insert(self, route.into(), method_router)
78 }
79
80 pub fn at<'m, 'p>(
81 &'m self,
82 path: &'p str,
83 ) -> Result<Match<'m, 'p, &'m MethodRouter>, matchit::MatchError> {
84 self.router.at(path)
85 }
86
87 pub fn routes(&self) -> &[(Box<str>, Box<[Method]>)] {
88 &self.routes
89 }
90}
91
92impl Handler for Router {
93 async fn call(&self, mut req: Request) -> Result<Response, Error> {
94 let head = &mut req.head;
95
96 let matched = self.at(head.uri.path()).context(MatchSnafu)?;
97
98 head.extensions
99 .get_or_insert_default::<PathParams>()
100 .insert(matched.params);
101
102 matched.value.call(req).await
103 }
104}