use core::{
fmt,
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
};
pub trait Strategy {
fn should_run() -> bool;
}
#[derive(Debug)]
pub enum Always {}
impl Strategy for Always {
#[inline(always)]
fn should_run() -> bool {
true
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[derive(Debug)]
pub enum OnSuccess {}
#[cfg(feature = "std")]
impl Strategy for OnSuccess {
#[inline]
fn should_run() -> bool {
!std::thread::panicking()
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[derive(Debug)]
pub enum OnUnwind {}
#[cfg(feature = "std")]
impl Strategy for OnUnwind {
#[inline]
fn should_run() -> bool {
std::thread::panicking()
}
}
#[must_use = "store the guard so its deferred action runs at the intended scope exit"]
pub struct DeferGuard<F: FnOnce(), S: Strategy = Always> {
action: Option<F>,
strategy: PhantomData<fn() -> S>,
}
impl<F: FnOnce()> DeferGuard<F> {
#[inline]
pub const fn new(action: F) -> Self {
Self::with_strategy(action)
}
}
impl<F: FnOnce(), S: Strategy> DeferGuard<F, S> {
#[inline]
pub const fn with_strategy(action: F) -> Self {
Self {
action: Some(action),
strategy: PhantomData,
}
}
#[inline]
pub fn disarm(mut self) -> F {
self.action
.take()
.expect("an armed defer guard always contains its action")
}
#[inline]
pub fn run_now(mut self) {
if let Some(action) = self.action.take() {
action();
}
}
}
impl<F: FnOnce(), S: Strategy> Drop for DeferGuard<F, S> {
#[inline]
fn drop(&mut self) {
let Some(action) = self.action.take() else {
return;
};
if S::should_run() {
action();
}
}
}
impl<F: FnOnce(), S: Strategy> fmt::Debug for DeferGuard<F, S> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("DeferGuard")
.field("armed", &self.action.is_some())
.finish()
}
}
enum ScopeState<T, F> {
Armed { value: T, action: F },
Disarmed,
}
#[must_use = "store the guard so its deferred action runs at the intended scope exit"]
pub struct ScopeGuard<T, F, S = Always>
where
F: FnOnce(T),
S: Strategy,
{
state: ScopeState<T, F>,
strategy: PhantomData<fn() -> S>,
}
impl<T, F, S> ScopeGuard<T, F, S>
where
F: FnOnce(T),
S: Strategy,
{
#[inline]
pub const fn with_strategy(value: T, action: F) -> Self {
Self {
state: ScopeState::Armed { value, action },
strategy: PhantomData,
}
}
#[inline]
pub fn into_inner(mut self) -> T {
let (value, action) = match mem::replace(&mut self.state, ScopeState::Disarmed) {
ScopeState::Armed { value, action } => (value, action),
ScopeState::Disarmed => unreachable!("a live scope guard is armed"),
};
drop(action);
value
}
#[inline]
pub fn into_parts(mut self) -> (T, F) {
match mem::replace(&mut self.state, ScopeState::Disarmed) {
ScopeState::Armed { value, action } => (value, action),
ScopeState::Disarmed => unreachable!("a live scope guard is armed"),
}
}
#[inline]
pub fn run_now(mut self) {
if let ScopeState::Armed { value, action } =
mem::replace(&mut self.state, ScopeState::Disarmed)
{
action(value);
}
}
fn value(&self) -> &T {
match &self.state {
ScopeState::Armed { value, .. } => value,
ScopeState::Disarmed => unreachable!("a live scope guard is armed"),
}
}
fn value_mut(&mut self) -> &mut T {
match &mut self.state {
ScopeState::Armed { value, .. } => value,
ScopeState::Disarmed => unreachable!("a live scope guard is armed"),
}
}
}
impl<T, F, S> Deref for ScopeGuard<T, F, S>
where
F: FnOnce(T),
S: Strategy,
{
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.value()
}
}
impl<T, F, S> DerefMut for ScopeGuard<T, F, S>
where
F: FnOnce(T),
S: Strategy,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.value_mut()
}
}
impl<T, F, S> Drop for ScopeGuard<T, F, S>
where
F: FnOnce(T),
S: Strategy,
{
#[inline]
fn drop(&mut self) {
let ScopeState::Armed { value, action } =
mem::replace(&mut self.state, ScopeState::Disarmed)
else {
return;
};
if S::should_run() {
action(value);
}
}
}
impl<T, F, S> fmt::Debug for ScopeGuard<T, F, S>
where
T: fmt::Debug,
F: FnOnce(T),
S: Strategy,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ScopeGuard")
.field("value", self.value())
.finish()
}
}
#[inline]
pub const fn defer<F: FnOnce()>(action: F) -> DeferGuard<F> {
DeferGuard::new(action)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub const fn defer_on_success<F: FnOnce()>(action: F) -> DeferGuard<F, OnSuccess> {
DeferGuard::with_strategy(action)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub const fn defer_on_unwind<F: FnOnce()>(action: F) -> DeferGuard<F, OnUnwind> {
DeferGuard::with_strategy(action)
}
#[inline]
pub const fn guard<T, F>(value: T, action: F) -> ScopeGuard<T, F>
where
F: FnOnce(T),
{
ScopeGuard::with_strategy(value, action)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub const fn guard_on_success<T, F>(value: T, action: F) -> ScopeGuard<T, F, OnSuccess>
where
F: FnOnce(T),
{
ScopeGuard::with_strategy(value, action)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub const fn guard_on_unwind<T, F>(value: T, action: F) -> ScopeGuard<T, F, OnUnwind>
where
F: FnOnce(T),
{
ScopeGuard::with_strategy(value, action)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[macro_export]
macro_rules! defer_on_success {
(move $($body:tt)*) => {
let _defer_on_success_guard = $crate::defer_on_success(move || { $($body)* });
};
($($body:tt)*) => {
let _defer_on_success_guard = $crate::defer_on_success(|| { $($body)* });
};
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[macro_export]
macro_rules! defer_on_unwind {
(move $($body:tt)*) => {
let _defer_on_unwind_guard = $crate::defer_on_unwind(move || { $($body)* });
};
($($body:tt)*) => {
let _defer_on_unwind_guard = $crate::defer_on_unwind(|| { $($body)* });
};
}