micro_web/
fn_trait.rs

1//! Function trait implementation for handling async request handlers
2//!
3//! This module provides the [`FnTrait`] trait which is used to abstract over different types
4//! of async functions that can serve as request handlers. It supports functions with varying
5//! numbers of parameters (from 0 to 12) and ensures they can be used in an async context.
6//!
7//! # Examples
8//!
9//! ```no_run
10//! // Handler with no parameters
11//! async fn handler0() -> &'static str {
12//!     "Hello World!"
13//! }
14//!
15//! // Handler with two parameters
16//! async fn handler2(param1: &str, param2: i32) -> String {
17//!     format!("Hello {}, number: {}", param1, param2)
18//! }
19//! ```
20//!
21//! The trait is automatically implemented for functions that:
22//! - Are async functions
23//! - Return a Future
24//! - Are Send + Sync
25//! - Have 0-12 parameters
26
27
28/// A trait for abstracting over async functions with varying numbers of parameters.
29///
30/// This trait allows the web framework to work with different types of handler functions
31/// in a uniform way, regardless of their parameter count or types.
32///
33/// # Type Parameters
34///
35/// * `Args`: The tuple type containing all function parameters
36///
37/// # Associated Types
38///
39/// * `Output`: The type that the function returns when resolved
40/// * `Fut`: The specific Future type that the function returns
41///
42/// # Examples
43///
44/// ```rust
45/// use http::Method;///
46///
47/// use micro_web::FnTrait;
48///
49/// async fn my_handler(method: &Method) -> String {
50///     format!("Handling {} request", method)
51/// }
52///
53/// // The function automatically implements FnTrait
54/// fn assert_handler<'r, F: FnTrait<(&'r Method,)>>(f: F) {}
55/// assert_handler(my_handler);
56/// ```
57pub trait FnTrait<Args>: Send + Sync {
58    type Output;
59    type Fut: Future<Output = Self::Output> + Send;
60    fn call(&self, args: Args) -> Self::Fut;
61}
62
63/// Implements `FnTrait` for functions with varying numbers of parameters.
64///
65/// This macro generates implementations for functions with 0 to 12 parameters,
66/// allowing them to be used as request handlers in the web framework.
67///
68/// The generated implementations ensure that:
69/// - The function is Send + Sync
70/// - The returned Future is Send
71/// - Parameters are properly passed through to the function
72///
73/// # Example Generated Implementation
74///
75/// ```ignore
76/// // For a two-parameter function:
77/// use micro_web::FnTrait;
78///
79/// impl<Func, Fut, A, B> FnTrait<(A, B)> for Func
80/// where
81///     Func: Fn(A, B) -> Fut + Send + Sync,
82///     Fut: std::future::Future + Send,
83/// {
84///     type Output = Fut::Output;
85///     type Fut = Fut;
86///
87///     fn call(&self, (a, b): (A, B)) -> Self::Fut {
88///         (self)(a, b)
89///     }
90/// }
91/// ```
92macro_rules! impl_fn_trait_for_fn ({ $($param:ident)* } => {
93    impl<Func, Fut, $($param,)*> FnTrait<($($param,)*)> for Func
94    where
95        Func: Fn($($param),*) -> Fut + Send + Sync ,
96        Fut: std::future::Future + Send,
97    {
98        type Output = Fut::Output;
99        type Fut = Fut;
100
101        #[inline]
102        #[allow(non_snake_case)]
103        fn call(&self, ($($param,)*): ($($param,)*)) -> Self::Fut {
104            (self)($($param,)*)
105        }
106    }
107});
108
109impl_fn_trait_for_fn! {}
110impl_fn_trait_for_fn! { A }
111impl_fn_trait_for_fn! { A B }
112impl_fn_trait_for_fn! { A B C }
113impl_fn_trait_for_fn! { A B C D }
114impl_fn_trait_for_fn! { A B C D E }
115impl_fn_trait_for_fn! { A B C D E F }
116impl_fn_trait_for_fn! { A B C D E F G }
117impl_fn_trait_for_fn! { A B C D E F G H }
118impl_fn_trait_for_fn! { A B C D E F G H I }
119impl_fn_trait_for_fn! { A B C D E F G H I J }
120impl_fn_trait_for_fn! { A B C D E F G H I J K }
121impl_fn_trait_for_fn! { A B C D E F G H I J K L }
122
123#[cfg(test)]
124mod tests {
125    use crate::fn_trait::FnTrait;
126    use http::{HeaderMap, Method};
127
128    fn assert_is_fn_trait<Args, F: FnTrait<Args>>(_f: F) {
129        //noop
130    }
131    async fn foo0() {}
132    async fn foo1(_a: ()) {}
133    async fn foo2(_a1: &Method, _a2: &HeaderMap) {}
134    async fn foo3(_a1: &Method, _a2: &HeaderMap, _a3: ()) {}
135    async fn foo4(_a1: &Method, _a2: &HeaderMap, _a3: (), _a4: ()) {}
136    async fn foo5(_a1: (), _a2: &HeaderMap, _a3: (), _a4: (), _a5: ()) {}
137    async fn foo6(_a1: (), _a2: &HeaderMap, _a3: (), _a4: (), _a5: (), _a6: ()) {}
138    async fn foo7(_a1: &Method, _a2: (), _a3: (), _a4: (), _a5: (), _a6: (), _a7: ()) {}
139    #[allow(clippy::too_many_arguments)]
140    async fn foo8(_a1: &Method, _a2: &HeaderMap, _a3: (), _a4: (), _a5: (), _a6: (), _a7: (), _a8: ()) {}
141    #[allow(clippy::too_many_arguments)]
142    async fn foo9(_a1: &Method, _a2: (), _a3: (), _a4: (), _a5: (), _a6: (), _a7: (), _a8: (), _a9: ()) {}
143    #[allow(clippy::too_many_arguments)]
144    async fn foo10(_a1: &Method, _a2: &HeaderMap, _a3: (), _a4: (), _a5: (), _a6: (), _a7: (), _a8: (), _a9: (), _a10: ()) {}
145    #[allow(clippy::too_many_arguments)]
146    async fn foo11(_a1: &Method, _a2: &HeaderMap, _a3: (), _a4: (), _a5: (), _a6: (), _a7: (), _a8: (), _a9: (), _a10: (), _a11: ()) {}
147    #[allow(clippy::too_many_arguments)]
148    async fn foo12(
149        _a1: &Method,
150        _a2: &HeaderMap,
151        _a3: (),
152        _a4: (),
153        _a5: (),
154        _a6: (),
155        _a7: (),
156        _a8: (),
157        _a9: (),
158        _a10: (),
159        _a11: (),
160        _a12: (),
161    ) {
162    }
163
164    #[test]
165    fn test_fn_is_fn_trait() {
166        assert_is_fn_trait(foo0);
167        assert_is_fn_trait(foo1);
168        assert_is_fn_trait(foo2);
169        assert_is_fn_trait(foo3);
170        assert_is_fn_trait(foo4);
171        assert_is_fn_trait(foo5);
172        assert_is_fn_trait(foo6);
173        assert_is_fn_trait(foo7);
174        assert_is_fn_trait(foo8);
175        assert_is_fn_trait(foo9);
176        assert_is_fn_trait(foo10);
177        assert_is_fn_trait(foo11);
178        assert_is_fn_trait(foo12);
179    }
180}