use crate::header::Counter;
use std::convert::Infallible;
pub trait FrameCounter {
type Error: std::error::Error + Send + Sync + 'static;
fn try_next(&mut self) -> Result<Counter, Self::Error>;
}
#[derive(Copy, Clone, Debug)]
pub struct MonotonicCounter {
current_counter: Counter,
max_counter: Counter,
exhausted: bool,
}
impl MonotonicCounter {
pub fn new(max_counter: Counter) -> Self {
Self::with_start_value(0, max_counter)
}
pub fn with_start_value(start_value: Counter, max_counter: Counter) -> Self {
Self {
current_counter: start_value,
max_counter,
exhausted: false,
}
}
pub fn current(&self) -> Counter {
self.current_counter
}
pub fn is_exhausted(&self) -> bool {
self.exhausted
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("MonotonicCounter is exhausted, its maximum value {max} was already used")]
pub struct CounterExhausted {
max: Counter,
}
impl CounterExhausted {
pub fn max(&self) -> Counter {
self.max
}
}
impl FrameCounter for MonotonicCounter {
type Error = CounterExhausted;
fn try_next(&mut self) -> Result<Counter, Self::Error> {
if self.exhausted {
return Err(CounterExhausted {
max: self.max_counter,
});
}
let counter = self.current_counter;
if counter >= self.max_counter {
self.exhausted = true;
} else {
self.current_counter += 1;
}
Ok(counter)
}
}
impl Default for MonotonicCounter {
fn default() -> Self {
Self::new(Counter::MAX)
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct PanickingMonotonicCounter(MonotonicCounter);
impl PanickingMonotonicCounter {
pub fn new(max_counter: Counter) -> Self {
Self::with_start_value(0, max_counter)
}
pub fn with_start_value(start_value: Counter, max_counter: Counter) -> Self {
Self(MonotonicCounter::with_start_value(start_value, max_counter))
}
pub fn current(&self) -> Counter {
self.0.current()
}
pub fn is_exhausted(&self) -> bool {
self.0.is_exhausted()
}
}
impl FrameCounter for PanickingMonotonicCounter {
type Error = Infallible;
fn try_next(&mut self) -> Result<Counter, Self::Error> {
let cnt = self.0.try_next().expect("Counter was exhausted");
Ok(cnt)
}
}
#[derive(Copy, Clone, Debug)]
pub struct SingleUseCounter(Option<Counter>);
impl SingleUseCounter {
pub fn new(counter: Counter) -> Self {
Self(Some(counter))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("SingleUseCounter was already used")]
pub struct CounterUsed;
impl FrameCounter for SingleUseCounter {
type Error = CounterUsed;
fn try_next(&mut self) -> Result<Counter, Self::Error> {
self.0.take().ok_or(CounterUsed)
}
}
#[derive(Copy, Clone, Debug)]
pub struct PanickingSingleUseCounter(SingleUseCounter);
impl PanickingSingleUseCounter {
pub fn new(counter: Counter) -> Self {
Self(SingleUseCounter::new(counter))
}
}
impl FrameCounter for PanickingSingleUseCounter {
type Error = Infallible;
fn try_next(&mut self) -> Result<Counter, Self::Error> {
Ok(self.0.try_next().unwrap_or_else(|err| panic!("{err}")))
}
}
#[cfg(test)]
mod test {
use crate::{frame::FrameCounter, header::Counter};
use super::{MonotonicCounter, SingleUseCounter};
use pretty_assertions::assert_eq;
#[test]
fn create_increasing_counters() {
let mut counter = MonotonicCounter::default();
for i in 0..10 {
assert_eq!(counter.try_next().unwrap(), i);
}
}
#[test]
fn is_exhausted_after_max_counter_was_returned() {
let mut counter = MonotonicCounter::new(1);
assert_eq!(counter.try_next().unwrap(), 0);
assert!(!counter.is_exhausted());
assert_eq!(counter.try_next().unwrap(), 1);
assert!(counter.is_exhausted());
}
#[test]
fn returns_err_when_exhausted() {
let mut counter = MonotonicCounter::new(1);
counter.try_next().unwrap();
counter.try_next().unwrap();
assert!(counter.is_exhausted());
assert!(counter.try_next().is_err());
}
#[test]
fn returns_err_when_u64_max_was_reached() {
let mut counter = MonotonicCounter::with_start_value(Counter::MAX - 1, Counter::MAX);
assert_eq!(counter.try_next().unwrap(), Counter::MAX - 1);
assert_eq!(counter.try_next().unwrap(), Counter::MAX);
assert!(counter.is_exhausted());
assert!(counter.try_next().is_err());
}
#[test]
fn single_use_counter_returns_its_value_once() {
let mut counter = SingleUseCounter::new(42);
assert_eq!(counter.try_next().unwrap(), 42);
assert!(counter.try_next().is_err());
}
}