micro_web/
handler.rs

1//! Request handler types and traits for the web framework.
2//!
3//! This module provides the core abstractions for handling HTTP requests:
4//! - `RequestHandler` trait for implementing request handlers
5//! - `FnHandler` for wrapping async functions as handlers
6//! - Helper functions for creating handlers
7
8use crate::body::ResponseBody;
9use crate::fn_trait::FnTrait;
10
11use crate::responder::Responder;
12use crate::{OptionReqBody, RequestContext};
13use async_trait::async_trait;
14use http::Response;
15
16use crate::extract::FromRequest;
17use std::marker::PhantomData;
18
19/// Trait for types that can handle HTTP requests.
20///
21/// Implementors must provide an `invoke` method that processes the request
22/// and returns a response. This is the core trait for request handling.
23#[async_trait]
24pub trait RequestHandler: Send + Sync {
25    async fn invoke<'server, 'req>(&self, req: &mut RequestContext<'server, 'req>, req_body: OptionReqBody) -> Response<ResponseBody>;
26}
27
28#[async_trait]
29impl<T> RequestHandler for Box<T>
30where
31    T: RequestHandler,
32{
33    async fn invoke<'server, 'req>(&self, req: &mut RequestContext<'server, 'req>, req_body: OptionReqBody) -> Response<ResponseBody> {
34        (**self).invoke(req, req_body).await
35    }
36}
37
38#[async_trait]
39impl RequestHandler for Box<dyn RequestHandler> {
40    async fn invoke<'server, 'req>(&self, req: &mut RequestContext<'server, 'req>, req_body: OptionReqBody) -> Response<ResponseBody> {
41        (**self).invoke(req, req_body).await
42    }
43}
44
45#[async_trait]
46impl RequestHandler for &dyn RequestHandler {
47    async fn invoke<'server, 'req>(&self, req: &mut RequestContext<'server, 'req>, req_body: OptionReqBody) -> Response<ResponseBody> {
48        (**self).invoke(req, req_body).await
49    }
50}
51
52/// A wrapper type that converts async functions into request handlers.
53///
54/// This allows regular async functions to be used as request handlers
55/// by implementing the `RequestHandler` trait for them.
56///
57/// Type parameters:
58/// - `F`: The async function type
59/// - `Args`: The function arguments type
60pub struct FnHandler<F, Args> {
61    f: F,
62    _phantom: PhantomData<fn(Args)>,
63}
64
65impl<F, Args> FnHandler<F, Args>
66where
67    F: FnTrait<Args>,
68{
69    fn new(f: F) -> Self {
70        Self { f, _phantom: PhantomData }
71    }
72}
73
74/// Creates a new `FnHandler` that wraps the given async function.
75///
76/// This is the main way to convert an async function into a request handler.
77pub fn handler_fn<F, Args>(f: F) -> FnHandler<F, Args>
78where
79    F: FnTrait<Args>,
80{
81    FnHandler::new(f)
82}
83
84#[async_trait]
85impl<F, Args> RequestHandler for FnHandler<F, Args>
86where
87    // Args must implement [`FromRequest`] trait
88    // This allows extracting the function arguments from the HTTP request
89    Args: FromRequest,
90    // F must be a function [`FnTrait`] that can accept Args::Output<'r> for any lifetime 'r
91    // This allows the function to work with arguments that have different lifetimes
92    for<'r> F: FnTrait<Args::Output<'r>>,
93    // The output of calling F must implement [`Responder`] trait
94    // This ensures the function's return value can be converted into an HTTP response
95    for<'r> <F as FnTrait<Args::Output<'r>>>::Output: Responder,
96{
97    async fn invoke<'server, 'req>(&self, req: &mut RequestContext<'server, 'req>, req_body: OptionReqBody) -> Response<ResponseBody> {
98        let args = match Args::from_request(req, req_body.clone()).await {
99            Ok(args) => args,
100            Err(responder) => return responder.response_to(req),
101        };
102        let responder = self.f.call(args).await;
103        responder.response_to(req)
104    }
105}
106
107#[cfg(test)]
108mod test {
109    use crate::fn_trait::FnTrait;
110    use crate::handler::{FnHandler, RequestHandler};
111
112    use http::Method;
113
114    fn assert_is_fn_handler<H: FnTrait<Args>, Args>(_handler: &FnHandler<H, Args>) {
115        // no op
116    }
117
118    fn assert_is_handler<T: RequestHandler>(_handler: &T) {
119        // no op
120    }
121
122    #[test]
123    fn assert_fn_is_http_handler_1() {
124        async fn get(_header: Method) {}
125
126        let http_handler = FnHandler::new(get);
127        assert_is_fn_handler(&http_handler);
128        assert_is_handler(&http_handler);
129    }
130
131    #[test]
132    fn assert_fn_is_http_handler_2() {
133        async fn get(_header: &Method, _str: String) {}
134
135        let http_handler = FnHandler::new(get);
136        assert_is_fn_handler(&http_handler);
137        assert_is_handler(&http_handler);
138    }
139}