skyzen/
handler.rs

1//! Handle the request and make a response.
2//! ```rust
3//! // A simple echo server
4//! async fn handler(body: String) -> http_kit::Result<String> {
5//!     Ok(body)
6//! }
7//! ```
8
9use core::{future::Future, marker::PhantomData};
10use http_kit::{Endpoint, Request, Response};
11use skyzen_core::{Extractor, Responder};
12use std::fmt::Display;
13
14/// Error type for handler operations.
15pub enum HandlerError<E: Extractor, R: Responder> {
16    /// An error occurred during extraction.
17    ExtractorError(E::Error),
18    /// An error occurred during response generation.
19    ResponderError(R::Error),
20}
21
22impl<E: Extractor, R: Responder> Display for HandlerError<E, R> {
23    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
24        match self {
25            Self::ExtractorError(e) => write!(f, "{e}"),
26            Self::ResponderError(e) => write!(f, "{e}"),
27        }
28    }
29}
30
31impl<E: Extractor, R: Responder> core::fmt::Debug for HandlerError<E, R> {
32    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33        match self {
34            Self::ExtractorError(e) => write!(f, "{e:?}"),
35            Self::ResponderError(e) => write!(f, "{e:?}"),
36        }
37    }
38}
39
40impl<E: Extractor, R: Responder> http_kit::HttpError for HandlerError<E, R> {
41    fn status(&self) -> http_kit::StatusCode {
42        match self {
43            Self::ExtractorError(e) => e.status(),
44            Self::ResponderError(e) => e.status(),
45        }
46    }
47}
48
49impl<E: Extractor, R: Responder> core::error::Error for HandlerError<E, R> {}
50
51/// An HTTP handler.
52/// This trait is a wrapper trait for `Fn` types. You will rarely use this type directly.
53pub trait Handler<T: Extractor, R: Responder>: Send + Sync + Clone + 'static {
54    /// Handle the request and make a response.
55    fn call_handler(
56        &self,
57        request: &mut Request,
58    ) -> impl Future<Output = Result<Response, HandlerError<T, R>>> + Send;
59}
60
61/// Adapter that turns a strongly typed [`Handler`] into an [`Endpoint`].
62#[derive(Debug)]
63pub struct IntoEndpoint<H: Handler<T, R>, T: Extractor, R: Responder> {
64    handler: H,
65    _marker: PhantomData<(T, R)>,
66}
67
68/// Transform handler to endpoint.
69pub const fn into_endpoint<T: Extractor + Send, R: Responder, H: Handler<T, R>>(
70    handler: H,
71) -> IntoEndpoint<H, T, R> {
72    IntoEndpoint::new(handler)
73}
74
75impl<H: Handler<T, R>, T: Extractor, R: Responder> IntoEndpoint<H, T, R> {
76    /// Create an `IntoEndpoint` instance.
77    pub const fn new(handler: H) -> Self {
78        Self {
79            handler,
80            _marker: PhantomData,
81        }
82    }
83}
84
85impl<H, T, R> Clone for IntoEndpoint<H, T, R>
86where
87    H: Handler<T, R> + Clone,
88    T: Extractor,
89    R: Responder,
90{
91    fn clone(&self) -> Self {
92        Self {
93            handler: self.handler.clone(),
94            _marker: PhantomData,
95        }
96    }
97}
98
99macro_rules! impl_handler {
100    ($($ty:ident),*) => {
101        #[allow(non_snake_case)]
102
103        impl<F, Fut, Res,$($ty:Extractor,)*> Handler<($($ty,)*) , Res> for F
104        where
105            F: 'static + Clone + Send + Sync + Fn($($ty,)*) -> Fut,
106            Fut: Send + Future<Output = Res>,
107            Res: Responder,
108        {
109            async fn call_handler(&self, request: &mut Request) -> Result<Response, HandlerError<($($ty,)*), Res>> {
110                let ($($ty,)*) = <($($ty,)*) as Extractor>::extract(request).await.map_err(|e| HandlerError::ExtractorError(e))?;
111                let mut response = Response::new(http_kit::Body::empty());
112                (self)($($ty,)*).await.respond_to(request,&mut response).map_err(|e| HandlerError::ResponderError(e))?;
113                Ok(response)
114            }
115        }
116    };
117}
118
119tuples!(impl_handler);
120
121impl<H: Handler<T, R> + Send + Sync, T: Extractor + Send + Sync, R: Responder + Send + Sync>
122    Endpoint for IntoEndpoint<H, T, R>
123{
124    type Error = HandlerError<T, R>;
125    async fn respond(&mut self, request: &mut Request) -> Result<Response, Self::Error> {
126        self.handler.call_handler(request).await
127    }
128}