1use std::future::Future;
2use std::marker::PhantomData;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use crate::context::Context;
7use crate::extractor::FromContext;
8use crate::responder::IntoResponse;
9use crate::response::Response;
10
11#[cfg(not(target_arch = "wasm32"))]
12type HandlerCallFuture<'a> = Pin<Box<dyn Future<Output = Response> + Send + 'a>>;
13#[cfg(target_arch = "wasm32")]
14type HandlerCallFuture<'a> = Pin<Box<dyn Future<Output = Response> + 'a>>;
15
16#[cfg(not(target_arch = "wasm32"))]
17pub trait HandlerCallFutureBounds: Future + Send {}
18#[cfg(not(target_arch = "wasm32"))]
19impl<T: Future + Send + ?Sized> HandlerCallFutureBounds for T {}
20
21#[cfg(target_arch = "wasm32")]
22pub trait HandlerCallFutureBounds: Future {}
23#[cfg(target_arch = "wasm32")]
24impl<T: Future + ?Sized> HandlerCallFutureBounds for T {}
25
26#[cfg(not(target_arch = "wasm32"))]
27pub trait HandlerBounds: Send + Sync {}
28#[cfg(not(target_arch = "wasm32"))]
29impl<T: Send + Sync + ?Sized> HandlerBounds for T {}
30
31#[cfg(target_arch = "wasm32")]
32pub trait HandlerBounds {}
33#[cfg(target_arch = "wasm32")]
34impl<T: ?Sized> HandlerBounds for T {}
35
36#[cfg(not(target_arch = "wasm32"))]
37pub trait HandlerFnBounds: Send + Sync {}
38#[cfg(not(target_arch = "wasm32"))]
39impl<T: Send + Sync + ?Sized> HandlerFnBounds for T {}
40
41#[cfg(target_arch = "wasm32")]
42pub trait HandlerFnBounds {}
43#[cfg(target_arch = "wasm32")]
44impl<T: ?Sized> HandlerFnBounds for T {}
45
46#[cfg(not(target_arch = "wasm32"))]
47pub trait HandlerFutureBounds: Future + Send {}
48#[cfg(not(target_arch = "wasm32"))]
49impl<T: Future + Send + ?Sized> HandlerFutureBounds for T {}
50
51#[cfg(target_arch = "wasm32")]
52pub trait HandlerFutureBounds: Future {}
53#[cfg(target_arch = "wasm32")]
54impl<T: Future + ?Sized> HandlerFutureBounds for T {}
55
56pub trait Handler: HandlerBounds + 'static {
85 fn call(&self, ctx: Context) -> impl HandlerCallFutureBounds<Output = Response> + '_;
87}
88
89trait HandlerImpl: HandlerBounds {
91 fn call_boxed<'a>(&'a self, ctx: Context) -> HandlerCallFuture<'a>;
92}
93
94impl<T: Handler> HandlerImpl for T {
95 fn call_boxed<'a>(&'a self, ctx: Context) -> HandlerCallFuture<'a> {
96 Box::pin(Handler::call(self, ctx))
97 }
98}
99
100#[derive(Clone)]
105pub struct AnyHandler {
106 inner: Arc<dyn HandlerImpl>,
107}
108
109impl AnyHandler {
110 pub fn new(handler: impl Handler) -> Self {
112 Self {
113 inner: Arc::new(handler),
114 }
115 }
116
117 pub fn call(&self, ctx: Context) -> impl HandlerCallFutureBounds<Output = Response> + '_ {
119 self.inner.call_boxed(ctx)
120 }
121}
122
123pub trait IntoHandler<Args> {
125 fn into_handler(self) -> AnyHandler;
127}
128
129struct FnHandler<F, Args>(F, PhantomData<fn() -> Args>);
135
136impl<F, Args> FnHandler<F, Args> {
137 fn new(f: F) -> Self {
138 Self(f, PhantomData)
139 }
140}
141
142macro_rules! impl_into_handler {
144 ($($ty:ident $arg:ident),*) => {
145 impl<F, Fut, R, $($ty,)*> IntoHandler<($($ty,)*)> for F
146 where
147 F: Fn($($ty,)*) -> Fut + HandlerFnBounds + 'static,
148 Fut: HandlerFutureBounds<Output = R> + 'static,
149 R: IntoResponse + 'static,
150 $($ty: FromContext + 'static,)*
151 {
152 fn into_handler(self) -> AnyHandler {
153 AnyHandler::new(FnHandler::new(self))
154 }
155 }
156
157 impl<F, Fut, R, $($ty,)*> Handler for FnHandler<F, ($($ty,)*)>
158 where
159 F: Fn($($ty,)*) -> Fut + HandlerFnBounds + 'static,
160 Fut: HandlerFutureBounds<Output = R> + 'static,
161 R: IntoResponse + 'static,
162 $($ty: FromContext + 'static,)*
163 {
164 fn call(&self, ctx: Context) -> impl HandlerCallFutureBounds<Output = Response> + '_ {
165 Box::pin(async move {
166 let _ = &ctx;
168 $(let $arg = $ty::from_context(&ctx).await;)*
169 (self.0)($($arg,)*).await.into_response()
170 })
171 }
172 }
173 };
174}
175
176impl_into_handler!();
177impl_into_handler!(T1 t1);
178impl_into_handler!(T1 t1, T2 t2);
179impl_into_handler!(T1 t1, T2 t2, T3 t3);
180impl_into_handler!(T1 t1, T2 t2, T3 t3, T4 t4);
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use crate::extractor::{Channel, CommandArgs, CommandName, User};
186 use crate::test_util::StubData;
187 use futures_lite::future::block_on;
188
189 fn call<H: IntoHandler<Args>, Args>(handler: H) -> Response {
190 let handler = handler.into_handler();
191 block_on(handler.call(Context::new(StubData)))
192 }
193
194 #[test]
195 fn zero_arg_handler() {
196 async fn ping() -> &'static str {
197 "Pong!"
198 }
199 assert_eq!(call(ping).content(), Some("Pong!"));
200 }
201
202 #[test]
203 fn extractors_are_applied_in_order() {
204 async fn four(a: CommandName, b: CommandArgs, c: User, d: Channel) -> String {
205 format!("{} {} {} {}", a.0, b.0, c.name, d.id)
206 }
207 assert_eq!(
208 call(four).content(),
209 Some("cmd args stub-user stub-channel")
210 );
211 }
212
213 #[test]
214 fn closures_are_handlers_too() {
215 let handler = || async { String::from("closure") };
216 assert_eq!(call(handler).content(), Some("closure"));
217 }
218
219 #[test]
220 fn handlers_may_capture_non_thread_safe_extractors() {
221 async fn with_ctx(ctx: Context) -> String {
224 ctx.user_id().to_string()
225 }
226 assert_eq!(call(with_ctx).content(), Some("stub-user"));
227 }
228}