use std::fmt;
use super::token::CancelToken;
pub struct CancelScope {
token: CancelToken,
}
impl CancelScope {
#[must_use]
pub fn new(parent: Option<CancelToken>) -> Self {
let token = parent.map_or_else(CancelToken::root, |parent| parent.child());
Self { token }
}
pub fn cancel(&self) {
self.token.cancel();
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.token.is_cancelled()
}
#[must_use]
pub fn token(&self) -> CancelToken {
self.token.clone()
}
}
impl fmt::Debug for CancelScope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CancelScope")
.field("cancelled", &self.is_cancelled())
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use kithara_test_utils::kithara;
use super::CancelScope;
use crate::common::cancel::CancelToken;
#[kithara::test(timeout(Duration::from_secs(5)))]
fn standalone_scope_owns_uncancelled_root() {
let scope = CancelScope::new(None);
assert!(!scope.is_cancelled());
assert!(!scope.token().is_cancelled());
}
#[kithara::test(timeout(Duration::from_secs(5)))]
fn standalone_cancel_cancels_own_subtree() {
let scope = CancelScope::new(None);
let token = scope.token();
scope.cancel();
assert!(scope.is_cancelled());
assert!(token.is_cancelled());
}
#[kithara::test(timeout(Duration::from_secs(5)))]
fn composed_scope_observes_parent_cancel() {
let parent = CancelToken::never();
let scope = CancelScope::new(Some(parent.clone()));
let token = scope.token();
assert!(!scope.is_cancelled());
parent.cancel();
assert!(scope.is_cancelled());
assert!(token.is_cancelled());
}
#[kithara::test(timeout(Duration::from_secs(5)))]
fn composed_scope_cancel_does_not_cancel_parent() {
let parent = CancelToken::never();
let scope = CancelScope::new(Some(parent.clone()));
scope.cancel();
assert!(scope.is_cancelled());
assert!(
!parent.is_cancelled(),
"scope.cancel() must not cancel the parent token it was handed"
);
}
#[kithara::test(timeout(Duration::from_secs(5)))]
fn drop_is_passive() {
let parent = CancelToken::never();
let live = {
let scope = CancelScope::new(Some(parent.clone()));
scope.token()
};
assert!(
!live.is_cancelled() && !parent.is_cancelled(),
"dropping a scope must not cancel anything"
);
}
}