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 type Future: Future<Output = ()> + Send + 'static;
15
16 fn call(self, job_id: JobId, scheduler: JobScheduler, app: Arc<App>) -> Self::Future;
18}
19
20impl<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#[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
164pub 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}