fno_agents/
write_queue.rs1use std::collections::VecDeque;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum WriteMsg {
18 Bytes(Vec<u8>),
21 Resize { rows: u16, cols: u16 },
23}
24
25impl WriteMsg {
26 pub fn byte_len(&self) -> usize {
28 match self {
29 WriteMsg::Bytes(b) => b.len(),
30 WriteMsg::Resize { .. } => 0,
31 }
32 }
33}
34
35#[derive(Debug, thiserror::Error, PartialEq, Eq)]
36pub enum WriteQueueError {
37 #[error("write queue at capacity ({capacity} messages); apply backpressure")]
38 Full { capacity: usize },
39}
40
41#[derive(Debug)]
43pub struct WriteQueue {
44 inner: VecDeque<WriteMsg>,
45 capacity: usize,
46}
47
48impl WriteQueue {
49 pub fn new(capacity: usize) -> Self {
52 WriteQueue {
53 inner: VecDeque::new(),
54 capacity: capacity.max(1),
55 }
56 }
57
58 pub fn capacity(&self) -> usize {
59 self.capacity
60 }
61
62 pub fn len(&self) -> usize {
63 self.inner.len()
64 }
65
66 pub fn is_empty(&self) -> bool {
67 self.inner.is_empty()
68 }
69
70 pub fn is_full(&self) -> bool {
71 self.inner.len() >= self.capacity
72 }
73
74 pub fn enqueue(&mut self, msg: WriteMsg) -> Result<(), (WriteMsg, WriteQueueError)> {
78 if self.is_full() {
79 return Err((
80 msg,
81 WriteQueueError::Full {
82 capacity: self.capacity,
83 },
84 ));
85 }
86 self.inner.push_back(msg);
87 Ok(())
88 }
89
90 pub fn dequeue(&mut self) -> Option<WriteMsg> {
92 self.inner.pop_front()
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn fifo_order_preserved() {
102 let mut q = WriteQueue::new(4);
103 q.enqueue(WriteMsg::Bytes(b"a".to_vec())).unwrap();
104 q.enqueue(WriteMsg::Resize { rows: 24, cols: 80 }).unwrap();
105 q.enqueue(WriteMsg::Bytes(b"b".to_vec())).unwrap();
106 assert_eq!(q.dequeue(), Some(WriteMsg::Bytes(b"a".to_vec())));
107 assert_eq!(q.dequeue(), Some(WriteMsg::Resize { rows: 24, cols: 80 }));
108 assert_eq!(q.dequeue(), Some(WriteMsg::Bytes(b"b".to_vec())));
109 assert_eq!(q.dequeue(), None);
110 }
111
112 #[test]
113 fn full_returns_backpressure_and_does_not_drop_message() {
114 let mut q = WriteQueue::new(2);
115 q.enqueue(WriteMsg::Bytes(b"1".to_vec())).unwrap();
116 q.enqueue(WriteMsg::Bytes(b"2".to_vec())).unwrap();
117 let rejected = WriteMsg::Bytes(b"3".to_vec());
118 let err = q.enqueue(rejected.clone()).unwrap_err();
119 assert_eq!(
120 err.0, rejected,
121 "rejected message must be returned for retry"
122 );
123 assert_eq!(err.1, WriteQueueError::Full { capacity: 2 });
124 assert_eq!(q.len(), 2, "full queue must not have grown past capacity");
125
126 assert!(q.dequeue().is_some());
128 assert!(q.enqueue(rejected).is_ok());
129 }
130
131 #[test]
132 fn zero_capacity_clamps_to_one() {
133 let mut q = WriteQueue::new(0);
134 assert_eq!(q.capacity(), 1);
135 assert!(q.enqueue(WriteMsg::Bytes(b"x".to_vec())).is_ok());
136 assert!(q.is_full());
137 }
138}