use crate::probers::ProbeResult;
use std::collections::VecDeque;
pub struct RingBuffer {
capacity: usize,
inner: VecDeque<ProbeResult>,
}
impl RingBuffer {
pub fn new(capacity: usize) -> Self {
assert!(capacity > 0, "RingBuffer capacity must be > 0");
Self {
capacity,
inner: VecDeque::with_capacity(capacity),
}
}
pub fn push(&mut self, result: ProbeResult) {
if self.inner.len() == self.capacity {
self.inner.pop_front();
}
self.inner.push_back(result);
}
pub fn snapshot(&self) -> Vec<ProbeResult> {
self.inner.iter().cloned().collect()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn capacity(&self) -> usize {
self.capacity
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Utc;
fn make_result(rtt_us: Option<u64>, seq: u64) -> ProbeResult {
ProbeResult {
target: "test".to_string(),
rtt_us,
timestamp: Utc::now(),
seq,
responder_ip: None,
}
}
#[test]
fn test_capacity_eviction() {
let mut rb = RingBuffer::new(3);
for i in 0..5u64 {
rb.push(make_result(Some(i * 1000), i));
}
assert_eq!(rb.len(), 3);
let snap = rb.snapshot();
assert_eq!(snap[0].seq, 2);
assert_eq!(snap[1].seq, 3);
assert_eq!(snap[2].seq, 4);
}
#[test]
fn test_empty() {
let rb = RingBuffer::new(10);
assert!(rb.is_empty());
assert_eq!(rb.len(), 0);
}
#[test]
fn test_snapshot_is_clone() {
let mut rb = RingBuffer::new(5);
rb.push(make_result(Some(1000), 0));
let snap = rb.snapshot();
rb.push(make_result(Some(2000), 1));
assert_eq!(snap.len(), 1);
}
}