Skip to main content

sova_core/
handler.rs

1use crate::error::{Error, IntoResponse, Result};
2use crate::request::Request;
3use crate::response::Response;
4use std::future::Future;
5use std::pin::Pin;
6use std::sync::Arc;
7
8pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
9
10/// Type-erased async handler: `Request -> Response` (middleware / outer chain).
11pub type Handler = Arc<dyn Fn(Request) -> BoxFuture<Response> + Send + Sync>;
12
13/// Leaf handler that may return [`Error`] for `error_handler`.
14pub type FallibleHandler =
15    Arc<dyn Fn(Request) -> BoxFuture<Result<Response>> + Send + Sync>;
16
17pub(crate) type ErrorHandlerFn =
18    Arc<dyn Fn(Error) -> BoxFuture<Response> + Send + Sync>;
19
20/// Convert async functions into a [`FallibleHandler`].
21pub trait IntoHandler<T> {
22    fn into_handler(self) -> FallibleHandler;
23}
24
25pub struct ResponseMarker;
26pub struct ResultMarker;
27
28impl<F, Fut, R> IntoHandler<(ResponseMarker,)> for F
29where
30    F: Fn(Request) -> Fut + Send + Sync + 'static,
31    Fut: Future<Output = R> + Send + 'static,
32    R: IntoResponse,
33{
34    fn into_handler(self) -> FallibleHandler {
35        Arc::new(move |req| {
36            let fut = self(req);
37            Box::pin(async move { Ok(fut.await.into_response()) })
38        })
39    }
40}
41
42impl<F, Fut, R> IntoHandler<(ResultMarker,)> for F
43where
44    F: Fn(Request) -> Fut + Send + Sync + 'static,
45    Fut: Future<Output = Result<R>> + Send + 'static,
46    R: IntoResponse,
47{
48    fn into_handler(self) -> FallibleHandler {
49        Arc::new(move |req| {
50            let fut = self(req);
51            Box::pin(async move { Ok(fut.await?.into_response()) })
52        })
53    }
54}
55
56/// Marker for handler errors that map via [`IntoResponse`] (not `error_handler`).
57///
58/// Implement for plugin error newtypes (e.g. validation). Do **not** implement for [`Error`].
59pub trait ErrorResponse: IntoResponse {}
60
61/// `Result<T, E>` where `E: ErrorResponse`.
62pub struct FallibleResponseMarker;
63
64impl<F, Fut, R, E> IntoHandler<(FallibleResponseMarker, E)> for F
65where
66    F: Fn(Request) -> Fut + Send + Sync + 'static,
67    Fut: Future<Output = std::result::Result<R, E>> + Send + 'static,
68    R: IntoResponse,
69    E: ErrorResponse + 'static,
70{
71    fn into_handler(self) -> FallibleHandler {
72        Arc::new(move |req| {
73            let fut = self(req);
74            Box::pin(async move {
75                match fut.await {
76                    Ok(r) => Ok(r.into_response()),
77                    Err(e) => Ok(e.into_response()),
78                }
79            })
80        })
81    }
82}
83
84/// `Fn() -> Fut` handlers that ignore the request (e.g. `|| async { "ok" }`).
85pub struct NoArgResponseMarker;
86
87impl<F, Fut, R> IntoHandler<(NoArgResponseMarker,)> for F
88where
89    F: Fn() -> Fut + Send + Sync + 'static,
90    Fut: Future<Output = R> + Send + 'static,
91    R: IntoResponse,
92{
93    fn into_handler(self) -> FallibleHandler {
94        Arc::new(move |_req| {
95            let fut = self();
96            Box::pin(async move { Ok(fut.await.into_response()) })
97        })
98    }
99}
100
101impl IntoHandler<()> for FallibleHandler {
102    fn into_handler(self) -> FallibleHandler {
103        self
104    }
105}
106
107/// Wrap a fallible leaf so middleware chains see a plain [`Handler`].
108pub fn wrap_errors(handler: FallibleHandler, eh: Option<ErrorHandlerFn>) -> Handler {
109    Arc::new(move |req| {
110        let handler = Arc::clone(&handler);
111        let eh = eh.clone();
112        Box::pin(async move {
113            match handler(req).await {
114                Ok(res) => res,
115                // Plugin already decided status/body — do not run error_handler.
116                Err(Error::Response(res)) => *res,
117                Err(err) => match &eh {
118                    Some(hook) => hook(err).await,
119                    None => err.into_response(),
120                },
121            }
122        })
123    })
124}