use std::collections::VecDeque;
use crate::SizeGauge;
#[derive(Debug)]
pub struct Ring<T> {
buf: VecDeque<T>,
capacity: usize,
gauge: SizeGauge,
}
impl<T: Clone> Clone for Ring<T> {
fn clone(&self) -> Self {
let gauge = SizeGauge::new();
gauge.set(self.buf.len());
Self {
buf: self.buf.clone(),
capacity: self.capacity,
gauge,
}
}
}
impl<T> Ring<T> {
pub fn with_capacity(capacity: usize, gauge: SizeGauge) -> Self {
Self {
buf: VecDeque::new(),
capacity,
gauge,
}
}
pub fn push(&mut self, item: T) -> Option<T> {
if self.capacity == 0 {
return Some(item);
}
let evicted = if self.buf.len() >= self.capacity {
self.buf.pop_front()
} else {
None
};
self.buf.push_back(item);
self.gauge.set(self.buf.len());
evicted
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.buf.iter()
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
pub fn capacity(&self) -> usize {
self.capacity
}
}
#[cfg(test)]
mod tests {
use crate::{Ring, SizeGauge};
#[test]
fn push_never_exceeds_capacity_and_returns_evicted() {
let gauge = SizeGauge::new();
let mut ring: Ring<u32> = Ring::with_capacity(3, gauge.clone());
assert_eq!(ring.push(1), None);
assert_eq!(ring.push(2), None);
assert_eq!(ring.push(3), None);
assert_eq!(ring.len(), 3);
assert_eq!(gauge.get(), 3);
assert_eq!(ring.push(4), Some(1));
assert_eq!(ring.len(), 3);
assert_eq!(gauge.get(), 3);
let items: Vec<u32> = ring.iter().copied().collect();
assert_eq!(items, vec![2, 3, 4]);
}
#[test]
fn clone_has_independent_gauge_so_snapshot_pushes_do_not_corrupt_owner() {
let owner_gauge = SizeGauge::new();
let mut ring: Ring<u32> = Ring::with_capacity(4, owner_gauge.clone());
ring.push(1);
ring.push(2);
assert_eq!(owner_gauge.get(), 2);
let mut snapshot = ring.clone();
assert_eq!(snapshot.len(), 2);
snapshot.push(3);
snapshot.push(4);
assert_eq!(snapshot.len(), 4);
assert_eq!(
owner_gauge.get(),
2,
"owner gauge corrupted by a push to a snapshot clone"
);
}
#[test]
fn zero_capacity_stores_nothing_and_hands_item_back() {
let gauge = SizeGauge::new();
let mut ring: Ring<u32> = Ring::with_capacity(0, gauge.clone());
assert_eq!(ring.push(7), Some(7));
assert_eq!(ring.len(), 0);
assert!(ring.is_empty());
assert_eq!(gauge.get(), 0);
}
}