use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use crate::{DeltaResult, Error};
pub type CancellationTokenRef = Arc<dyn CancellationToken>;
pub type CancelledFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
pub(crate) fn check_cancelled(token: Option<&CancellationTokenRef>) -> DeltaResult<()> {
match token {
Some(t) if t.is_cancelled() => Err(Error::Cancelled),
_ => Ok(()),
}
}
pub trait CancellationToken: Send + Sync {
fn is_cancelled(&self) -> bool;
fn cancelled_future(&self) -> CancelledFuture<'_>;
}
pub(crate) struct CancellableIterator<I> {
inner: I,
token: Option<CancellationTokenRef>,
done: bool,
}
impl<I> CancellableIterator<I> {
pub(crate) fn new(inner: I, token: Option<CancellationTokenRef>) -> Self {
Self {
inner,
token,
done: false,
}
}
}
impl<I, T> Iterator for CancellableIterator<I>
where
I: Iterator<Item = DeltaResult<T>>,
{
type Item = DeltaResult<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
if self.token.as_ref().is_some_and(|t| t.is_cancelled()) {
self.done = true;
return Some(Err(Error::Cancelled));
}
let item = self.inner.next();
if matches!(item, Some(Err(Error::Cancelled))) {
self.done = true;
}
item
}
}
#[cfg(test)]
mod tests {
use std::future::ready;
use std::sync::atomic::{AtomicBool, Ordering};
use super::*;
#[derive(Default)]
struct TestToken(AtomicBool);
impl TestToken {
fn cancel(&self) {
self.0.store(true, Ordering::SeqCst);
}
}
impl CancellationToken for TestToken {
fn is_cancelled(&self) -> bool {
self.0.load(Ordering::SeqCst)
}
fn cancelled_future(&self) -> CancelledFuture<'_> {
Box::pin(ready(()))
}
}
fn ok_iter(n: usize) -> impl Iterator<Item = DeltaResult<usize>> {
(0..n).map(Ok)
}
#[test]
fn no_token_passes_through_unchanged() {
let out: Vec<_> = CancellableIterator::new(ok_iter(3), None)
.map(Result::unwrap)
.collect();
assert_eq!(out, vec![0, 1, 2]);
}
#[test]
fn uncancelled_token_passes_through_unchanged() {
let token: CancellationTokenRef = Arc::new(TestToken::default());
let out: Vec<_> = CancellableIterator::new(ok_iter(3), Some(token))
.map(Result::unwrap)
.collect();
assert_eq!(out, vec![0, 1, 2]);
}
#[test]
fn pre_cancelled_yields_one_error_then_ends() {
let token = Arc::new(TestToken::default());
token.cancel();
let mut iter = CancellableIterator::new(ok_iter(3), Some(token as CancellationTokenRef));
assert!(matches!(iter.next(), Some(Err(Error::Cancelled))));
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn mid_stream_cancellation_yields_error_not_silent_truncation() {
let token = Arc::new(TestToken::default());
let ct: CancellationTokenRef = token.clone();
let mut iter = CancellableIterator::new(ok_iter(5), Some(ct));
assert!(matches!(iter.next(), Some(Ok(0))));
assert!(matches!(iter.next(), Some(Ok(1))));
token.cancel();
assert!(matches!(iter.next(), Some(Err(Error::Cancelled))));
assert!(iter.next().is_none());
}
#[test]
fn inner_cancelled_error_fuses_without_double_emit() {
let token: CancellationTokenRef = Arc::new(TestToken::default());
let inner = vec![Ok(0), Err(Error::Cancelled), Ok(99)].into_iter();
let mut iter = CancellableIterator::new(inner, Some(token));
assert!(matches!(iter.next(), Some(Ok(0))));
assert!(matches!(iter.next(), Some(Err(Error::Cancelled))));
assert!(iter.next().is_none());
}
#[test]
fn check_cancelled_reports_state() {
let token = Arc::new(TestToken::default());
let ct: CancellationTokenRef = token.clone();
assert!(check_cancelled(Some(&ct)).is_ok());
assert!(check_cancelled(None).is_ok());
token.cancel();
assert!(matches!(check_cancelled(Some(&ct)), Err(Error::Cancelled)));
}
}