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