Skip to main content

ntex/web/
handler.rs

1use std::{fmt, marker::PhantomData, rc::Rc};
2
3use super::{FromRequest, Responder, State, WebRequest, WebResponse, WebResponseError};
4use crate::util::BoxFuture;
5
6/// Async fn handler that receives the application and request state.
7///
8/// Implemented for async functions and closures whose first two arguments are
9/// the application state `&St` and the request state `U`, followed by up to 16
10/// extractors. `T` is the tuple of extractor types. Register such handlers with
11/// [`Route::to_with_state()`](crate::web::Route::to_with_state).
12pub trait HandlerSt<St, U, T>: 'static
13where
14    St: State,
15{
16    /// Handler result, converted into a response.
17    type Output: Responder<St>;
18
19    /// Call the handler with the states and extracted values.
20    async fn call(&self, st: &St, req: U, param: T) -> Self::Output;
21}
22
23impl<St, U, F, R> HandlerSt<St, U, ()> for F
24where
25    F: AsyncFn(&St, U) -> R + 'static,
26    R: Responder<St>,
27    St: State,
28{
29    type Output = R;
30
31    #[allow(clippy::ignored_unit_patterns)]
32    async fn call(&self, st: &St, req: U, _: ()) -> R {
33        (self)(st, req).await
34    }
35}
36
37/// Async fn handler.
38///
39/// Implemented for async functions and closures that take up to 16
40/// extractors. `T` is the tuple of extractor types. Register such handlers with
41/// [`Route::to()`](crate::web::Route::to). Use [`HandlerSt`] when the handler
42/// needs the application or request state.
43pub trait Handler<St, T>: 'static
44where
45    St: State,
46{
47    /// Handler result, converted into a response.
48    type Output: Responder<St>;
49
50    /// Call the handler with the extracted values.
51    async fn call(&self, param: T) -> Self::Output;
52}
53
54impl<St, F, R> Handler<St, ()> for F
55where
56    F: AsyncFn() -> R + 'static,
57    R: Responder<St>,
58    St: State,
59{
60    type Output = R;
61
62    #[allow(clippy::ignored_unit_patterns)]
63    async fn call(&self, _: ()) -> R {
64        (self)().await
65    }
66}
67
68pub(super) trait HandlerFn<St: State, U>: fmt::Debug {
69    fn call<'a>(&'a self, _: &'a St, _: WebRequest<U>) -> BoxFuture<'a, WebResponse>;
70}
71
72pub(super) struct HandlerStWrapper<St, U, F, T> {
73    hnd: F,
74    _t: PhantomData<(St, U, T)>,
75}
76
77impl<St, U, F, T> HandlerStWrapper<St, U, F, T>
78where
79    F: HandlerSt<St, U, T> + 'static,
80    T: FromRequest<St> + 'static,
81    T::Error: WebResponseError<St, St::Error>,
82    St: State,
83    U: 'static,
84{
85    pub(super) fn create(hnd: F) -> Rc<dyn HandlerFn<St, U>> {
86        Rc::new(HandlerStWrapper {
87            hnd,
88            _t: PhantomData,
89        })
90    }
91}
92
93impl<St, U, F, T> fmt::Debug for HandlerStWrapper<St, U, F, T> {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        write!(f, "HandlerSt({:?})", std::any::type_name::<F>())
96    }
97}
98
99impl<St, U, F, T> HandlerFn<St, U> for HandlerStWrapper<St, U, F, T>
100where
101    F: HandlerSt<St, U, T> + 'static,
102    T: FromRequest<St> + 'static,
103    T::Error: WebResponseError<St, St::Error>,
104    St: State,
105{
106    fn call<'a>(&'a self, st: &'a St, req: WebRequest<U>) -> BoxFuture<'a, WebResponse> {
107        Box::pin(async move {
108            let (req, mut payload, reqst) = req.into_parts();
109            let param = match T::from_request(st, &req, &mut payload).await {
110                Ok(param) => param,
111                Err(e) => return WebResponse::from_err(st, &e, req),
112            };
113
114            let result = self.hnd.call(st, reqst, param).await;
115            let response = result.respond_to(st, &req).await;
116            WebResponse::new(response, req)
117        })
118    }
119}
120
121pub(super) struct HandlerWrapper<St, U, F, T> {
122    hnd: F,
123    _t: PhantomData<(St, U, T)>,
124}
125
126impl<St, U, F, T> HandlerWrapper<St, U, F, T>
127where
128    F: Handler<St, T> + 'static,
129    T: FromRequest<St> + 'static,
130    T::Error: WebResponseError<St, St::Error>,
131    St: State,
132    U: 'static,
133{
134    pub(super) fn create(hnd: F) -> Rc<dyn HandlerFn<St, U>> {
135        Rc::new(HandlerWrapper {
136            hnd,
137            _t: PhantomData,
138        })
139    }
140}
141
142impl<St, U, F, T> fmt::Debug for HandlerWrapper<St, U, F, T> {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        write!(f, "HandlerNoState({:?})", std::any::type_name::<F>())
145    }
146}
147
148impl<St, U, F, T> HandlerFn<St, U> for HandlerWrapper<St, U, F, T>
149where
150    F: Handler<St, T> + 'static,
151    T: FromRequest<St> + 'static,
152    T::Error: WebResponseError<St, St::Error>,
153    St: State,
154{
155    fn call<'a>(&'a self, st: &'a St, req: WebRequest<U>) -> BoxFuture<'a, WebResponse> {
156        Box::pin(async move {
157            let (req, mut payload, _) = req.into_parts();
158            let param = match T::from_request(st, &req, &mut payload).await {
159                Ok(param) => param,
160                Err(e) => return WebResponse::from_err(st, &e, req),
161            };
162
163            let result = self.hnd.call(param).await;
164            let response = result.respond_to(st, &req).await;
165            WebResponse::new(response, req)
166        })
167    }
168}
169
170/// `FromRequest` trait impl for tuples
171macro_rules! factory_tuple (
172    {$(#[$meta:meta])* $(($T:ident, $t:ident)),+} => {
173        $(#[$meta])*
174        impl<St, Func, U, $($T,)+ Res> HandlerSt<St, U, ($($T,)+)> for Func
175        where
176            St: State,
177            Func: 'static,
178            Func: AsyncFn(&St, U, $($T,)+) -> Res,
179            Res: Responder<St>,
180        {
181            type Output = Res;
182
183            async fn call(&self, st: &St, req: U, ($($t,)+): ($($T,)+)) -> Self::Output {
184                (self)(st, req, $($t,)+).await
185            }
186        }
187    }
188);
189
190macro_rules! factory_tuple_no_state (
191    {$(#[$meta:meta])* $(($T:ident, $t:ident)),+} => {
192        $(#[$meta])*
193        impl<St, Func, $($T,)+ Res> Handler<St, ($($T,)+)> for Func
194        where
195            St: State,
196            Func: 'static,
197            Func: AsyncFn($($T,)+) -> Res,
198            Res: Responder<St>,
199        {
200            type Output = Res;
201
202            async fn call(&self, ($($t,)+): ($($T,)+)) -> Self::Output {
203                (self)($($t,)+).await
204            }
205        }
206    }
207);
208
209#[allow(clippy::wildcard_imports)]
210#[rustfmt::skip]
211mod m {
212    use super::*;
213    use variadics_please::all_tuples;
214
215    // Can't use #[doc(fake_variadic)] here
216    all_tuples!(factory_tuple, 1, 16, T, t);
217
218    all_tuples!(factory_tuple_no_state, 1, 16, T, t);
219}