use std::collections::VecDeque;
use std::error::Error;
use std::fmt::{self, Debug, Display, Formatter};
use std::iter::FromIterator;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct BiteQueue<T> {
items: VecDeque<T>,
}
impl<T> BiteQueue<T> {
pub const fn new() -> Self {
Self {
items: VecDeque::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
items: VecDeque::with_capacity(capacity),
}
}
pub fn push(&mut self, item: T) {
self.items.push_back(item);
}
pub fn front(&self) -> Option<&T> {
self.items.front()
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = &T> + DoubleEndedIterator {
self.items.iter()
}
pub fn bite(&mut self, maximum: usize) -> Result<Bite<'_, T>, BiteError> {
if maximum == 0 {
return Err(BiteError::ZeroSize);
}
let amount = maximum.min(self.items.len());
Ok(self.take_prefix(amount))
}
pub fn bite_exact(&mut self, amount: usize) -> Result<Bite<'_, T>, BiteError> {
if amount == 0 {
return Err(BiteError::ZeroSize);
}
if self.items.len() < amount {
return Err(BiteError::InsufficientItems {
requested: amount,
available: self.items.len(),
});
}
Ok(self.take_prefix(amount))
}
pub fn into_inner(self) -> VecDeque<T> {
self.items
}
fn take_prefix(&mut self, amount: usize) -> Bite<'_, T> {
let items = self.items.drain(..amount).collect();
Bite {
source: self,
items,
finished: false,
}
}
}
impl<T> Extend<T> for BiteQueue<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
self.items.extend(iter);
}
}
impl<T> FromIterator<T> for BiteQueue<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self {
items: iter.into_iter().collect(),
}
}
}
impl<T> IntoIterator for BiteQueue<T> {
type Item = T;
type IntoIter = std::collections::vec_deque::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}
impl<'a, T> IntoIterator for &'a BiteQueue<T> {
type Item = &'a T;
type IntoIter = std::collections::vec_deque::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.items.iter()
}
}
#[must_use = "an uncommitted bite automatically rolls back"]
pub struct Bite<'a, T> {
source: &'a mut BiteQueue<T>,
items: VecDeque<T>,
finished: bool,
}
impl<T: Debug> Debug for Bite<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Bite")
.field("items", &self.items)
.field("finished", &self.finished)
.finish_non_exhaustive()
}
}
impl<T> Bite<'_, T> {
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn front(&self) -> Option<&T> {
self.items.front()
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = &T> + DoubleEndedIterator {
self.items.iter()
}
pub fn commit(mut self) -> Vec<T> {
self.finished = true;
self.items.drain(..).collect()
}
pub fn rollback(mut self) {
self.restore();
}
fn restore(&mut self) {
while let Some(item) = self.items.pop_back() {
self.source.items.push_front(item);
}
self.finished = true;
}
}
impl<T> Drop for Bite<'_, T> {
fn drop(&mut self) {
if !self.finished {
self.restore();
}
}
}
impl<'a, T> IntoIterator for &'a Bite<'_, T> {
type Item = &'a T;
type IntoIter = std::collections::vec_deque::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.items.iter()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BiteError {
ZeroSize,
InsufficientItems {
requested: usize,
available: usize,
},
}
impl Display for BiteError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::ZeroSize => f.write_str("bite size must be greater than zero"),
Self::InsufficientItems {
requested,
available,
} => write!(
f,
"requested {requested} items, but only {available} are available"
),
}
}
}
impl Error for BiteError {}
#[cfg(test)]
mod tests {
use super::*;
use std::panic::{AssertUnwindSafe, catch_unwind};
fn values(queue: &BiteQueue<i32>) -> Vec<i32> {
queue.iter().copied().collect()
}
#[test]
fn commit_permanently_consumes_a_prefix() {
let mut queue = BiteQueue::from_iter([1, 2, 3, 4]);
let committed = queue.bite(2).unwrap().commit();
assert_eq!(committed, [1, 2]);
assert_eq!(values(&queue), [3, 4]);
}
#[test]
fn drop_rolls_back_in_original_order() {
let mut queue = BiteQueue::from_iter([1, 2, 3]);
{
let bite = queue.bite(2).unwrap();
assert_eq!(bite.iter().copied().collect::<Vec<_>>(), [1, 2]);
}
assert_eq!(values(&queue), [1, 2, 3]);
}
#[test]
fn explicit_rollback_restores_the_prefix() {
let mut queue = BiteQueue::from_iter([1, 2, 3]);
queue.bite(2).unwrap().rollback();
assert_eq!(values(&queue), [1, 2, 3]);
}
#[test]
fn panic_unwinding_rolls_back() {
let mut queue = BiteQueue::from_iter([1, 2, 3]);
let result = catch_unwind(AssertUnwindSafe(|| {
let _bite = queue.bite(2).unwrap();
panic!("processing failed");
}));
assert!(result.is_err());
assert_eq!(values(&queue), [1, 2, 3]);
}
#[test]
fn bite_uses_at_most_the_requested_size() {
let mut queue = BiteQueue::from_iter([1, 2]);
let bite = queue.bite(10).unwrap();
assert_eq!(bite.len(), 2);
}
#[test]
fn exact_failure_does_not_change_the_queue() {
let mut queue = BiteQueue::from_iter([1, 2]);
assert_eq!(
queue.bite_exact(3).unwrap_err(),
BiteError::InsufficientItems {
requested: 3,
available: 2,
}
);
assert_eq!(values(&queue), [1, 2]);
}
#[test]
fn zero_size_is_rejected() {
let mut queue = BiteQueue::<i32>::new();
assert_eq!(queue.bite(0).unwrap_err(), BiteError::ZeroSize);
assert_eq!(queue.bite_exact(0).unwrap_err(), BiteError::ZeroSize);
}
}