use std::marker::PhantomData;
#[derive(Debug)]
pub(crate) struct BatchBoundary {
between_events: bool,
}
impl Default for BatchBoundary {
fn default() -> Self {
Self::new()
}
}
impl BatchBoundary {
pub(crate) fn new() -> Self {
Self {
between_events: true,
}
}
#[inline]
pub(crate) fn handled(&mut self, is_last_in_request: bool) {
self.between_events = is_last_in_request;
}
#[inline]
pub(crate) fn at_boundary(&self) -> Option<AtBoundary<'_>> {
self.between_events.then_some(AtBoundary {
_tracker: PhantomData,
})
}
}
#[derive(Debug)]
pub(crate) struct AtBoundary<'t> {
_tracker: PhantomData<&'t BatchBoundary>,
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn a_fresh_tracker_is_between_events() {
assert!(BatchBoundary::new().at_boundary().is_some());
assert!(BatchBoundary::default().at_boundary().is_some());
}
#[test]
fn a_slot_that_does_not_end_its_event_closes_the_boundary_until_one_does() {
let mut tracker = BatchBoundary::new();
tracker.handled(false);
assert!(tracker.at_boundary().is_none(), "inside an event");
tracker.handled(false);
assert!(tracker.at_boundary().is_none(), "still inside it");
tracker.handled(true);
assert!(tracker.at_boundary().is_some(), "the event ended");
tracker.handled(true);
assert!(tracker.at_boundary().is_some(), "a one-slot event");
}
proptest! {
#[test]
fn the_token_follows_the_last_slot(slots in proptest::collection::vec(any::<bool>(), 0..64)) {
let mut tracker = BatchBoundary::new();
prop_assert!(tracker.at_boundary().is_some());
for is_last in slots {
tracker.handled(is_last);
prop_assert_eq!(tracker.at_boundary().is_some(), is_last);
}
}
}
}