#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CommandPriority {
Critical,
High,
Normal,
Low,
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct QueuedCommand {
pub id: u64,
pub label: String,
pub priority: CommandPriority,
pub sequence: u64,
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct CommandQueue {
commands: Vec<QueuedCommand>,
next_id: u64,
next_seq: u64,
total_enqueued: u64,
max_depth: usize,
}
#[allow(dead_code)]
fn priority_rank(p: &CommandPriority) -> u8 {
match p {
CommandPriority::Critical => 0,
CommandPriority::High => 1,
CommandPriority::Normal => 2,
CommandPriority::Low => 3,
}
}
#[allow(dead_code)]
fn sort_commands(cmds: &mut [QueuedCommand]) {
cmds.sort_by(|a, b| {
let pa = priority_rank(&a.priority);
let pb = priority_rank(&b.priority);
pa.cmp(&pb).then(a.sequence.cmp(&b.sequence))
});
}
#[allow(dead_code)]
pub fn new_command_queue() -> CommandQueue {
CommandQueue {
commands: Vec::new(),
next_id: 1,
next_seq: 0,
total_enqueued: 0,
max_depth: 0,
}
}
#[allow(dead_code)]
pub fn enqueue(queue: &mut CommandQueue, label: &str, priority: CommandPriority) -> u64 {
let id = queue.next_id;
queue.next_id += 1;
let seq = queue.next_seq;
queue.next_seq += 1;
queue.total_enqueued += 1;
queue.commands.push(QueuedCommand {
id,
label: label.to_string(),
priority,
sequence: seq,
});
sort_commands(&mut queue.commands);
if queue.commands.len() > queue.max_depth {
queue.max_depth = queue.commands.len();
}
id
}
#[allow(dead_code)]
pub fn dequeue(queue: &mut CommandQueue) -> Option<QueuedCommand> {
if queue.commands.is_empty() {
None
} else {
Some(queue.commands.remove(0))
}
}
#[allow(dead_code)]
pub fn peek_next(queue: &CommandQueue) -> Option<&QueuedCommand> {
queue.commands.first()
}
#[allow(dead_code)]
pub fn command_count(queue: &CommandQueue) -> usize {
queue.commands.len()
}
#[allow(dead_code)]
pub fn is_queue_empty(queue: &CommandQueue) -> bool {
queue.commands.is_empty()
}
#[allow(dead_code)]
pub fn has_priority(queue: &CommandQueue, priority: &CommandPriority) -> bool {
queue.commands.iter().any(|c| &c.priority == priority)
}
#[allow(dead_code)]
pub fn total_enqueued(queue: &CommandQueue) -> u64 {
queue.total_enqueued
}
#[allow(dead_code)]
pub fn max_queue_depth(queue: &CommandQueue) -> usize {
queue.max_depth
}
#[allow(dead_code)]
pub fn clear_queue(queue: &mut CommandQueue) {
queue.commands.clear();
}
#[allow(dead_code)]
pub fn drain_all(queue: &mut CommandQueue) -> Vec<QueuedCommand> {
let mut out = std::mem::take(&mut queue.commands);
sort_commands(&mut out);
out
}
#[allow(dead_code)]
pub fn enqueue_batch(queue: &mut CommandQueue, items: &[(&str, CommandPriority)]) -> Vec<u64> {
let mut ids = Vec::with_capacity(items.len());
for (label, pri) in items {
ids.push(enqueue(queue, label, pri.clone()));
}
ids
}
#[allow(dead_code)]
pub fn commands_by_priority<'a>(
queue: &'a CommandQueue,
priority: &CommandPriority,
) -> Vec<&'a QueuedCommand> {
queue
.commands
.iter()
.filter(|c| &c.priority == priority)
.collect()
}
#[allow(dead_code)]
pub fn command_queue_to_json(queue: &CommandQueue) -> String {
let mut s = String::from("{\"commands\":[");
for (i, c) in queue.commands.iter().enumerate() {
if i > 0 {
s.push(',');
}
s.push_str(&format!(
"{{\"id\":{},\"label\":\"{}\",\"priority\":\"{:?}\"}}",
c.id, c.label, c.priority
));
}
s.push_str(&format!(
"],\"total_enqueued\":{},\"max_depth\":{}}}",
queue.total_enqueued, queue.max_depth
));
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_command_queue() {
let q = new_command_queue();
assert!(is_queue_empty(&q));
assert_eq!(command_count(&q), 0);
}
#[test]
fn test_enqueue_single() {
let mut q = new_command_queue();
let id = enqueue(&mut q, "cmd1", CommandPriority::Normal);
assert!(id > 0);
assert_eq!(command_count(&q), 1);
}
#[test]
fn test_dequeue_fifo() {
let mut q = new_command_queue();
enqueue(&mut q, "first", CommandPriority::Normal);
enqueue(&mut q, "second", CommandPriority::Normal);
let c = dequeue(&mut q).expect("should succeed");
assert_eq!(c.label, "first");
}
#[test]
fn test_dequeue_empty() {
let mut q = new_command_queue();
assert!(dequeue(&mut q).is_none());
}
#[test]
fn test_priority_ordering() {
let mut q = new_command_queue();
enqueue(&mut q, "low", CommandPriority::Low);
enqueue(&mut q, "critical", CommandPriority::Critical);
enqueue(&mut q, "normal", CommandPriority::Normal);
let c = dequeue(&mut q).expect("should succeed");
assert_eq!(c.label, "critical");
}
#[test]
fn test_peek_next() {
let mut q = new_command_queue();
assert!(peek_next(&q).is_none());
enqueue(&mut q, "peek_me", CommandPriority::High);
let p = peek_next(&q).expect("should succeed");
assert_eq!(p.label, "peek_me");
assert_eq!(command_count(&q), 1); }
#[test]
fn test_clear_queue() {
let mut q = new_command_queue();
enqueue(&mut q, "a", CommandPriority::Normal);
enqueue(&mut q, "b", CommandPriority::High);
clear_queue(&mut q);
assert!(is_queue_empty(&q));
}
#[test]
fn test_drain_all() {
let mut q = new_command_queue();
enqueue(&mut q, "a", CommandPriority::Low);
enqueue(&mut q, "b", CommandPriority::Critical);
let drained = drain_all(&mut q);
assert_eq!(drained.len(), 2);
assert_eq!(drained[0].label, "b"); assert!(is_queue_empty(&q));
}
#[test]
fn test_enqueue_batch() {
let mut q = new_command_queue();
let ids = enqueue_batch(
&mut q,
&[
("x", CommandPriority::Normal),
("y", CommandPriority::High),
("z", CommandPriority::Low),
],
);
assert_eq!(ids.len(), 3);
assert_eq!(command_count(&q), 3);
}
#[test]
fn test_commands_by_priority() {
let mut q = new_command_queue();
enqueue(&mut q, "a", CommandPriority::Normal);
enqueue(&mut q, "b", CommandPriority::High);
enqueue(&mut q, "c", CommandPriority::Normal);
let normals = commands_by_priority(&q, &CommandPriority::Normal);
assert_eq!(normals.len(), 2);
}
#[test]
fn test_total_enqueued() {
let mut q = new_command_queue();
enqueue(&mut q, "a", CommandPriority::Normal);
enqueue(&mut q, "b", CommandPriority::Normal);
dequeue(&mut q);
assert_eq!(total_enqueued(&q), 2);
}
#[test]
fn test_is_queue_empty() {
let mut q = new_command_queue();
assert!(is_queue_empty(&q));
enqueue(&mut q, "x", CommandPriority::Low);
assert!(!is_queue_empty(&q));
}
#[test]
fn test_has_priority() {
let mut q = new_command_queue();
enqueue(&mut q, "a", CommandPriority::High);
assert!(has_priority(&q, &CommandPriority::High));
assert!(!has_priority(&q, &CommandPriority::Low));
}
#[test]
fn test_max_queue_depth() {
let mut q = new_command_queue();
enqueue(&mut q, "a", CommandPriority::Normal);
enqueue(&mut q, "b", CommandPriority::Normal);
enqueue(&mut q, "c", CommandPriority::Normal);
dequeue(&mut q);
assert_eq!(max_queue_depth(&q), 3);
}
#[test]
fn test_command_queue_to_json() {
let mut q = new_command_queue();
enqueue(&mut q, "test", CommandPriority::Normal);
let json = command_queue_to_json(&q);
assert!(json.contains("\"commands\""));
assert!(json.contains("\"test\""));
assert!(json.contains("\"total_enqueued\":1"));
}
#[test]
fn test_priority_stable_fifo() {
let mut q = new_command_queue();
enqueue(&mut q, "high1", CommandPriority::High);
enqueue(&mut q, "high2", CommandPriority::High);
enqueue(&mut q, "high3", CommandPriority::High);
let c1 = dequeue(&mut q).expect("should succeed");
let c2 = dequeue(&mut q).expect("should succeed");
assert_eq!(c1.label, "high1");
assert_eq!(c2.label, "high2");
}
}