use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
#[derive(Debug, Clone, Default)]
pub struct CancelToken {
cancelled: Arc<AtomicBool>,
}
impl CancelToken {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn from_arc(cancelled: Arc<AtomicBool>) -> Self {
Self { cancelled }
}
pub fn cancel(&self) {
self.cancelled.store(true, Ordering::Release);
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.cancelled.load(Ordering::Acquire)
}
}
#[inline]
pub(crate) fn bail_if_cancelled(token: Option<&CancelToken>) -> Result<(), crate::PtpError> {
if let Some(t) = token {
if t.is_cancelled() {
return Err(crate::PtpError::Cancelled);
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_token_is_not_cancelled() {
let token = CancelToken::new();
assert!(!token.is_cancelled());
}
#[test]
fn cancel_flips_the_flag() {
let token = CancelToken::new();
token.cancel();
assert!(token.is_cancelled());
}
#[test]
fn cancel_is_visible_through_clones() {
let token = CancelToken::new();
let clone = token.clone();
token.cancel();
assert!(clone.is_cancelled());
}
#[test]
fn cancel_is_idempotent() {
let token = CancelToken::new();
token.cancel();
token.cancel();
assert!(token.is_cancelled());
}
#[test]
fn bail_if_cancelled_returns_ok_for_none() {
assert!(bail_if_cancelled(None).is_ok());
}
#[test]
fn bail_if_cancelled_returns_ok_for_unset_token() {
let token = CancelToken::new();
assert!(bail_if_cancelled(Some(&token)).is_ok());
}
#[test]
fn bail_if_cancelled_returns_cancelled_for_set_token() {
let token = CancelToken::new();
token.cancel();
let err = bail_if_cancelled(Some(&token)).unwrap_err();
assert!(matches!(err, crate::PtpError::Cancelled));
}
}