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