mecha10-cli 0.8.0

Mecha10 CLI tool
Documentation
//! Thread-safe, size-capped item buffer shared by the CLI's TUI screens.
//!
//! Drops the oldest items once the buffer exceeds its configured capacity.

use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct BoundedBuffer<T> {
    items: Arc<Mutex<Vec<T>>>,
    max_size: usize,
}

impl<T: Clone> BoundedBuffer<T> {
    /// Create a new buffer capped at `max_size` items.
    pub fn new(max_size: usize) -> Self {
        Self {
            items: Arc::new(Mutex::new(Vec::new())),
            max_size,
        }
    }

    /// Add an item, dropping the oldest items past `max_size` if needed.
    pub fn push(&self, item: T) {
        let mut items = self.items.lock().unwrap();
        items.push(item);

        if items.len() > self.max_size {
            let excess = items.len() - self.max_size;
            items.drain(0..excess);
        }
    }

    /// Get a clone of all current items.
    pub fn get_all(&self) -> Vec<T> {
        self.items.lock().unwrap().clone()
    }

    /// Clear all items.
    #[allow(dead_code)]
    pub fn clear(&self) {
        self.items.lock().unwrap().clear();
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn new_buffer_starts_empty() {
        let buffer: BoundedBuffer<i32> = BoundedBuffer::new(3);
        assert!(buffer.get_all().is_empty());
    }

    #[test]
    fn push_appends_items_in_order() {
        let buffer = BoundedBuffer::new(10);
        buffer.push(1);
        buffer.push(2);
        buffer.push(3);
        assert_eq!(buffer.get_all(), vec![1, 2, 3]);
    }

    #[test]
    fn push_drops_oldest_items_past_max_size() {
        let buffer = BoundedBuffer::new(2);
        buffer.push(1);
        buffer.push(2);
        buffer.push(3);
        assert_eq!(buffer.get_all(), vec![2, 3]);
    }

    #[test]
    fn clear_empties_the_buffer() {
        let buffer = BoundedBuffer::new(5);
        buffer.push("a".to_string());
        buffer.push("b".to_string());
        buffer.clear();
        assert!(buffer.get_all().is_empty());
    }

    #[test]
    fn clone_shares_the_same_underlying_buffer() {
        let buffer = BoundedBuffer::new(5);
        let handle = buffer.clone();
        handle.push(42);
        assert_eq!(buffer.get_all(), vec![42]);
    }
}