1#[allow(dead_code)]
4#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
5pub enum TaskPriority {
6 Low,
7 Normal,
8 High,
9 Critical,
10}
11
12#[allow(dead_code)]
13#[derive(Clone)]
14pub struct ScheduledTask {
15 pub id: u64,
16 pub name: String,
17 pub priority: TaskPriority,
18 pub scheduled_time: f64,
20 pub interval: Option<f64>,
22 pub enabled: bool,
23 pub run_count: u32,
24}
25
26#[allow(dead_code)]
27pub struct Scheduler {
28 pub tasks: Vec<ScheduledTask>,
29 pub current_time: f64,
30 pub next_id: u64,
31}
32
33#[allow(dead_code)]
38pub fn new_scheduler() -> Scheduler {
39 Scheduler {
40 tasks: Vec::new(),
41 current_time: 0.0,
42 next_id: 1,
43 }
44}
45
46#[allow(dead_code)]
51pub fn schedule_once(sched: &mut Scheduler, name: &str, delay: f64, priority: TaskPriority) -> u64 {
52 let id = sched.next_id;
53 sched.next_id += 1;
54 sched.tasks.push(ScheduledTask {
55 id,
56 name: name.to_string(),
57 priority,
58 scheduled_time: sched.current_time + delay,
59 interval: None,
60 enabled: true,
61 run_count: 0,
62 });
63 id
64}
65
66#[allow(dead_code)]
67pub fn schedule_repeating(
68 sched: &mut Scheduler,
69 name: &str,
70 interval: f64,
71 priority: TaskPriority,
72) -> u64 {
73 let id = sched.next_id;
74 sched.next_id += 1;
75 sched.tasks.push(ScheduledTask {
76 id,
77 name: name.to_string(),
78 priority,
79 scheduled_time: sched.current_time + interval,
80 interval: Some(interval),
81 enabled: true,
82 run_count: 0,
83 });
84 id
85}
86
87#[allow(dead_code)]
88pub fn cancel_task(sched: &mut Scheduler, id: u64) -> bool {
89 let before = sched.tasks.len();
90 sched.tasks.retain(|t| t.id != id);
91 sched.tasks.len() < before
92}
93
94#[allow(dead_code)]
100pub fn advance_time(sched: &mut Scheduler, dt: f64) -> Vec<ScheduledTask> {
101 sched.current_time += dt;
102 let current = sched.current_time;
103
104 let mut fired: Vec<ScheduledTask> = Vec::new();
105
106 for task in sched.tasks.iter_mut() {
107 if !task.enabled {
108 continue;
109 }
110 if task.scheduled_time <= current {
111 let snapshot = task.clone();
112 fired.push(snapshot);
113 task.run_count += 1;
114 if let Some(interval) = task.interval {
115 task.scheduled_time += interval;
117 }
118 }
119 }
120
121 fired.sort_by(|a, b| {
123 b.priority.cmp(&a.priority).then(
124 a.scheduled_time
125 .partial_cmp(&b.scheduled_time)
126 .unwrap_or(std::cmp::Ordering::Equal),
127 )
128 });
129
130 fired
131}
132
133#[allow(dead_code)]
138pub fn due_tasks(sched: &Scheduler) -> Vec<&ScheduledTask> {
139 sched
140 .tasks
141 .iter()
142 .filter(|t| t.enabled && t.scheduled_time <= sched.current_time)
143 .collect()
144}
145
146#[allow(dead_code)]
147pub fn task_count(sched: &Scheduler) -> usize {
148 sched.tasks.len()
149}
150
151#[allow(dead_code)]
152pub fn enabled_task_count(sched: &Scheduler) -> usize {
153 sched.tasks.iter().filter(|t| t.enabled).count()
154}
155
156#[allow(dead_code)]
157pub fn get_scheduled_task(sched: &Scheduler, id: u64) -> Option<&ScheduledTask> {
158 sched.tasks.iter().find(|t| t.id == id)
159}
160
161#[allow(dead_code)]
162pub fn set_task_enabled(sched: &mut Scheduler, id: u64, enabled: bool) {
163 if let Some(task) = sched.tasks.iter_mut().find(|t| t.id == id) {
164 task.enabled = enabled;
165 }
166}
167
168#[allow(dead_code)]
170pub fn tasks_by_priority(sched: &Scheduler) -> Vec<&ScheduledTask> {
171 let mut sorted: Vec<&ScheduledTask> = sched.tasks.iter().collect();
172 sorted.sort_by(|a, b| {
173 b.priority.cmp(&a.priority).then(
174 a.scheduled_time
175 .partial_cmp(&b.scheduled_time)
176 .unwrap_or(std::cmp::Ordering::Equal),
177 )
178 });
179 sorted
180}
181
182#[allow(dead_code)]
183pub fn next_due_time(sched: &Scheduler) -> Option<f64> {
184 sched
185 .tasks
186 .iter()
187 .filter(|t| t.enabled)
188 .map(|t| t.scheduled_time)
189 .reduce(f64::min)
190}
191
192#[allow(dead_code)]
193pub fn clear_completed_tasks(sched: &mut Scheduler) {
194 sched
196 .tasks
197 .retain(|t| !(t.interval.is_none() && t.run_count > 0));
198}
199
200#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn test_new_scheduler() {
210 let s = new_scheduler();
211 assert_eq!(task_count(&s), 0);
212 assert!((s.current_time).abs() < 1e-9);
213 }
214
215 #[test]
216 fn test_schedule_once() {
217 let mut s = new_scheduler();
218 let id = schedule_once(&mut s, "task_a", 1.0, TaskPriority::Normal);
219 assert_eq!(task_count(&s), 1);
220 assert!(get_scheduled_task(&s, id).is_some());
221 }
222
223 #[test]
224 fn test_schedule_repeating() {
225 let mut s = new_scheduler();
226 let id = schedule_repeating(&mut s, "rep", 0.5, TaskPriority::High);
227 let task = get_scheduled_task(&s, id).expect("should succeed");
228 assert!(task.interval.is_some());
229 }
230
231 #[test]
232 fn test_cancel_task() {
233 let mut s = new_scheduler();
234 let id = schedule_once(&mut s, "tmp", 1.0, TaskPriority::Low);
235 assert!(cancel_task(&mut s, id));
236 assert_eq!(task_count(&s), 0);
237 assert!(!cancel_task(&mut s, id));
238 }
239
240 #[test]
241 fn test_advance_time_fires_task() {
242 let mut s = new_scheduler();
243 schedule_once(&mut s, "go", 1.0, TaskPriority::Normal);
244 let fired = advance_time(&mut s, 1.5);
245 assert_eq!(fired.len(), 1);
246 assert_eq!(fired[0].name, "go");
247 }
248
249 #[test]
250 fn test_advance_time_no_fire_early() {
251 let mut s = new_scheduler();
252 schedule_once(&mut s, "future", 5.0, TaskPriority::Normal);
253 let fired = advance_time(&mut s, 1.0);
254 assert!(fired.is_empty());
255 }
256
257 #[test]
258 fn test_repeating_reschedules() {
259 let mut s = new_scheduler();
260 let id = schedule_repeating(&mut s, "rep", 1.0, TaskPriority::Normal);
261 let fired1 = advance_time(&mut s, 1.1);
262 assert_eq!(fired1.len(), 1);
263 assert!(get_scheduled_task(&s, id).is_some());
265 let task = get_scheduled_task(&s, id).expect("should succeed");
266 assert!(task.scheduled_time > s.current_time - 0.001);
267 }
268
269 #[test]
270 fn test_task_count() {
271 let mut s = new_scheduler();
272 schedule_once(&mut s, "a", 1.0, TaskPriority::Low);
273 schedule_once(&mut s, "b", 2.0, TaskPriority::High);
274 assert_eq!(task_count(&s), 2);
275 }
276
277 #[test]
278 fn test_due_tasks() {
279 let mut s = new_scheduler();
280 schedule_once(&mut s, "now", 0.0, TaskPriority::Normal);
281 schedule_once(&mut s, "later", 10.0, TaskPriority::Normal);
282 let due = due_tasks(&s);
283 assert_eq!(due.len(), 1);
284 assert_eq!(due[0].name, "now");
285 }
286
287 #[test]
288 fn test_tasks_by_priority_order() {
289 let mut s = new_scheduler();
290 schedule_once(&mut s, "low", 1.0, TaskPriority::Low);
291 schedule_once(&mut s, "crit", 1.0, TaskPriority::Critical);
292 schedule_once(&mut s, "norm", 1.0, TaskPriority::Normal);
293 let sorted = tasks_by_priority(&s);
294 assert_eq!(sorted[0].priority, TaskPriority::Critical);
295 assert_eq!(sorted[sorted.len() - 1].priority, TaskPriority::Low);
296 }
297
298 #[test]
299 fn test_next_due_time() {
300 let mut s = new_scheduler();
301 schedule_once(&mut s, "a", 3.0, TaskPriority::Normal);
302 schedule_once(&mut s, "b", 1.0, TaskPriority::Normal);
303 let next = next_due_time(&s).expect("should succeed");
304 assert!((next - 1.0).abs() < 1e-9);
305 }
306
307 #[test]
308 fn test_next_due_time_empty() {
309 let s = new_scheduler();
310 assert!(next_due_time(&s).is_none());
311 }
312
313 #[test]
314 fn test_clear_completed_tasks() {
315 let mut s = new_scheduler();
316 schedule_once(&mut s, "one_shot", 0.5, TaskPriority::Normal);
317 schedule_repeating(&mut s, "rep", 1.0, TaskPriority::Normal);
318 advance_time(&mut s, 1.5);
319 clear_completed_tasks(&mut s);
320 assert_eq!(task_count(&s), 1);
322 assert_eq!(s.tasks[0].name, "rep");
323 }
324
325 #[test]
326 fn test_enabled_task_count() {
327 let mut s = new_scheduler();
328 let id1 = schedule_once(&mut s, "a", 1.0, TaskPriority::Normal);
329 schedule_once(&mut s, "b", 1.0, TaskPriority::Normal);
330 set_task_enabled(&mut s, id1, false);
331 assert_eq!(enabled_task_count(&s), 1);
332 }
333
334 #[test]
335 fn test_set_task_enabled() {
336 let mut s = new_scheduler();
337 let id = schedule_once(&mut s, "x", 0.0, TaskPriority::Normal);
338 set_task_enabled(&mut s, id, false);
339 let fired = advance_time(&mut s, 1.0);
340 assert!(fired.is_empty());
342 }
343}