tsuki_scheduler/runtime/
mod.rs

1#[cfg(feature = "async-std")]
2mod async_std;
3use std::{future::Future, time::Duration};
4
5#[cfg(feature = "async-std")]
6pub use async_std::*;
7#[cfg(feature = "tokio")]
8mod tokio;
9#[cfg(feature = "tokio")]
10pub use tokio::*;
11#[cfg(feature = "thread")]
12mod thread;
13#[cfg(feature = "thread")]
14pub use thread::*;
15#[cfg(feature = "promise")]
16mod promise;
17#[cfg(feature = "promise")]
18pub use promise::*;
19
20mod local;
21pub use local::*;
22
23use crate::{Dtu, Task, TaskRun, TaskUid, prelude::IntoSchedule};
24
25pub trait Runtime {
26    type Handle;
27}
28
29pub trait AsyncRuntime: Runtime + Send + Sync {
30    /// wake runner after a duration
31    fn wake_after(&self, duration: Duration, ctx: &mut std::task::Context<'_>);
32    fn spawn<F>(task: F) -> Self::Handle
33    where
34        F: Future<Output = ()> + Send + 'static;
35}
36
37impl<R: Runtime> Task<R> {
38    /// Create a new task.
39    ///
40    ///
41    /// The run function could have the following signature:
42    ///
43    /// `Fn(<&mut Runtime>?, ..Args)`
44    ///
45    /// the Args could be any type that implements `TaskRunArg`.
46    ///
47    /// For now, these types are supported:
48    /// * [`TaskRun`] : information about this run.
49    /// * [`TaskUid`] : the task unique identifier.
50    /// * [`Dtu`] : the time when this task is scheduled to run.
51    ///
52    /// For example, you can call this function like this:
53    ///
54    ///
55    /// ```rust
56    /// # use tsuki_scheduler::prelude::*;
57    /// let task = Task::<Local>::new(now(), |time: Dtu, id: TaskUid| {
58    ///    println!("Time: {time}, Uid: {id}");
59    /// });
60    /// ```
61    ///
62    ///
63    pub fn new<S, F, A>(schedule: S, run: F) -> Self
64    where
65        S: IntoSchedule,
66        F: IntoRunTaskFn<R, A>,
67    {
68        Self {
69            schedule: Box::new(schedule.into_schedule()),
70            run: Box::new(run.convert()),
71        }
72    }
73}
74impl<R: AsyncRuntime> Task<R> {
75    /// This is basically the same as [`Task::new`] but with more hint for compiler that this is an async function.
76    #[allow(private_bounds)]
77    pub fn new_async<S, F, A, Fut>(schedule: S, run: F) -> Self
78    where
79        S: IntoSchedule,
80        F: IntoRunTaskFn<R, Async<A, Fut>>,
81    {
82        Self {
83            schedule: Box::new(schedule.into_schedule()),
84            run: Box::new(run.convert()),
85        }
86    }
87}
88
89pub trait IntoRunTaskFn<R: Runtime, A> {
90    fn convert(self) -> impl Fn(&mut R, &TaskRun) -> <R as Runtime>::Handle + Send + 'static;
91}
92
93pub trait AsyncTaskFn<R: Runtime, A> {
94    type Future: std::future::Future<Output = ()> + Send + 'static;
95    fn convert(self) -> impl Fn(&mut R, &TaskRun) -> Self::Future;
96}
97
98pub trait TaskRunArg: Sized {
99    fn extract(task_run: &TaskRun) -> Self;
100}
101
102impl TaskRunArg for TaskRun {
103    fn extract(task_run: &TaskRun) -> Self {
104        task_run.clone()
105    }
106}
107
108impl TaskRunArg for TaskUid {
109    fn extract(task_run: &TaskRun) -> Self {
110        task_run.key
111    }
112}
113
114impl TaskRunArg for Dtu {
115    fn extract(task_run: &TaskRun) -> Self {
116        task_run.time
117    }
118}
119
120struct WithRuntime<A>(A);
121struct Async<A, F>(A, F);
122
123macro_rules! impl_for {
124    ($($T: ident)*) => {
125        impl_for!([][$($T)*]);
126    };
127    ([$($T: ident)*][$TN: ident $($Rest: ident)*]) => {
128        impl_for!(@impl [$($T)*]);
129        impl_for!([$($T)* $TN][$($Rest)*]);
130    };
131    ([$($T: ident)*][]) => {
132        impl_for!(@impl [$($T)*]);
133    };
134    (@impl [$($T: ident)*]) => {
135        impl<R, F, $($T,)*> IntoRunTaskFn<R, ($($T,)*)> for F
136        where
137            R: Runtime,
138            F: FnOnce($($T,)*) -> R::Handle + Send + 'static + Clone,
139            $($T: TaskRunArg,)*
140        {
141            #[allow(unused_variables)]
142            fn convert(
143                self,
144            ) -> impl Fn(&mut R, &TaskRun) -> <R as Runtime>::Handle + Send + 'static {
145                move |_, task_run| (self.clone())($($T::extract(task_run),)*)
146            }
147        }
148        impl<R, F, $($T,)*> IntoRunTaskFn<R, WithRuntime<($($T,)*)>> for F
149        where
150            R: Runtime,
151            F: FnOnce(&mut R, $($T,)*) -> R::Handle + Send + Sync + 'static + Clone,
152            $($T: TaskRunArg,)*
153        {
154            #[allow(unused_variables)]
155            fn convert(
156                self,
157            ) -> impl Fn(&mut R, &TaskRun) -> <R as Runtime>::Handle + Send+ 'static {
158                move |run_time, task_run| (self.clone())(run_time, $($T::extract(task_run),)*)
159            }
160        }
161        impl<R, F, Fut, $($T,)*> IntoRunTaskFn<R, Async<($($T,)*), Fut>> for F
162        where
163            R: AsyncRuntime,
164            F: FnOnce($($T,)*) -> Fut + Send + 'static + Clone,
165            Fut: std::future::Future<Output = ()> + Send + 'static ,
166            $($T: TaskRunArg,)*
167        {
168            #[allow(unused_variables)]
169            fn convert(
170                self,
171            ) -> impl Fn(&mut R, &TaskRun) -> <R as Runtime>::Handle + Send + 'static {
172                move |_, task_run| R::spawn((self.clone())($($T::extract(task_run),)*))
173            }
174        }
175    }
176}
177
178impl_for!(
179    T0 T1 T2 T3
180);