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}