oxihuman_core/
task_scheduler.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub enum RecurrenceRule {
12 Once,
13 EveryMs(u64),
14}
15
16#[derive(Debug, Clone)]
18pub struct SchedulerTask {
19 pub id: u64,
20 pub name: String,
21 pub next_run_ms: u64,
22 pub rule: RecurrenceRule,
23 pub enabled: bool,
24 pub run_count: u64,
25}
26
27#[derive(Debug, Default)]
29pub struct TaskScheduler {
30 tasks: HashMap<u64, SchedulerTask>,
31 next_id: u64,
32 current_time_ms: u64,
33}
34
35impl TaskScheduler {
36 pub fn new() -> Self {
37 Self::default()
38 }
39
40 pub fn schedule(&mut self, name: &str, first_run_ms: u64, rule: RecurrenceRule) -> u64 {
41 let id = self.next_id;
42 self.next_id += 1;
43 self.tasks.insert(
44 id,
45 SchedulerTask {
46 id,
47 name: name.to_string(),
48 next_run_ms: first_run_ms,
49 rule,
50 enabled: true,
51 run_count: 0,
52 },
53 );
54 id
55 }
56
57 pub fn advance(&mut self, time_ms: u64) -> Vec<u64> {
58 self.current_time_ms = time_ms;
60 let mut fired = Vec::new();
61 for task in self.tasks.values_mut() {
62 if !task.enabled || task.next_run_ms > time_ms {
63 continue;
64 }
65 fired.push(task.id);
66 task.run_count += 1;
67 match &task.rule {
68 RecurrenceRule::Once => {
69 task.enabled = false;
70 }
71 RecurrenceRule::EveryMs(interval) => {
72 task.next_run_ms = time_ms + interval;
73 }
74 }
75 }
76 fired
77 }
78
79 pub fn cancel(&mut self, id: u64) -> bool {
80 if let Some(t) = self.tasks.get_mut(&id) {
81 t.enabled = false;
82 true
83 } else {
84 false
85 }
86 }
87
88 pub fn task_count(&self) -> usize {
89 self.tasks.len()
90 }
91
92 pub fn enabled_count(&self) -> usize {
93 self.tasks.values().filter(|t| t.enabled).count()
94 }
95
96 pub fn run_count(&self, id: u64) -> u64 {
97 self.tasks.get(&id).map(|t| t.run_count).unwrap_or(0)
98 }
99}
100
101pub fn new_task_scheduler() -> TaskScheduler {
102 TaskScheduler::new()
103}
104
105pub fn ts_schedule(
106 sched: &mut TaskScheduler,
107 name: &str,
108 first_ms: u64,
109 rule: RecurrenceRule,
110) -> u64 {
111 sched.schedule(name, first_ms, rule)
112}
113
114pub fn ts_advance(sched: &mut TaskScheduler, time_ms: u64) -> Vec<u64> {
115 sched.advance(time_ms)
116}
117
118pub fn ts_cancel(sched: &mut TaskScheduler, id: u64) -> bool {
119 sched.cancel(id)
120}
121
122pub fn ts_task_count(sched: &TaskScheduler) -> usize {
123 sched.task_count()
124}
125
126pub fn ts_run_count(sched: &TaskScheduler, id: u64) -> u64 {
127 sched.run_count(id)
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_schedule_once() {
136 let mut s = new_task_scheduler();
137 let id = ts_schedule(&mut s, "job", 100, RecurrenceRule::Once);
138 let fired = ts_advance(&mut s, 200);
139 assert!(fired.contains(&id));
140 }
141
142 #[test]
143 fn test_once_fires_only_once() {
144 let mut s = new_task_scheduler();
145 let id = ts_schedule(&mut s, "job", 50, RecurrenceRule::Once);
146 ts_advance(&mut s, 100);
147 let fired2 = ts_advance(&mut s, 200);
148 assert!(!fired2.contains(&id));
149 }
150
151 #[test]
152 fn test_repeating_fires_multiple_times() {
153 let mut s = new_task_scheduler();
154 let id = ts_schedule(&mut s, "tick", 100, RecurrenceRule::EveryMs(100));
155 ts_advance(&mut s, 100);
156 ts_advance(&mut s, 200);
157 assert_eq!(ts_run_count(&s, id), 2);
158 }
159
160 #[test]
161 fn test_before_first_run_not_fired() {
162 let mut s = new_task_scheduler();
163 let id = ts_schedule(&mut s, "future", 1000, RecurrenceRule::Once);
164 let fired = ts_advance(&mut s, 500);
165 assert!(!fired.contains(&id));
166 }
167
168 #[test]
169 fn test_cancel_prevents_firing() {
170 let mut s = new_task_scheduler();
171 let id = ts_schedule(&mut s, "j", 100, RecurrenceRule::Once);
172 ts_cancel(&mut s, id);
173 let fired = ts_advance(&mut s, 200);
174 assert!(!fired.contains(&id));
175 }
176
177 #[test]
178 fn test_task_count() {
179 let mut s = new_task_scheduler();
180 ts_schedule(&mut s, "a", 0, RecurrenceRule::Once);
181 ts_schedule(&mut s, "b", 0, RecurrenceRule::Once);
182 assert_eq!(ts_task_count(&s), 2);
183 }
184
185 #[test]
186 fn test_enabled_count_decreases_after_once() {
187 let mut s = new_task_scheduler();
188 ts_schedule(&mut s, "x", 10, RecurrenceRule::Once);
189 assert_eq!(s.enabled_count(), 1);
190 ts_advance(&mut s, 100);
191 assert_eq!(s.enabled_count(), 0);
192 }
193
194 #[test]
195 fn test_run_count_starts_at_zero() {
196 let mut s = new_task_scheduler();
197 let id = ts_schedule(&mut s, "z", 1000, RecurrenceRule::Once);
198 assert_eq!(ts_run_count(&s, id), 0);
199 }
200
201 #[test]
202 fn test_unknown_id_run_count_zero() {
203 let s = new_task_scheduler();
204 assert_eq!(ts_run_count(&s, 999), 0);
205 }
206}