1#[allow(dead_code)]
5#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub enum CommandPriority {
7 Critical,
9 High,
11 Normal,
13 Low,
15}
16
17#[allow(dead_code)]
19#[derive(Clone, Debug)]
20pub struct QueuedCommand {
21 pub id: u64,
23 pub label: String,
25 pub priority: CommandPriority,
27 pub sequence: u64,
29}
30
31#[allow(dead_code)]
33#[derive(Clone, Debug)]
34pub struct CommandQueue {
35 commands: Vec<QueuedCommand>,
37 next_id: u64,
39 next_seq: u64,
41 total_enqueued: u64,
43 max_depth: usize,
45}
46
47#[allow(dead_code)]
53fn priority_rank(p: &CommandPriority) -> u8 {
54 match p {
55 CommandPriority::Critical => 0,
56 CommandPriority::High => 1,
57 CommandPriority::Normal => 2,
58 CommandPriority::Low => 3,
59 }
60}
61
62#[allow(dead_code)]
64fn sort_commands(cmds: &mut [QueuedCommand]) {
65 cmds.sort_by(|a, b| {
66 let pa = priority_rank(&a.priority);
67 let pb = priority_rank(&b.priority);
68 pa.cmp(&pb).then(a.sequence.cmp(&b.sequence))
69 });
70}
71
72#[allow(dead_code)]
78pub fn new_command_queue() -> CommandQueue {
79 CommandQueue {
80 commands: Vec::new(),
81 next_id: 1,
82 next_seq: 0,
83 total_enqueued: 0,
84 max_depth: 0,
85 }
86}
87
88#[allow(dead_code)]
95pub fn enqueue(queue: &mut CommandQueue, label: &str, priority: CommandPriority) -> u64 {
96 let id = queue.next_id;
97 queue.next_id += 1;
98 let seq = queue.next_seq;
99 queue.next_seq += 1;
100 queue.total_enqueued += 1;
101 queue.commands.push(QueuedCommand {
102 id,
103 label: label.to_string(),
104 priority,
105 sequence: seq,
106 });
107 sort_commands(&mut queue.commands);
108 if queue.commands.len() > queue.max_depth {
109 queue.max_depth = queue.commands.len();
110 }
111 id
112}
113
114#[allow(dead_code)]
117pub fn dequeue(queue: &mut CommandQueue) -> Option<QueuedCommand> {
118 if queue.commands.is_empty() {
119 None
120 } else {
121 Some(queue.commands.remove(0))
122 }
123}
124
125#[allow(dead_code)]
127pub fn peek_next(queue: &CommandQueue) -> Option<&QueuedCommand> {
128 queue.commands.first()
129}
130
131#[allow(dead_code)]
137pub fn command_count(queue: &CommandQueue) -> usize {
138 queue.commands.len()
139}
140
141#[allow(dead_code)]
143pub fn is_queue_empty(queue: &CommandQueue) -> bool {
144 queue.commands.is_empty()
145}
146
147#[allow(dead_code)]
149pub fn has_priority(queue: &CommandQueue, priority: &CommandPriority) -> bool {
150 queue.commands.iter().any(|c| &c.priority == priority)
151}
152
153#[allow(dead_code)]
155pub fn total_enqueued(queue: &CommandQueue) -> u64 {
156 queue.total_enqueued
157}
158
159#[allow(dead_code)]
161pub fn max_queue_depth(queue: &CommandQueue) -> usize {
162 queue.max_depth
163}
164
165#[allow(dead_code)]
171pub fn clear_queue(queue: &mut CommandQueue) {
172 queue.commands.clear();
173}
174
175#[allow(dead_code)]
177pub fn drain_all(queue: &mut CommandQueue) -> Vec<QueuedCommand> {
178 let mut out = std::mem::take(&mut queue.commands);
179 sort_commands(&mut out);
180 out
181}
182
183#[allow(dead_code)]
185pub fn enqueue_batch(queue: &mut CommandQueue, items: &[(&str, CommandPriority)]) -> Vec<u64> {
186 let mut ids = Vec::with_capacity(items.len());
187 for (label, pri) in items {
188 ids.push(enqueue(queue, label, pri.clone()));
189 }
190 ids
191}
192
193#[allow(dead_code)]
195pub fn commands_by_priority<'a>(
196 queue: &'a CommandQueue,
197 priority: &CommandPriority,
198) -> Vec<&'a QueuedCommand> {
199 queue
200 .commands
201 .iter()
202 .filter(|c| &c.priority == priority)
203 .collect()
204}
205
206#[allow(dead_code)]
212pub fn command_queue_to_json(queue: &CommandQueue) -> String {
213 let mut s = String::from("{\"commands\":[");
214 for (i, c) in queue.commands.iter().enumerate() {
215 if i > 0 {
216 s.push(',');
217 }
218 s.push_str(&format!(
219 "{{\"id\":{},\"label\":\"{}\",\"priority\":\"{:?}\"}}",
220 c.id, c.label, c.priority
221 ));
222 }
223 s.push_str(&format!(
224 "],\"total_enqueued\":{},\"max_depth\":{}}}",
225 queue.total_enqueued, queue.max_depth
226 ));
227 s
228}
229
230#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[test]
239 fn test_new_command_queue() {
240 let q = new_command_queue();
241 assert!(is_queue_empty(&q));
242 assert_eq!(command_count(&q), 0);
243 }
244
245 #[test]
246 fn test_enqueue_single() {
247 let mut q = new_command_queue();
248 let id = enqueue(&mut q, "cmd1", CommandPriority::Normal);
249 assert!(id > 0);
250 assert_eq!(command_count(&q), 1);
251 }
252
253 #[test]
254 fn test_dequeue_fifo() {
255 let mut q = new_command_queue();
256 enqueue(&mut q, "first", CommandPriority::Normal);
257 enqueue(&mut q, "second", CommandPriority::Normal);
258 let c = dequeue(&mut q).expect("should succeed");
259 assert_eq!(c.label, "first");
260 }
261
262 #[test]
263 fn test_dequeue_empty() {
264 let mut q = new_command_queue();
265 assert!(dequeue(&mut q).is_none());
266 }
267
268 #[test]
269 fn test_priority_ordering() {
270 let mut q = new_command_queue();
271 enqueue(&mut q, "low", CommandPriority::Low);
272 enqueue(&mut q, "critical", CommandPriority::Critical);
273 enqueue(&mut q, "normal", CommandPriority::Normal);
274 let c = dequeue(&mut q).expect("should succeed");
275 assert_eq!(c.label, "critical");
276 }
277
278 #[test]
279 fn test_peek_next() {
280 let mut q = new_command_queue();
281 assert!(peek_next(&q).is_none());
282 enqueue(&mut q, "peek_me", CommandPriority::High);
283 let p = peek_next(&q).expect("should succeed");
284 assert_eq!(p.label, "peek_me");
285 assert_eq!(command_count(&q), 1); }
287
288 #[test]
289 fn test_clear_queue() {
290 let mut q = new_command_queue();
291 enqueue(&mut q, "a", CommandPriority::Normal);
292 enqueue(&mut q, "b", CommandPriority::High);
293 clear_queue(&mut q);
294 assert!(is_queue_empty(&q));
295 }
296
297 #[test]
298 fn test_drain_all() {
299 let mut q = new_command_queue();
300 enqueue(&mut q, "a", CommandPriority::Low);
301 enqueue(&mut q, "b", CommandPriority::Critical);
302 let drained = drain_all(&mut q);
303 assert_eq!(drained.len(), 2);
304 assert_eq!(drained[0].label, "b"); assert!(is_queue_empty(&q));
306 }
307
308 #[test]
309 fn test_enqueue_batch() {
310 let mut q = new_command_queue();
311 let ids = enqueue_batch(
312 &mut q,
313 &[
314 ("x", CommandPriority::Normal),
315 ("y", CommandPriority::High),
316 ("z", CommandPriority::Low),
317 ],
318 );
319 assert_eq!(ids.len(), 3);
320 assert_eq!(command_count(&q), 3);
321 }
322
323 #[test]
324 fn test_commands_by_priority() {
325 let mut q = new_command_queue();
326 enqueue(&mut q, "a", CommandPriority::Normal);
327 enqueue(&mut q, "b", CommandPriority::High);
328 enqueue(&mut q, "c", CommandPriority::Normal);
329 let normals = commands_by_priority(&q, &CommandPriority::Normal);
330 assert_eq!(normals.len(), 2);
331 }
332
333 #[test]
334 fn test_total_enqueued() {
335 let mut q = new_command_queue();
336 enqueue(&mut q, "a", CommandPriority::Normal);
337 enqueue(&mut q, "b", CommandPriority::Normal);
338 dequeue(&mut q);
339 assert_eq!(total_enqueued(&q), 2);
340 }
341
342 #[test]
343 fn test_is_queue_empty() {
344 let mut q = new_command_queue();
345 assert!(is_queue_empty(&q));
346 enqueue(&mut q, "x", CommandPriority::Low);
347 assert!(!is_queue_empty(&q));
348 }
349
350 #[test]
351 fn test_has_priority() {
352 let mut q = new_command_queue();
353 enqueue(&mut q, "a", CommandPriority::High);
354 assert!(has_priority(&q, &CommandPriority::High));
355 assert!(!has_priority(&q, &CommandPriority::Low));
356 }
357
358 #[test]
359 fn test_max_queue_depth() {
360 let mut q = new_command_queue();
361 enqueue(&mut q, "a", CommandPriority::Normal);
362 enqueue(&mut q, "b", CommandPriority::Normal);
363 enqueue(&mut q, "c", CommandPriority::Normal);
364 dequeue(&mut q);
365 assert_eq!(max_queue_depth(&q), 3);
366 }
367
368 #[test]
369 fn test_command_queue_to_json() {
370 let mut q = new_command_queue();
371 enqueue(&mut q, "test", CommandPriority::Normal);
372 let json = command_queue_to_json(&q);
373 assert!(json.contains("\"commands\""));
374 assert!(json.contains("\"test\""));
375 assert!(json.contains("\"total_enqueued\":1"));
376 }
377
378 #[test]
379 fn test_priority_stable_fifo() {
380 let mut q = new_command_queue();
381 enqueue(&mut q, "high1", CommandPriority::High);
382 enqueue(&mut q, "high2", CommandPriority::High);
383 enqueue(&mut q, "high3", CommandPriority::High);
384 let c1 = dequeue(&mut q).expect("should succeed");
385 let c2 = dequeue(&mut q).expect("should succeed");
386 assert_eq!(c1.label, "high1");
387 assert_eq!(c2.label, "high2");
388 }
389}