ferro_rs/schedule/
task.rs1use super::expression::CronExpression;
7use crate::error::FrameworkError;
8use async_trait::async_trait;
9use std::future::Future;
10use std::pin::Pin;
11use std::sync::Arc;
12
13pub type BoxedTask = Arc<dyn TaskHandler + Send + Sync>;
15
16pub type TaskResult = Result<(), FrameworkError>;
18
19pub type BoxedFuture<'a> = Pin<Box<dyn Future<Output = TaskResult> + Send + 'a>>;
21
22#[async_trait]
26pub trait TaskHandler: Send + Sync {
27 async fn handle(&self) -> TaskResult;
29}
30
31#[async_trait]
68pub trait Task: Send + Sync {
69 async fn handle(&self) -> TaskResult;
71}
72
73#[async_trait]
75impl<T: Task> TaskHandler for T {
76 async fn handle(&self) -> TaskResult {
77 Task::handle(self).await
78 }
79}
80
81pub struct TaskEntry {
86 pub name: String,
88 pub expression: CronExpression,
90 pub task: BoxedTask,
92 pub description: Option<String>,
94 pub without_overlapping: bool,
96 pub run_in_background: bool,
98}
99
100impl TaskEntry {
101 pub fn is_due(&self) -> bool {
103 self.expression.is_due()
104 }
105
106 pub async fn run(&self) -> TaskResult {
108 self.task.handle().await
109 }
110
111 pub fn schedule_description(&self) -> &str {
113 self.expression.expression()
114 }
115}
116
117pub(crate) struct ClosureTask<F>
119where
120 F: Fn() -> BoxedFuture<'static> + Send + Sync,
121{
122 pub(crate) handler: F,
123}
124
125#[async_trait]
126impl<F> TaskHandler for ClosureTask<F>
127where
128 F: Fn() -> BoxedFuture<'static> + Send + Sync,
129{
130 async fn handle(&self) -> TaskResult {
131 (self.handler)().await
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 struct TestTask;
140
141 #[async_trait]
142 impl Task for TestTask {
143 async fn handle(&self) -> TaskResult {
144 Ok(())
145 }
146 }
147
148 #[tokio::test]
149 async fn test_task_trait() {
150 let task = TestTask;
151
152 let result: TaskResult = Task::handle(&task).await;
153 assert!(result.is_ok());
154 }
155
156 #[tokio::test]
157 async fn test_task_entry() {
158 let task = TestTask;
159 let entry = TaskEntry {
160 name: "test-task".to_string(),
161 expression: CronExpression::every_minute(),
162 task: Arc::new(task),
163 description: Some("A test task".to_string()),
164 without_overlapping: false,
165 run_in_background: false,
166 };
167
168 assert_eq!(entry.name, "test-task");
169 assert_eq!(entry.schedule_description(), "* * * * *");
170
171 let result = entry.run().await;
172 assert!(result.is_ok());
173 }
174}