use crate::observer::{Event, Termination};
use educe::Educe;
use std::collections::VecDeque;
#[derive(Educe)]
#[educe(Debug, Clone, PartialEq, Eq)]
pub enum EventBatch<T, E> {
Next(T),
Termination(Termination<E>),
NextAndTermination(T, Termination<E>),
NextBatch(Vec<T>),
NextBatchAndTermination(Vec<T>, Termination<E>),
}
impl<T, E> EventBatch<T, E> {
pub fn ends_stream(&self) -> bool {
matches!(
self,
Self::Termination(_) | Self::NextAndTermination(..) | Self::NextBatchAndTermination(..)
)
}
}
#[derive(Educe)]
#[educe(Debug)]
pub struct PendingEvents<T, E> {
values: VecDeque<T>,
termination: Option<Termination<E>>,
}
impl<T, E> PendingEvents<T, E> {
pub fn new() -> Self {
Self {
values: VecDeque::new(),
termination: None,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
values: VecDeque::with_capacity(capacity),
termination: None,
}
}
pub fn from_batch(events: EventBatch<T, E>) -> (Option<T>, Self) {
let empty_queue = |termination| Self {
values: VecDeque::new(),
termination,
};
let queue_batch = |values: Vec<T>, termination| {
let mut values = VecDeque::from(values);
let first_next = values.pop_front();
(
first_next,
Self {
values,
termination,
},
)
};
match events {
EventBatch::Next(value) => (Some(value), empty_queue(None)),
EventBatch::Termination(termination) => (None, empty_queue(Some(termination))),
EventBatch::NextAndTermination(value, termination) => {
(Some(value), empty_queue(Some(termination)))
}
EventBatch::NextBatch(values) => queue_batch(values, None),
EventBatch::NextBatchAndTermination(values, termination) => {
queue_batch(values, Some(termination))
}
}
}
pub fn is_terminated(&self) -> bool {
self.termination.is_some()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty() && self.termination.is_none()
}
#[must_use = "a rejected event must be dropped outside the lock that guards these events"]
pub fn push(&mut self, event: Event<T, E>) -> Option<Event<T, E>> {
if self.is_terminated() {
return Some(event);
}
match event {
Event::Next(value) => self.values.push_back(value),
Event::Termination(termination) => self.termination = Some(termination),
}
None
}
#[must_use = "rejected events must be dropped outside the lock that guards these events"]
pub fn push_batch(&mut self, events: EventBatch<T, E>) -> Option<EventBatch<T, E>> {
if self.is_terminated() {
return Some(events);
}
match events {
EventBatch::Next(value) => self.values.push_back(value),
EventBatch::Termination(termination) => self.termination = Some(termination),
EventBatch::NextAndTermination(value, termination) => {
self.values.push_back(value);
self.termination = Some(termination);
}
EventBatch::NextBatch(values) => self.values.extend(values),
EventBatch::NextBatchAndTermination(values, termination) => {
self.values.extend(values);
self.termination = Some(termination);
}
}
None
}
pub fn pop(&mut self) -> Option<Event<T, E>> {
match self.pop_next() {
Some(value) => Some(Event::Next(value)),
None => self.take_termination().map(Event::Termination),
}
}
pub fn pop_next(&mut self) -> Option<T> {
self.values.pop_front()
}
pub fn take_termination(&mut self) -> Option<Termination<E>> {
self.termination.take()
}
}
impl<T, E> Default for PendingEvents<T, E> {
fn default() -> Self {
Self::new()
}
}