use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use crate::{AsAny, 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: AsAny {
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)));
}
#[derive(Default)]
struct OtherToken;
impl CancellationToken for OtherToken {
fn is_cancelled(&self) -> bool {
false
}
fn cancelled_future(&self) -> CancelledFuture<'_> {
Box::pin(ready(()))
}
}
#[test]
fn downcast_recovers_the_same_token() {
let erased: CancellationTokenRef = Arc::new(TestToken::default());
let recovered = erased
.clone()
.as_any()
.downcast::<TestToken>()
.expect("erased token should downcast to its concrete type");
recovered.cancel();
assert!(erased.is_cancelled());
}
#[test]
fn downcast_to_the_wrong_type_fails() {
let erased: CancellationTokenRef = Arc::new(TestToken::default());
assert!(erased.clone().as_any().downcast::<OtherToken>().is_err());
assert!(erased
.as_ref()
.any_ref()
.downcast_ref::<OtherToken>()
.is_none());
}
#[test]
fn any_ref_borrows_the_token_through_the_trait_object() {
let token = Arc::new(TestToken::default());
let erased: CancellationTokenRef = token.clone();
assert!(erased.any_ref().downcast_ref::<TestToken>().is_none());
let borrowed = erased
.as_ref()
.any_ref()
.downcast_ref::<TestToken>()
.expect("erased token should downcast to its concrete type");
assert!(!borrowed.is_cancelled());
token.cancel();
assert!(borrowed.is_cancelled());
}
}