use alloc::boxed::Box;
use crate::{Stop, StopReason};
pub struct BoxedStop(Option<Box<dyn Stop + Send + Sync>>);
impl BoxedStop {
#[inline]
pub fn new<T: Stop + 'static>(stop: T) -> Self {
if !stop.may_stop() {
return Self(None);
}
Self(Some(Box::new(stop)))
}
}
impl Stop for BoxedStop {
#[inline]
fn check(&self) -> Result<(), StopReason> {
match &self.0 {
Some(inner) => inner.check(),
None => Ok(()),
}
}
#[inline]
fn should_stop(&self) -> bool {
match &self.0 {
Some(inner) => inner.should_stop(),
None => false,
}
}
#[inline]
fn may_stop(&self) -> bool {
self.0.is_some()
}
}
impl core::fmt::Debug for BoxedStop {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("BoxedStop").finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{StopSource, Stopper, Unstoppable};
#[test]
fn boxed_stop_from_unstoppable() {
let stop = BoxedStop::new(Unstoppable);
assert!(!stop.should_stop());
assert!(stop.check().is_ok());
assert!(!stop.may_stop());
}
#[test]
fn boxed_stop_from_stopper() {
let stopper = Stopper::new();
let stop = BoxedStop::new(stopper.clone());
assert!(!stop.should_stop());
stopper.cancel();
assert!(stop.should_stop());
assert_eq!(stop.check(), Err(StopReason::Cancelled));
}
#[test]
fn boxed_stop_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<BoxedStop>();
}
#[test]
fn boxed_stop_debug() {
let stop = BoxedStop::new(Unstoppable);
let debug = alloc::format!("{:?}", stop);
assert!(debug.contains("BoxedStop"));
}
#[test]
fn boxed_stop_avoids_monomorphization() {
fn process(stop: BoxedStop) -> bool {
stop.should_stop()
}
assert!(!process(BoxedStop::new(Unstoppable)));
assert!(!process(BoxedStop::new(StopSource::new())));
assert!(!process(BoxedStop::new(Stopper::new())));
}
#[test]
fn may_stop_delegates_through_boxed() {
assert!(!BoxedStop::new(Unstoppable).may_stop());
assert!(BoxedStop::new(Stopper::new()).may_stop());
}
#[test]
fn unstoppable_no_allocation() {
let stop = BoxedStop::new(Unstoppable);
assert!(!stop.may_stop());
assert!(stop.check().is_ok());
}
}