puzz_route/
method.rs

1use puzz_core::http::Method;
2use puzz_core::response::IntoResponse;
3use puzz_core::service::util::BoxService;
4use puzz_core::service::{Service, ServiceExt};
5use puzz_core::{BoxError, Request, Response};
6
7use crate::error::MethodNotAllowed;
8use crate::RouteFuture;
9
10#[derive(Debug)]
11pub struct MethodRouter {
12    options: Option<BoxService<Request, Response, BoxError>>,
13    get: Option<BoxService<Request, Response, BoxError>>,
14    post: Option<BoxService<Request, Response, BoxError>>,
15    put: Option<BoxService<Request, Response, BoxError>>,
16    delete: Option<BoxService<Request, Response, BoxError>>,
17    head: Option<BoxService<Request, Response, BoxError>>,
18    trace: Option<BoxService<Request, Response, BoxError>>,
19    patch: Option<BoxService<Request, Response, BoxError>>,
20}
21
22macro_rules! router_impl_method_fn {
23    ($method:ident) => {
24        pub fn $method<S>(mut self, service: S) -> Self
25        where
26            S: Service<Request> + 'static,
27            S::Response: IntoResponse,
28            S::Error: Into<BoxError>,
29        {
30            self.$method = Some(Self::into_box_service(service));
31            self
32        }
33    };
34}
35
36impl MethodRouter {
37    fn new() -> Self {
38        Self {
39            options: None,
40            get: None,
41            post: None,
42            put: None,
43            delete: None,
44            head: None,
45            trace: None,
46            patch: None,
47        }
48    }
49
50    router_impl_method_fn!(options);
51    router_impl_method_fn!(get);
52    router_impl_method_fn!(post);
53    router_impl_method_fn!(put);
54    router_impl_method_fn!(delete);
55    router_impl_method_fn!(head);
56    router_impl_method_fn!(trace);
57    router_impl_method_fn!(patch);
58
59    fn into_box_service<S>(service: S) -> BoxService<Request, Response, BoxError>
60    where
61        S: Service<Request> + 'static,
62        S::Response: IntoResponse,
63        S::Error: Into<BoxError>,
64    {
65        service
66            .map_response(IntoResponse::into_response)
67            .map_err(Into::into)
68            .boxed()
69    }
70}
71
72impl Service<Request> for MethodRouter {
73    type Response = Response;
74    type Error = BoxError;
75    type Future = RouteFuture;
76
77    fn call(&self, request: Request) -> Self::Future {
78        macro_rules! call {
79            ($req:expr, $method:expr, $svc:expr) => {
80                if $method == $req.method() {
81                    if let Some(svc) = $svc {
82                        return RouteFuture::Future {
83                            fut: svc.call($req),
84                        };
85                    }
86                }
87            };
88        }
89
90        call!(request, Method::HEAD, &self.head);
91        call!(request, Method::HEAD, &self.get);
92        call!(request, Method::GET, &self.get);
93        call!(request, Method::POST, &self.post);
94        call!(request, Method::OPTIONS, &self.options);
95        call!(request, Method::PATCH, &self.patch);
96        call!(request, Method::PUT, &self.put);
97        call!(request, Method::DELETE, &self.delete);
98        call!(request, Method::TRACE, &self.trace);
99
100        RouteFuture::Error {
101            err: Some(MethodNotAllowed::new(request).into()),
102        }
103    }
104}
105
106macro_rules! impl_method_fn {
107    ($method:ident) => {
108        pub fn $method<S>(service: S) -> MethodRouter
109        where
110            S: Service<Request> + 'static,
111            S::Response: IntoResponse,
112            S::Error: Into<BoxError>,
113        {
114            MethodRouter::new().$method(service)
115        }
116    };
117}
118
119impl_method_fn!(options);
120impl_method_fn!(get);
121impl_method_fn!(post);
122impl_method_fn!(put);
123impl_method_fn!(delete);
124impl_method_fn!(head);
125impl_method_fn!(trace);
126impl_method_fn!(patch);