use crate::{ChildStopper, Stopper};
pub trait Cancellable: Clone + Send {
fn stop(&self);
}
impl Cancellable for Stopper {
#[inline]
fn stop(&self) {
self.cancel();
}
}
impl Cancellable for ChildStopper {
#[inline]
fn stop(&self) {
self.cancel();
}
}
#[derive(Debug)]
pub struct CancelGuard<C: Cancellable> {
source: Option<C>,
}
impl<C: Cancellable> CancelGuard<C> {
#[inline]
pub fn new(source: C) -> Self {
Self {
source: Some(source),
}
}
#[inline]
pub fn disarm(mut self) {
self.source = None;
}
#[inline]
pub fn is_armed(&self) -> bool {
self.source.is_some()
}
#[inline]
pub fn source(&self) -> Option<&C> {
self.source.as_ref()
}
}
impl<C: Cancellable> Drop for CancelGuard<C> {
fn drop(&mut self) {
if let Some(source) = self.source.take() {
source.stop();
}
}
}
pub trait StopDropRoll: Cancellable {
fn stop_on_drop(&self) -> CancelGuard<Self>;
}
impl<C: Cancellable> StopDropRoll for C {
#[inline]
fn stop_on_drop(&self) -> CancelGuard<Self> {
CancelGuard::new(self.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Stop, StopExt};
#[test]
fn guard_cancels_on_drop() {
let source = Stopper::new();
assert!(!source.is_cancelled());
{
let _guard = source.stop_on_drop();
}
assert!(source.is_cancelled());
}
#[test]
fn guard_disarm_prevents_cancel() {
let source = Stopper::new();
{
let guard = source.stop_on_drop();
guard.disarm();
}
assert!(!source.is_cancelled());
}
#[test]
fn guard_is_armed() {
let source = Stopper::new();
let guard = source.stop_on_drop();
assert!(guard.is_armed());
guard.disarm();
}
#[test]
fn guard_source_accessor() {
let source = Stopper::new();
let guard = source.stop_on_drop();
assert!(guard.source().is_some());
}
#[test]
fn guard_pattern_success() {
fn work(source: &Stopper) -> Result<i32, &'static str> {
let guard = source.stop_on_drop();
let result = Ok(42);
guard.disarm();
result
}
let source = Stopper::new();
assert_eq!(work(&source), Ok(42));
assert!(!source.is_cancelled());
}
#[test]
fn guard_pattern_failure() {
fn work(source: &Stopper) -> Result<i32, &'static str> {
let _guard = source.stop_on_drop();
Err("failed")
}
let source = Stopper::new();
assert_eq!(work(&source), Err("failed"));
assert!(source.is_cancelled());
}
#[test]
fn guard_multiple_clones() {
let source = Stopper::new();
let source2 = source.clone();
{
let _guard = source.stop_on_drop();
}
assert!(source.is_cancelled());
assert!(source2.is_cancelled());
}
#[test]
fn guard_with_clone() {
let source = Stopper::new();
let clone = source.clone();
assert!(!clone.should_stop());
{
let _guard = source.stop_on_drop();
}
assert!(clone.should_stop());
}
#[test]
fn guard_tree_stopper() {
let parent = Stopper::new();
let child = parent.child();
{
let _guard = child.stop_on_drop();
}
assert!(child.is_cancelled());
assert!(!parent.is_cancelled());
}
#[test]
fn guard_tree_stopper_disarm() {
let parent = Stopper::new();
let child = parent.child();
{
let guard = child.stop_on_drop();
guard.disarm();
}
assert!(!child.is_cancelled());
assert!(!parent.is_cancelled());
}
}