use crate::{Stop, StopReason};
pub struct FnStop<F> {
f: F,
}
impl<F> FnStop<F>
where
F: Fn() -> bool + Send + Sync,
{
#[inline]
pub fn new(f: F) -> Self {
Self { f }
}
}
impl<F> Stop for FnStop<F>
where
F: Fn() -> bool + Send + Sync,
{
#[inline]
fn check(&self) -> Result<(), StopReason> {
if (self.f)() {
Err(StopReason::Cancelled)
} else {
Ok(())
}
}
#[inline]
fn should_stop(&self) -> bool {
(self.f)()
}
}
impl<F: Clone> Clone for FnStop<F> {
fn clone(&self) -> Self {
Self { f: self.f.clone() }
}
}
impl<F: Copy> Copy for FnStop<F> {}
impl<F> core::fmt::Debug for FnStop<F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("FnStop").finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::sync::atomic::{AtomicBool, Ordering};
#[test]
fn fn_stop_basic() {
let flag = AtomicBool::new(false);
let stop = FnStop::new(|| flag.load(Ordering::Relaxed));
assert!(!stop.should_stop());
assert!(stop.check().is_ok());
flag.store(true, Ordering::Relaxed);
assert!(stop.should_stop());
assert_eq!(stop.check(), Err(StopReason::Cancelled));
}
#[test]
fn fn_stop_with_static() {
static FLAG: AtomicBool = AtomicBool::new(false);
let stop = FnStop::new(|| FLAG.load(Ordering::Relaxed));
assert!(!stop.should_stop());
FLAG.store(true, Ordering::Relaxed);
assert!(stop.should_stop());
FLAG.store(false, Ordering::Relaxed);
}
#[test]
fn fn_stop_always_true() {
let stop = FnStop::new(|| true);
assert!(stop.should_stop());
assert_eq!(stop.check(), Err(StopReason::Cancelled));
}
#[test]
fn fn_stop_always_false() {
let stop = FnStop::new(|| false);
assert!(!stop.should_stop());
assert!(stop.check().is_ok());
}
#[test]
fn fn_stop_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<FnStop<fn() -> bool>>();
}
#[test]
fn fn_stop_copy() {
let stop: FnStop<fn() -> bool> = FnStop::new(|| false);
let stop2 = stop; assert!(!stop.should_stop()); assert!(!stop2.should_stop());
}
}
#[cfg(all(test, feature = "alloc"))]
mod alloc_tests {
use super::*;
#[test]
fn fn_stop_debug() {
extern crate alloc;
let stop = FnStop::new(|| false);
let debug = alloc::format!("{:?}", stop);
assert!(debug.contains("FnStop"));
}
}