liminal-server 0.21.1

Standalone server for the liminal messaging bus
Documentation
//! A bounded queue that drops its oldest entry, with a count, when full.
//!
//! A slow reader can never grow server memory beyond `capacity` entries, and
//! what it lost is a number it is told, never a silence: the evictions since
//! the reader last took from the ring travel with what it takes.

use std::collections::VecDeque;

/// A bounded first-in first-out queue that evicts its oldest entry on a push
/// into a full queue and counts every eviction.
#[derive(Debug)]
pub struct BoundedRing<T> {
    entries: VecDeque<T>,
    capacity: usize,
    dropped: u64,
    dropped_since_take: u64,
}

impl<T> BoundedRing<T> {
    /// An empty ring holding at most `capacity` entries (at least one).
    #[must_use]
    pub fn new(capacity: usize) -> Self {
        let capacity = capacity.max(1);
        Self {
            entries: VecDeque::with_capacity(capacity),
            capacity,
            dropped: 0,
            dropped_since_take: 0,
        }
    }

    /// Appends `entry`, evicting and counting the oldest entry when full.
    /// Answers whether an entry was evicted.
    pub fn push(&mut self, entry: T) -> bool {
        let evicted = self.entries.len() >= self.capacity;
        if evicted {
            self.entries.pop_front();
            self.dropped = self.dropped.saturating_add(1);
            self.dropped_since_take = self.dropped_since_take.saturating_add(1);
        }
        self.entries.push_back(entry);
        evicted
    }

    /// Removes every held entry, oldest first, with the evictions since the
    /// previous take; the next take counts from zero again.
    pub fn take(&mut self) -> (std::collections::vec_deque::Drain<'_, T>, u64) {
        let dropped = std::mem::take(&mut self.dropped_since_take);
        (self.entries.drain(..), dropped)
    }

    /// Entries currently held.
    #[must_use]
    pub fn len(&self) -> usize {
        self.entries.len()
    }

    /// Whether the ring holds nothing.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }

    /// The bound.
    #[must_use]
    pub const fn capacity(&self) -> usize {
        self.capacity
    }

    /// Entries evicted since the ring was made (monotonic).
    #[must_use]
    pub const fn dropped(&self) -> u64 {
        self.dropped
    }

    /// Entries evicted since the previous take.
    #[must_use]
    pub const fn dropped_since_take(&self) -> u64 {
        self.dropped_since_take
    }
}