predawn/
route.rs

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}