Skip to main content

vtcode_commons/interjection/
events.rs

1#![expect(
2    unused_results,
3    reason = "Queue maintenance intentionally discards fluent collection mutation results."
4)]
5
6use std::sync::{Arc, Mutex, MutexGuard};
7
8#[derive(Debug)]
9pub struct EventQueue<E> {
10    events: Arc<Mutex<Vec<E>>>,
11}
12
13impl<E> Clone for EventQueue<E> {
14    fn clone(&self) -> Self {
15        Self { events: Arc::clone(&self.events) }
16    }
17}
18
19impl<E> Default for EventQueue<E> {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl<E> EventQueue<E> {
26    pub(crate) fn new() -> Self {
27        Self { events: Arc::new(Mutex::new(Vec::new())) }
28    }
29
30    pub(crate) fn push(&self, event: E) {
31        self.lock().push(event);
32    }
33
34    fn push_capped(&self, event: E, max: usize) {
35        let mut q = self.lock();
36        q.push(event);
37        if q.len() > max {
38            let excess = q.len() - max;
39            q.drain(..excess);
40        }
41    }
42
43    fn len(&self) -> usize {
44        self.lock().len()
45    }
46
47    pub(crate) fn is_empty(&self) -> bool {
48        self.lock().is_empty()
49    }
50
51    fn drain_matching(&self, take: impl Fn(&E) -> bool) -> Vec<E> {
52        let mut q = self.lock();
53        let (matched, kept): (Vec<E>, Vec<E>) = std::mem::take(&mut *q).into_iter().partition(|e| take(e));
54        *q = kept;
55        matched
56    }
57
58    pub(crate) fn drain_all(&self) -> Vec<E> {
59        std::mem::take(&mut *self.lock())
60    }
61
62    fn clear(&self) {
63        self.lock().clear();
64    }
65
66    fn lock(&self) -> MutexGuard<'_, Vec<E>> {
67        self.events.lock().unwrap_or_else(|e| e.into_inner())
68    }
69}
70
71impl<E: Clone> EventQueue<E> {
72    fn snapshot(&self) -> Vec<E> {
73        self.lock().clone()
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn push_and_len() {
83        let q: EventQueue<u32> = EventQueue::new();
84        assert!(q.is_empty());
85        q.push(1);
86        q.push(2);
87        assert_eq!(q.len(), 2);
88    }
89
90    #[test]
91    fn clones_share_one_queue() {
92        let q: EventQueue<u32> = EventQueue::new();
93        let q2 = q.clone();
94        q.push(7);
95        assert_eq!(q2.len(), 1);
96    }
97
98    #[test]
99    fn push_capped_drops_oldest() {
100        let q: EventQueue<u32> = EventQueue::new();
101        for i in 0..5 {
102            q.push_capped(i, 3);
103        }
104        assert_eq!(q.drain_matching(|_| true), vec![2, 3, 4]);
105        assert!(q.is_empty());
106    }
107
108    #[test]
109    fn drain_matching_returns_matched_retains_rest_fifo() {
110        let q: EventQueue<u32> = EventQueue::new();
111        for i in 0..6 {
112            q.push(i);
113        }
114        let evens = q.drain_matching(|n| n % 2 == 0);
115        assert_eq!(evens, vec![0, 2, 4]);
116        assert_eq!(q.drain_matching(|_| true), vec![1, 3, 5]);
117    }
118
119    #[test]
120    fn push_capped_under_limit_keeps_all() {
121        let q: EventQueue<u32> = EventQueue::new();
122        q.push_capped(1, 5);
123        q.push_capped(2, 5);
124        assert_eq!(q.drain_matching(|_| true), vec![1, 2]);
125    }
126
127    #[test]
128    fn drain_matching_none_match_retains_all() {
129        let q: EventQueue<u32> = EventQueue::new();
130        q.push(1);
131        q.push(2);
132        assert!(q.drain_matching(|n| *n > 10).is_empty());
133        assert_eq!(q.len(), 2);
134    }
135
136    #[test]
137    fn drain_matching_on_empty_is_empty() {
138        let q: EventQueue<u32> = EventQueue::new();
139        assert!(q.drain_matching(|_| true).is_empty());
140    }
141
142    #[test]
143    fn drain_all_empties_in_fifo_order() {
144        let q: EventQueue<u32> = EventQueue::new();
145        q.push(1);
146        q.push(2);
147        assert_eq!(q.drain_all(), vec![1, 2]);
148        assert!(q.is_empty());
149    }
150
151    #[test]
152    fn clear_discards_all() {
153        let q: EventQueue<u32> = EventQueue::new();
154        q.push(1);
155        q.clear();
156        assert!(q.is_empty());
157    }
158
159    #[test]
160    fn snapshot_reads_without_draining() {
161        let q: EventQueue<u32> = EventQueue::new();
162        q.push(9);
163        assert_eq!(q.snapshot(), vec![9]);
164        assert_eq!(q.len(), 1);
165    }
166}