Skip to main content

stateset_sync/
buffer.rs

1use std::collections::VecDeque;
2
3use crate::event::SyncEvent;
4
5/// A bounded, FIFO event buffer.
6///
7/// When the buffer is at capacity and a new event is pushed, the oldest
8/// event is evicted and returned. This mirrors the JS `_eventBuffer`
9/// behavior in `SyncEngine` where old events are shifted when the
10/// buffer exceeds `_eventBufferSize`.
11///
12/// # Examples
13///
14/// ```
15/// use stateset_sync::{EventBuffer, SyncEvent};
16/// use serde_json::json;
17///
18/// let mut buffer = EventBuffer::new(2);
19/// buffer.push(SyncEvent::new("a", "x", "1", json!({})));
20/// buffer.push(SyncEvent::new("b", "x", "2", json!({})));
21///
22/// // Third push evicts the oldest event
23/// let evicted = buffer.push(SyncEvent::new("c", "x", "3", json!({})));
24/// assert!(evicted.is_some());
25/// assert_eq!(evicted.unwrap().event_type, "a");
26/// assert_eq!(buffer.len(), 2);
27/// ```
28#[derive(Debug)]
29pub struct EventBuffer {
30    buffer: VecDeque<SyncEvent>,
31    capacity: usize,
32}
33
34impl EventBuffer {
35    /// Create a new `EventBuffer` with the given capacity.
36    ///
37    /// A capacity of 0 means the buffer will evict every event immediately.
38    #[must_use]
39    pub fn new(capacity: usize) -> Self {
40        Self { buffer: VecDeque::with_capacity(capacity), capacity }
41    }
42
43    /// Push an event into the buffer.
44    ///
45    /// If the buffer is at capacity, the oldest event is evicted and returned.
46    /// Returns `None` if no eviction was needed.
47    pub fn push(&mut self, event: SyncEvent) -> Option<SyncEvent> {
48        let evicted =
49            if self.buffer.len() >= self.capacity { self.buffer.pop_front() } else { None };
50        // For zero-capacity buffers, don't actually store the event
51        if self.capacity > 0 {
52            self.buffer.push_back(event);
53        }
54        evicted
55    }
56
57    /// Drain all events from the buffer, returning them in FIFO order.
58    pub fn drain_all(&mut self) -> Vec<SyncEvent> {
59        self.buffer.drain(..).collect()
60    }
61
62    /// Return the number of events in the buffer.
63    #[must_use]
64    pub fn len(&self) -> usize {
65        self.buffer.len()
66    }
67
68    /// Whether the buffer contains no events.
69    #[must_use]
70    pub fn is_empty(&self) -> bool {
71        self.buffer.is_empty()
72    }
73
74    /// Whether the buffer is at capacity.
75    #[must_use]
76    pub fn is_full(&self) -> bool {
77        self.buffer.len() >= self.capacity
78    }
79
80    /// Return the maximum capacity.
81    #[must_use]
82    pub const fn capacity(&self) -> usize {
83        self.capacity
84    }
85
86    /// Peek at the most recent `count` events (from the back of the buffer).
87    #[must_use]
88    pub fn recent(&self, count: usize) -> Vec<&SyncEvent> {
89        let start = self.buffer.len().saturating_sub(count);
90        self.buffer.iter().skip(start).collect()
91    }
92
93    /// Clear the buffer.
94    pub fn clear(&mut self) {
95        self.buffer.clear();
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use serde_json::json;
103
104    fn make_event(name: &str) -> SyncEvent {
105        SyncEvent::new(name, "entity", "id", json!({}))
106    }
107
108    #[test]
109    fn push_within_capacity() {
110        let mut buffer = EventBuffer::new(5);
111        let evicted = buffer.push(make_event("a"));
112        assert!(evicted.is_none());
113        assert_eq!(buffer.len(), 1);
114    }
115
116    #[test]
117    fn push_at_capacity_evicts() {
118        let mut buffer = EventBuffer::new(2);
119        buffer.push(make_event("a"));
120        buffer.push(make_event("b"));
121        assert!(buffer.is_full());
122
123        let evicted = buffer.push(make_event("c"));
124        assert!(evicted.is_some());
125        assert_eq!(evicted.unwrap().event_type, "a");
126        assert_eq!(buffer.len(), 2);
127    }
128
129    #[test]
130    fn eviction_preserves_fifo_order() {
131        let mut buffer = EventBuffer::new(3);
132        buffer.push(make_event("a"));
133        buffer.push(make_event("b"));
134        buffer.push(make_event("c"));
135
136        // Push d evicts a
137        let evicted = buffer.push(make_event("d"));
138        assert_eq!(evicted.unwrap().event_type, "a");
139
140        let drained = buffer.drain_all();
141        assert_eq!(drained[0].event_type, "b");
142        assert_eq!(drained[1].event_type, "c");
143        assert_eq!(drained[2].event_type, "d");
144    }
145
146    #[test]
147    fn drain_all_empties_buffer() {
148        let mut buffer = EventBuffer::new(10);
149        buffer.push(make_event("a"));
150        buffer.push(make_event("b"));
151
152        let drained = buffer.drain_all();
153        assert_eq!(drained.len(), 2);
154        assert!(buffer.is_empty());
155    }
156
157    #[test]
158    fn drain_all_empty_buffer() {
159        let mut buffer = EventBuffer::new(10);
160        let drained = buffer.drain_all();
161        assert!(drained.is_empty());
162    }
163
164    #[test]
165    fn is_empty_and_is_full() {
166        let mut buffer = EventBuffer::new(2);
167        assert!(buffer.is_empty());
168        assert!(!buffer.is_full());
169
170        buffer.push(make_event("a"));
171        assert!(!buffer.is_empty());
172        assert!(!buffer.is_full());
173
174        buffer.push(make_event("b"));
175        assert!(!buffer.is_empty());
176        assert!(buffer.is_full());
177    }
178
179    #[test]
180    fn zero_capacity_buffer() {
181        let mut buffer = EventBuffer::new(0);
182        assert!(buffer.is_full());
183
184        // Push to zero-capacity buffer: nothing stored
185        let evicted = buffer.push(make_event("a"));
186        assert!(evicted.is_none()); // No event to evict from empty buffer
187        assert!(buffer.is_empty());
188        assert_eq!(buffer.len(), 0);
189    }
190
191    #[test]
192    fn capacity_accessor() {
193        let buffer = EventBuffer::new(42);
194        assert_eq!(buffer.capacity(), 42);
195    }
196
197    #[test]
198    fn recent_events() {
199        let mut buffer = EventBuffer::new(10);
200        buffer.push(make_event("a"));
201        buffer.push(make_event("b"));
202        buffer.push(make_event("c"));
203
204        let recent = buffer.recent(2);
205        assert_eq!(recent.len(), 2);
206        assert_eq!(recent[0].event_type, "b");
207        assert_eq!(recent[1].event_type, "c");
208    }
209
210    #[test]
211    fn recent_more_than_available() {
212        let mut buffer = EventBuffer::new(10);
213        buffer.push(make_event("a"));
214
215        let recent = buffer.recent(5);
216        assert_eq!(recent.len(), 1);
217    }
218
219    #[test]
220    fn clear_buffer() {
221        let mut buffer = EventBuffer::new(10);
222        buffer.push(make_event("a"));
223        buffer.push(make_event("b"));
224        buffer.clear();
225        assert!(buffer.is_empty());
226        assert_eq!(buffer.len(), 0);
227    }
228
229    #[test]
230    fn debug_impl() {
231        let buffer = EventBuffer::new(10);
232        let debug = format!("{buffer:?}");
233        assert!(debug.contains("EventBuffer"));
234    }
235
236    #[test]
237    fn multiple_evictions() {
238        let mut buffer = EventBuffer::new(2);
239        buffer.push(make_event("a"));
240        buffer.push(make_event("b"));
241
242        let e1 = buffer.push(make_event("c"));
243        assert_eq!(e1.unwrap().event_type, "a");
244
245        let e2 = buffer.push(make_event("d"));
246        assert_eq!(e2.unwrap().event_type, "b");
247
248        let remaining = buffer.drain_all();
249        assert_eq!(remaining.len(), 2);
250        assert_eq!(remaining[0].event_type, "c");
251        assert_eq!(remaining[1].event_type, "d");
252    }
253}