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);