Skip to main content

sova_core/
handler.rs

1use crate::error::{Error, IntoResponse, Result};
2use crate::extract::FromRequest;
3use crate::request::Request;
4use crate::response::Response;
5use std::future::Future;
6use std::pin::Pin;
7use std::sync::Arc;
8
9pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
10
11/// Type-erased async handler: `Request -> Response` (middleware / outer chain).
12pub type Handler = Arc<dyn Fn(Request) -> BoxFuture<Response> + Send + Sync>;
13
14/// Leaf handler that may return [`Error`] for `error_handler`.
15pub type FallibleHandler = Arc<dyn Fn(Request) -> BoxFuture<Result<Response>> + Send + Sync>;
16
17pub(crate) type ErrorHandlerFn = Arc<dyn Fn(Error) -> BoxFuture<Response> + Send + Sync>;
18
19/// Convert async functions into a [`FallibleHandler`].
20pub trait IntoHandler<T> {
21    fn into_handler(self) -> FallibleHandler;
22}
23
24pub struct ResponseMarker;
25pub struct ResultMarker;
26
27impl<F, Fut, R> IntoHandler<(ResponseMarker,)> for F
28where
29    F: Fn(Request) -> Fut + Send + Sync + 'static,
30    Fut: Future<Output = R> + Send + 'static,
31    R: IntoResponse,
32{
33    fn into_handler(self) -> FallibleHandler {
34        Arc::new(move |req| {
35            let fut = self(req);
36            Box::pin(async move { Ok(fut.await.into_response()) })
37        })
38    }
39}
40
41impl<F, Fut, R> IntoHandler<(ResultMarker,)> for F
42where
43    F: Fn(Request) -> Fut + Send + Sync + 'static,
44    Fut: Future<Output = Result<R>> + Send + 'static,
45    R: IntoResponse,
46{
47    fn into_handler(self) -> FallibleHandler {
48        Arc::new(move |req| {
49            let fut = self(req);
50            Box::pin(async move { Ok(fut.await?.into_response()) })
51        })
52    }
53}
54
55/// Marker for handler errors that map via [`IntoResponse`] (not `error_handler`).
56///
57/// Implement for plugin error newtypes (e.g. validation). Do **not** implement for [`Error`].
58pub trait ErrorResponse: IntoResponse {}
59
60/// `Result<T, E>` where `E: ErrorResponse`.
61pub struct FallibleResponseMarker;
62
63impl<F, Fut, R, E> IntoHandler<(FallibleResponseMarker, E)> for F
64where
65    F: Fn(Request) -> Fut + Send + Sync + 'static,
66    Fut: Future<Output = std::result::Result<R, E>> + Send + 'static,
67    R: IntoResponse,
68    E: ErrorResponse + 'static,
69{
70    fn into_handler(self) -> FallibleHandler {
71        Arc::new(move |req| {
72            let fut = self(req);
73            Box::pin(async move {
74                match fut.await {
75                    Ok(r) => Ok(r.into_response()),
76                    Err(e) => Ok(e.into_response()),
77                }
78            })
79        })
80    }
81}
82
83/// `Fn() -> Fut` handlers that ignore the request (e.g. `|| async { "ok" }`).
84pub struct NoArgResponseMarker;
85
86impl<F, Fut, R> IntoHandler<(NoArgResponseMarker,)> for F
87where
88    F: Fn() -> Fut + Send + Sync + 'static,
89    Fut: Future<Output = R> + Send + 'static,
90    R: IntoResponse,
91{
92    fn into_handler(self) -> FallibleHandler {
93        Arc::new(move |_req| {
94            let fut = self();
95            Box::pin(async move { Ok(fut.await.into_response()) })
96        })
97    }
98}
99
100impl IntoHandler<()> for FallibleHandler {
101    fn into_handler(self) -> FallibleHandler {
102        self
103    }
104}
105
106macro_rules! impl_extract_handlers {
107    ($(($marker:ident, $($T:ident),+));+ $(;)?) => {
108        $(
109            pub struct $marker;
110            impl_extract_handlers!(@one $marker, $($T),+);
111        )+
112    };
113    (@one $marker:ident, $($T:ident),+) => {
114        impl<FnH, Fut, R, $($T),+> IntoHandler<($marker, $($T),+)> for FnH
115        where
116            FnH: Fn($($T),+) -> Fut + Send + Sync + 'static,
117            $($T: FromRequest + 'static,)+
118            Fut: Future<Output = R> + Send + 'static,
119            R: IntoResponse,
120        {
121            fn into_handler(self) -> FallibleHandler {
122                let handler = Arc::new(self);
123                Arc::new(move |mut req| {
124                    let handler = Arc::clone(&handler);
125                    Box::pin(async move {
126                        $(
127                            #[allow(non_snake_case)]
128                            let $T = $T::from_request(&mut req).await?;
129                        )+
130                        Ok(handler($($T),+).await.into_response())
131                    })
132                })
133            }
134        }
135
136        impl<FnH, Fut, R, $($T),+> IntoHandler<($marker, ResultMarker, $($T),+)> for FnH
137        where
138            FnH: Fn($($T),+) -> Fut + Send + Sync + 'static,
139            $($T: FromRequest + 'static,)+
140            Fut: Future<Output = Result<R>> + Send + 'static,
141            R: IntoResponse,
142        {
143            fn into_handler(self) -> FallibleHandler {
144                let handler = Arc::new(self);
145                Arc::new(move |mut req| {
146                    let handler = Arc::clone(&handler);
147                    Box::pin(async move {
148                        $(
149                            #[allow(non_snake_case)]
150                            let $T = $T::from_request(&mut req).await?;
151                        )+
152                        Ok(handler($($T),+).await?.into_response())
153                    })
154                })
155            }
156        }
157
158        impl<FnH, Fut, R, ErrE, $($T),+> IntoHandler<($marker, FallibleResponseMarker, ErrE, $($T),+)>
159            for FnH
160        where
161            FnH: Fn($($T),+) -> Fut + Send + Sync + 'static,
162            $($T: FromRequest + Send + 'static,)+
163            Fut: Future<Output = std::result::Result<R, ErrE>> + Send + 'static,
164            R: IntoResponse,
165            ErrE: ErrorResponse + Send + Sync + 'static,
166        {
167            fn into_handler(self) -> FallibleHandler {
168                let handler = Arc::new(self);
169                Arc::new(move |mut req| {
170                    let handler = Arc::clone(&handler);
171                    Box::pin(async move {
172                        $(
173                            #[allow(non_snake_case)]
174                            let $T = $T::from_request(&mut req).await?;
175                        )+
176                        match handler($($T),+).await {
177                            Ok(r) => Ok(r.into_response()),
178                            Err(e) => Ok(e.into_response()),
179                        }
180                    })
181                })
182            }
183        }
184    };
185}
186
187impl_extract_handlers! {
188    (Extract1, T1);
189    (Extract2, T1, T2);
190    (Extract3, T1, T2, T3);
191    (Extract4, T1, T2, T3, T4);
192    (Extract5, T1, T2, T3, T4, T5);
193    (Extract6, T1, T2, T3, T4, T5, T6);
194    (Extract7, T1, T2, T3, T4, T5, T6, T7);
195    (Extract8, T1, T2, T3, T4, T5, T6, T7, T8);
196}
197
198/// Wrap a fallible leaf so middleware chains see a plain [`Handler`].
199pub fn wrap_errors(handler: FallibleHandler, eh: Option<ErrorHandlerFn>) -> Handler {
200    Arc::new(move |req| {
201        let handler = Arc::clone(&handler);
202        let eh = eh.clone();
203        let accept = req.header("accept").unwrap_or("*/*").to_string();
204        Box::pin(async move {
205            crate::accept::with_accept(accept, async move {
206                match handler(req).await {
207                    Ok(res) => res,
208                    // Plugin already decided status/body — do not run error_handler.
209                    Err(Error::Response(res)) => *res,
210                    Err(err) => match &eh {
211                        Some(hook) => hook(err).await,
212                        None => crate::accept::error_response_for_accept(None, err),
213                    },
214                }
215            })
216            .await
217        })
218    })
219}