Skip to main content

ai_agent/utils/
circular_buffer.rs

1//! A fixed-size circular buffer that automatically evicts the oldest items
2//! when the buffer is full. Useful for maintaining a rolling window of data.
3
4use std::collections::VecDeque;
5
6/// A fixed-size circular buffer that automatically evicts the oldest items
7/// when the buffer is full. Useful for maintaining a rolling window of data.
8pub struct CircularBuffer<T> {
9    buffer: VecDeque<T>,
10    capacity: usize,
11}
12
13impl<T> CircularBuffer<T> {
14    /// Create a new circular buffer with the given capacity.
15    pub fn new(capacity: usize) -> Self {
16        Self {
17            buffer: VecDeque::with_capacity(capacity),
18            capacity,
19        }
20    }
21
22    /// Add an item to the buffer. If the buffer is full,
23    /// the oldest item will be evicted.
24    pub fn add(&mut self, item: T) {
25        if self.buffer.len() >= self.capacity {
26            self.buffer.pop_front();
27        }
28        self.buffer.push_back(item);
29    }
30
31    /// Add multiple items to the buffer at once.
32    pub fn add_all(&mut self, items: impl IntoIterator<Item = T>) {
33        for item in items {
34            self.add(item);
35        }
36    }
37
38    /// Get the most recent N items from the buffer.
39    /// Returns fewer items if the buffer contains less than N items.
40    pub fn get_recent(&self, count: usize) -> Vec<&T> {
41        self.buffer.iter().rev().take(count).collect::<Vec<_>>()
42    }
43
44    /// Get all items currently in the buffer, in order from oldest to newest.
45    pub fn to_vec(&self) -> Vec<&T> {
46        self.buffer.iter().collect()
47    }
48
49    /// Clear all items from the buffer.
50    pub fn clear(&mut self) {
51        self.buffer.clear();
52    }
53
54    /// Get the current number of items in the buffer.
55    pub fn len(&self) -> usize {
56        self.buffer.len()
57    }
58
59    /// Check if the buffer is empty.
60    pub fn is_empty(&self) -> bool {
61        self.buffer.is_empty()
62    }
63}
64
65impl<T: Clone> Clone for CircularBuffer<T> {
66    fn clone(&self) -> Self {
67        Self {
68            buffer: self.buffer.clone(),
69            capacity: self.capacity,
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_basic_operations() {
80        let mut buffer = CircularBuffer::new(3);
81        assert_eq!(buffer.len(), 0);
82
83        buffer.add(1);
84        buffer.add(2);
85        buffer.add(3);
86        assert_eq!(buffer.len(), 3);
87
88        // Adding another item should evict the oldest
89        buffer.add(4);
90        assert_eq!(buffer.len(), 3);
91        assert_eq!(buffer.to_vec(), vec![&2, &3, &4]);
92    }
93
94    #[test]
95    fn test_get_recent() {
96        let mut buffer = CircularBuffer::new(5);
97        for i in 1..=10 {
98            buffer.add(i);
99        }
100
101        let recent = buffer.get_recent(3);
102        assert_eq!(recent, vec![&10, &9, &8]);
103    }
104
105    #[test]
106    fn test_clear() {
107        let mut buffer = CircularBuffer::new(3);
108        buffer.add(1);
109        buffer.add(2);
110        buffer.clear();
111        assert_eq!(buffer.len(), 0);
112    }
113}