links_core/scheduler/
task.rs1use chrono::{DateTime, Utc};
2use std::{
3 cmp::Reverse,
4 fmt::{Debug, Display},
5 time::Duration,
6};
7
8use crate::asserted_short_name;
9
10#[derive(Debug, PartialEq)]
12pub enum TimerTaskStatus {
13 Completed,
14 Terminate,
15 RetryAfter(Duration),
16}
17
18pub type Executable = Box<dyn FnMut() -> TimerTaskStatus + Send + 'static>;
19
20pub struct Task {
21 name: String,
22 execute_at: DateTime<Utc>,
23 interval: Duration,
24 executable: Executable,
25}
26impl Task {
27 pub fn new(name: &str, interval: Duration, task: Executable) -> Self {
28 Task {
29 name: name.to_owned(),
30 execute_at: chrono::Utc::now(),
31 interval,
32 executable: task,
33 }
34 }
35 pub fn execute(&mut self) -> TimerTaskStatus {
37 (self.executable)()
38 }
39 pub fn reschedule(&mut self) {
41 self.execute_at += self.interval;
42 }
43 pub fn reschedule_with_interval(&mut self, interval: Duration) {
45 self.execute_at += interval;
46 }
47 pub fn execute_at(&self) -> &DateTime<Utc> {
49 &self.execute_at
50 }
51}
52impl Display for Task {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct(asserted_short_name!("Task", Self))
55 .field("name", &self.name)
56 .field("execute_at", &self.execute_at)
57 .field("interval", &self.interval)
58 .finish()
59 }
60}
61impl Debug for Task {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct(asserted_short_name!("Task", Self))
64 .field("name", &self.name)
65 .field("execute_at", &self.execute_at)
66 .field("interval", &self.interval)
67 .field("executable", &"dyn FnMut() -> TaskStatus")
68 .finish()
69 }
70}
71impl Ord for Task {
72 #[inline]
73 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
74 Reverse(self.execute_at).cmp(&Reverse(other.execute_at))
75 }
76}
77impl PartialOrd for Task {
78 #[inline]
79 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
80 Some(Reverse(self.execute_at).cmp(&Reverse(other.execute_at)))
81 }
82}
83impl PartialEq for Task {
84 #[inline]
85 fn eq(&self, other: &Self) -> bool {
86 self.execute_at == other.execute_at
87 }
88}
89impl Eq for Task {}
90
91#[cfg(test)]
92mod test {
93 use super::*;
94 use crate::unittest::setup;
95 use log::info;
96 use std::collections::BinaryHeap;
97
98 #[test]
99 fn test_task_schedule() {
100 setup::log::configure_compact(log::LevelFilter::Info);
101
102 let mut schedules = BinaryHeap::new();
105 let interval = Duration::from_secs(1);
106 schedules.push(Task::new("Task1", interval, Box::new(|| TimerTaskStatus::Completed)));
107 schedules.push(Task::new("Task2", interval, Box::new(|| TimerTaskStatus::Completed)));
108 schedules.push(Task::new("Task3", interval, Box::new(|| TimerTaskStatus::Completed)));
109
110 let task = schedules.pop().unwrap();
111 info!("task: {}", task);
112 assert_eq!(task.name, "Task1");
113
114 let task = schedules.pop().unwrap();
115 info!("task: {}", task);
116 assert_eq!(task.name, "Task2");
117
118 let mut task = schedules.pop().unwrap();
119 info!("task: {}", task);
120 assert_eq!(task.name, "Task3");
121
122 let last_execution = task.execute_at;
124 task.reschedule();
125 info!("task: {:?}", task);
126 assert_eq!(task.execute_at, last_execution + interval);
127 }
128}