sigterm 0.3.10

Signal-aware async control and cancellation primitives for Tokio.
Documentation
/* src/guard.rs */

//! RAII guard that triggers shutdown on drop.
//!
//! This module provides [`ShutdownGuard`], a struct that triggers a shutdown signal
//! when it goes out of scope.

#[cfg(feature = "cancel")]
use crate::cancel::CancellationToken;
#[cfg(feature = "sync")]
use crate::shutdown::ShutdownHandle;

/// A trait for items that can trigger a shutdown.
pub trait Trigger {
	/// Triggers the shutdown signal.
	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();
	}
}

/// A guard that triggers a shutdown signal when dropped.
pub struct ShutdownGuard<T: Trigger> {
	trigger: Option<T>,
}

impl<T: Trigger> ShutdownGuard<T> {
	/// Creates a new guard that will consume and trigger the given item on drop.
	pub fn new(trigger: T) -> Self {
		#[cfg(feature = "tracing")]
		tracing::trace!("creating new shutdown guard");

		Self {
			trigger: Some(trigger),
		}
	}

	/// Consumes the guard without triggering the shutdown.
	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;

	// Mock trigger for testing
	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();
		}
		// Should NOT be called because we disarmed it
		assert!(!*called.borrow());
	}
}