#[cfg(feature = "cancel")]
use crate::cancel::CancellationToken;
#[cfg(feature = "sync")]
use crate::shutdown::ShutdownHandle;
pub trait Trigger {
fn trigger(self);
}
#[cfg(feature = "sync")]
impl Trigger for ShutdownHandle {
fn trigger(self) {
self.shutdown();
}
}
#[cfg(feature = "cancel")]
impl Trigger for CancellationToken {
fn trigger(self) {
self.cancel();
}
}
pub struct ShutdownGuard<T: Trigger> {
trigger: Option<T>,
}
impl<T: Trigger> ShutdownGuard<T> {
pub fn new(trigger: T) -> Self {
#[cfg(feature = "tracing")]
tracing::trace!("creating new shutdown guard");
Self {
trigger: Some(trigger),
}
}
pub fn disarm(mut self) -> T {
#[cfg(feature = "tracing")]
tracing::debug!("disarming shutdown guard");
self
.trigger
.take()
.expect("ShutdownGuard was already disarmed or dropped")
}
}
impl<T: Trigger> Drop for ShutdownGuard<T> {
fn drop(&mut self) {
if let Some(trigger) = self.trigger.take() {
#[cfg(feature = "tracing")]
tracing::debug!("shutdown guard dropped, triggering shutdown");
trigger.trigger();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::rc::Rc;
struct MockTrigger {
called: Rc<RefCell<bool>>,
}
impl Trigger for MockTrigger {
fn trigger(self) {
*self.called.borrow_mut() = true;
}
}
#[test]
fn test_guard_triggers_on_drop() {
let called = Rc::new(RefCell::new(false));
{
let _guard = ShutdownGuard::new(MockTrigger {
called: called.clone(),
});
}
assert!(*called.borrow());
}
#[test]
fn test_guard_disarm() {
let called = Rc::new(RefCell::new(false));
{
let guard = ShutdownGuard::new(MockTrigger {
called: called.clone(),
});
let _trigger = guard.disarm();
}
assert!(!*called.borrow());
}
}