spring_job/
handler.rs

1pub use inventory::submit;
2
3use crate::JobScheduler;
4use crate::{extractor::FromApp, JobId, Jobs};
5use spring::app::App;
6use std::pin::Pin;
7use std::{
8    future::Future,
9    sync::{Arc, Mutex},
10};
11
12pub trait Handler<T>: Clone + Send + Sized + 'static {
13    /// The type of future calling this handler returns.
14    type Future: Future<Output = ()> + Send + 'static;
15
16    /// Call the handler with the given request.
17    fn call(self, job_id: JobId, scheduler: JobScheduler, app: Arc<App>) -> Self::Future;
18}
19
20/// no args handler impl
21impl<F, Fut> Handler<()> for F
22where
23    F: FnOnce() -> Fut + Clone + Send + 'static,
24    Fut: Future<Output = ()> + Send,
25{
26    type Future = Pin<Box<dyn Future<Output = ()> + Send>>;
27
28    fn call(self, _job_id: JobId, _scheduler: JobScheduler, _app: Arc<App>) -> Self::Future {
29        Box::pin(async move {
30            self().await;
31        })
32    }
33}
34
35/// 1~15 args handler impl
36#[rustfmt::skip]
37macro_rules! all_the_tuples {
38    ($name:ident) => {
39        $name!([T1]);
40        $name!([T1, T2]);
41        $name!([T1, T2, T3]);
42        $name!([T1, T2, T3, T4]);
43        $name!([T1, T2, T3, T4, T5]);
44        $name!([T1, T2, T3, T4, T5, T6]);
45        $name!([T1, T2, T3, T4, T5, T6, T7]);
46        $name!([T1, T2, T3, T4, T5, T6, T7, T8]);
47        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9]);
48        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]);
49        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]);
50        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]);
51        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]);
52        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]);
53        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]);
54    };
55}
56
57macro_rules! impl_handler {
58    (
59        [$($ty:ident),*]
60    ) => {
61        #[allow(non_snake_case, unused_mut)]
62        impl<F, Fut, $($ty,)*> Handler<($($ty,)*)> for F
63        where
64            F: FnOnce($($ty,)*) -> Fut + Clone + Send + 'static,
65            Fut: Future<Output = ()> + Send,
66            $( $ty: FromApp + Send, )*
67        {
68            type Future = Pin<Box<dyn Future<Output = ()> + Send>>;
69
70            fn call(self, job_id: JobId, scheduler: JobScheduler, app: Arc<App>) -> Self::Future {
71                Box::pin(async move {
72                    $(
73                        let $ty = $ty::from_app(&job_id, &scheduler, &app).await;
74                    )*
75
76                    self($($ty,)*).await;
77                })
78            }
79        }
80    };
81}
82
83all_the_tuples!(impl_handler);
84
85pub(crate) struct BoxedHandler(Mutex<Box<dyn ErasedHandler>>);
86
87impl Clone for BoxedHandler {
88    fn clone(&self) -> Self {
89        Self(Mutex::new(self.0.lock().unwrap().clone_box()))
90    }
91}
92
93impl BoxedHandler {
94    pub(crate) fn from_handler<H, T>(handler: H) -> Self
95    where
96        H: Handler<T> + Sync,
97        T: 'static,
98    {
99        Self(Mutex::new(Box::new(MakeErasedHandler {
100            handler,
101            caller: |handler, job_id, jobs, app| Box::pin(H::call(handler, job_id, jobs, app)),
102        })))
103    }
104
105    pub(crate) fn call(
106        self,
107        job_id: JobId,
108        scheduler: JobScheduler,
109        app: Arc<App>,
110    ) -> Pin<Box<dyn Future<Output = ()> + Send>> {
111        self.0.into_inner().unwrap().call(job_id, scheduler, app)
112    }
113}
114
115pub(crate) trait ErasedHandler: Send {
116    fn clone_box(&self) -> Box<dyn ErasedHandler>;
117
118    fn call(
119        self: Box<Self>,
120        job_id: JobId,
121        scheduler: JobScheduler,
122        app: Arc<App>,
123    ) -> Pin<Box<dyn Future<Output = ()> + Send>>;
124}
125
126type HandlerCaller<H> =
127    fn(H, JobId, JobScheduler, Arc<App>) -> Pin<Box<dyn Future<Output = ()> + Send>>;
128
129pub(crate) struct MakeErasedHandler<H> {
130    pub(crate) handler: H,
131    pub(crate) caller: HandlerCaller<H>,
132}
133
134impl<H> Clone for MakeErasedHandler<H>
135where
136    H: Clone,
137{
138    fn clone(&self) -> Self {
139        Self {
140            handler: self.handler.clone(),
141            caller: self.caller,
142        }
143    }
144}
145
146impl<H> ErasedHandler for MakeErasedHandler<H>
147where
148    H: Clone + Send + Sync + 'static,
149{
150    fn clone_box(&self) -> Box<dyn ErasedHandler> {
151        Box::new(self.clone())
152    }
153
154    fn call(
155        self: Box<Self>,
156        job_id: JobId,
157        scheduler: JobScheduler,
158        app: Arc<App>,
159    ) -> Pin<Box<dyn Future<Output = ()> + Send>> {
160        (self.caller)(self.handler, job_id, scheduler, app)
161    }
162}
163
164/// TypeHandler is used to configure the spring-macro marked job handler
165pub trait TypedHandlerRegistrar: Send + Sync + 'static {
166    fn install_job(&self, jobs: Jobs) -> Jobs;
167}
168
169pub trait TypedJob {
170    fn typed_job<F: TypedHandlerRegistrar>(self, factory: F) -> Self;
171}
172
173impl TypedJob for Jobs {
174    fn typed_job<F: TypedHandlerRegistrar>(self, factory: F) -> Self {
175        factory.install_job(self)
176    }
177}
178
179inventory::collect!(&'static dyn TypedHandlerRegistrar);
180
181#[macro_export]
182macro_rules! submit_typed_handler {
183    ($ty:ident) => {
184        ::spring_job::handler::submit! {
185            &$ty as &dyn ::spring_job::handler::TypedHandlerRegistrar
186        }
187    };
188}
189
190pub fn auto_jobs() -> Jobs {
191    let mut jobs = Jobs::new();
192    for factory in inventory::iter::<&dyn TypedHandlerRegistrar> {
193        jobs = factory.install_job(jobs);
194    }
195    jobs
196}