oxihuman_core/
schedule_queue.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct ScheduleTask {
10 pub id: u64,
11 pub name: String,
12 pub due_at: u64,
13 pub repeat_interval: Option<u64>,
14 pub enabled: bool,
15}
16
17pub struct ScheduleQueue {
19 tasks: Vec<ScheduleTask>,
20 now: u64,
21 next_id: u64,
22 fired_count: u64,
23}
24
25#[allow(dead_code)]
26impl ScheduleQueue {
27 pub fn new() -> Self {
28 ScheduleQueue {
29 tasks: Vec::new(),
30 now: 0,
31 next_id: 0,
32 fired_count: 0,
33 }
34 }
35
36 pub fn schedule_once(&mut self, name: &str, due_at: u64) -> u64 {
37 let id = self.next_id;
38 self.next_id += 1;
39 let pos = self.tasks.partition_point(|t| t.due_at <= due_at);
40 self.tasks.insert(
41 pos,
42 ScheduleTask {
43 id,
44 name: name.to_string(),
45 due_at,
46 repeat_interval: None,
47 enabled: true,
48 },
49 );
50 id
51 }
52
53 pub fn schedule_repeating(&mut self, name: &str, due_at: u64, interval: u64) -> u64 {
54 let id = self.next_id;
55 self.next_id += 1;
56 let pos = self.tasks.partition_point(|t| t.due_at <= due_at);
57 self.tasks.insert(
58 pos,
59 ScheduleTask {
60 id,
61 name: name.to_string(),
62 due_at,
63 repeat_interval: Some(interval),
64 enabled: true,
65 },
66 );
67 id
68 }
69
70 pub fn advance(&mut self, dt: u64) -> Vec<ScheduleTask> {
71 self.now += dt;
72 let mut fired: Vec<ScheduleTask> = Vec::new();
73 let mut to_reschedule: Vec<(String, u64, u64)> = Vec::new();
74
75 self.tasks.retain(|t| {
76 if t.enabled && t.due_at <= self.now {
77 fired.push(t.clone());
78 if let Some(interval) = t.repeat_interval {
79 to_reschedule.push((t.name.clone(), self.now + interval, interval));
80 }
81 false
82 } else {
83 true
84 }
85 });
86
87 self.fired_count += fired.len() as u64;
88
89 for (name, due, interval) in to_reschedule {
90 self.schedule_repeating(&name, due, interval);
91 }
92
93 fired
94 }
95
96 pub fn cancel(&mut self, id: u64) -> bool {
97 let before = self.tasks.len();
98 self.tasks.retain(|t| t.id != id);
99 self.tasks.len() < before
100 }
101
102 pub fn set_enabled(&mut self, id: u64, enabled: bool) -> bool {
103 if let Some(t) = self.tasks.iter_mut().find(|t| t.id == id) {
104 t.enabled = enabled;
105 true
106 } else {
107 false
108 }
109 }
110
111 pub fn task_count(&self) -> usize {
112 self.tasks.len()
113 }
114
115 pub fn now(&self) -> u64 {
116 self.now
117 }
118
119 pub fn fired_count(&self) -> u64 {
120 self.fired_count
121 }
122
123 pub fn next_due(&self) -> Option<u64> {
124 self.tasks
125 .iter()
126 .filter(|t| t.enabled)
127 .map(|t| t.due_at)
128 .min()
129 }
130
131 pub fn is_empty(&self) -> bool {
132 self.tasks.is_empty()
133 }
134
135 pub fn clear(&mut self) {
136 self.tasks.clear();
137 }
138
139 pub fn has_task(&self, id: u64) -> bool {
140 self.tasks.iter().any(|t| t.id == id)
141 }
142}
143
144impl Default for ScheduleQueue {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150pub fn new_schedule_queue() -> ScheduleQueue {
151 ScheduleQueue::new()
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn schedule_and_fire() {
160 let mut q = new_schedule_queue();
161 q.schedule_once("task1", 10);
162 let fired = q.advance(10);
163 assert_eq!(fired.len(), 1);
164 assert_eq!(fired[0].name, "task1");
165 }
166
167 #[test]
168 fn not_due_yet() {
169 let mut q = new_schedule_queue();
170 q.schedule_once("t", 100);
171 let fired = q.advance(50);
172 assert!(fired.is_empty());
173 }
174
175 #[test]
176 fn repeating_reschedules() {
177 let mut q = new_schedule_queue();
178 q.schedule_repeating("tick", 5, 5);
179 q.advance(5);
180 assert_eq!(q.task_count(), 1);
181 q.advance(5);
182 assert_eq!(q.fired_count(), 2);
183 }
184
185 #[test]
186 fn cancel_task() {
187 let mut q = new_schedule_queue();
188 let id = q.schedule_once("x", 10);
189 assert!(q.cancel(id));
190 assert!(!q.has_task(id));
191 }
192
193 #[test]
194 fn disabled_not_fired() {
195 let mut q = new_schedule_queue();
196 let id = q.schedule_once("t", 5);
197 q.set_enabled(id, false);
198 let fired = q.advance(10);
199 assert!(fired.is_empty());
200 }
201
202 #[test]
203 fn next_due_time() {
204 let mut q = new_schedule_queue();
205 q.schedule_once("a", 50);
206 q.schedule_once("b", 20);
207 assert_eq!(q.next_due(), Some(20));
208 }
209
210 #[test]
211 fn fired_count_tracked() {
212 let mut q = new_schedule_queue();
213 q.schedule_once("a", 1);
214 q.schedule_once("b", 2);
215 q.advance(5);
216 assert_eq!(q.fired_count(), 2);
217 }
218
219 #[test]
220 fn clear_queue() {
221 let mut q = new_schedule_queue();
222 q.schedule_once("a", 10);
223 q.clear();
224 assert!(q.is_empty());
225 }
226
227 #[test]
228 fn multiple_fire_same_tick() {
229 let mut q = new_schedule_queue();
230 q.schedule_once("a", 5);
231 q.schedule_once("b", 5);
232 let fired = q.advance(5);
233 assert_eq!(fired.len(), 2);
234 }
235}