1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// devela::code::guard
//
//! Defines [`ScopeGuard`].
//
use crate::{Deref, DerefMut};
#[doc = crate::_tags!(guard)]
/// A general-purpose RAII guard that executes a callback on drop.
#[doc = crate::_doc_location!("code")]
///
/// - The callback can take both a value and a state.
/// - The state can be updated dynamically during the guard's lifetime.
/// - The guard can be dismissed, preventing the callback from executing on drop.
///
/// # Features
/// Uses `unsafe_layout` to avoid redundant unwrapping checks.
#[doc = crate::_doc!(vendor: "stated-scope-guard")]
#[derive(Debug)]
pub struct ScopeGuard<T, F: FnOnce(T, &S), S> {
/// The guarded value,
/// wrapped in an Option to allow taking ownership during drop.
value: Option<T>,
/// A callback to process the value and state on drop,
/// wrapped in an Option for safe ownership transfer.
callback: Option<F>,
/// The associated state passed to the callback.
state: S,
}
impl<T> ScopeGuard<T, fn(T, &bool), bool> {
/// Constructs a scope guard with a boolean state (defaulting to true).
///
/// The callback is executed on drop unless dismissed.
///
/// # Example
/// ```
/// # use devela::{Cell, ScopeGuard};
/// let result = Cell::new(0);
/// {
/// let _guard = ScopeGuard::new(10, |value| {
/// result.set(value + 5);
/// });
/// }
/// assert_eq!(result.get(), 15);
/// ```
pub fn new<F: FnOnce(T)>(value: T, callback: F) -> ScopeGuard<T, impl FnOnce(T, &bool), bool> {
ScopeGuard::with(value, true, move |value, state: &bool| {
if *state {
callback(value);
}
})
}
}
impl<T, F: FnOnce(T, &bool)> ScopeGuard<T, F, bool> {
/// Dismisses the callback for a boolean state guard.
///
/// Once dismissed, the callback won't be executed on drop.
///
/// # Example
/// ```
/// # use devela::{Cell, ScopeGuard};
/// let result = Cell::new(0);
/// {
/// let mut guard = ScopeGuard::new(10, |value| {
/// result.set(value + 5);
/// });
/// guard.dismiss();
/// }
/// assert_eq!(result.get(), 0);
/// ```
pub fn dismiss(&mut self) {
self.set_state(false);
}
}
impl<T, F: FnOnce(T, &S), S> ScopeGuard<T, F, S> {
/// Creates a scope guard with a custom state.
///
/// The guarded value is accessible via `Deref` and `DerefMut`.
///
/// # Example
/// ```
/// # use devela::{Cell, ScopeGuard};
/// // A simple resource that requires cleanup.
/// struct Resource;
/// impl Resource {
/// fn new() -> Self { Resource }
/// /// Cleans up the resource using the given strategy, updating the flag accordingly.
/// fn cleanup(&self, strategy: &Cleanup, flag: &Cell<&'static str>) {
/// match strategy {
/// Cleanup::Standard => flag.set("standard cleanup"),
/// Cleanup::Alternate => flag.set("alternate cleanup"),
/// }
/// }
/// }
/// // Define different cleanup strategies.
/// enum Cleanup {
/// Standard,
/// Alternate,
/// }
/// let cleanup_flag = Cell::new("not cleaned");
/// {
/// let mut guard = ScopeGuard::with(
/// Resource::new(),
/// Cleanup::Standard,
/// |res, strategy| { res.cleanup(strategy, &cleanup_flag) }
/// );
/// // Perform operations that require changing the cleanup strategy.
/// guard.set_state(Cleanup::Alternate);
/// } // When the guard goes out of scope, it triggers the cleanup callback.
/// assert_eq!(cleanup_flag.get(), "alternate cleanup");
/// ```
pub fn with(value: T, state: S, callback: F) -> Self {
Self {
value: Some(value),
state,
callback: Some(callback),
}
}
/// Updates the current state.
pub fn set_state(&mut self, state: S) {
self.state = state;
}
}
#[rustfmt::skip]
impl<T, F: FnOnce(T, &S), S> Deref for ScopeGuard<T, F, S> {
type Target = T;
fn deref(&self) -> &Self::Target {
#[cfg(any(feature = "safe_code", not(feature = "unsafe_layout")))]
{ self.value.as_ref().unwrap() }
#[cfg(all(not(feature = "safe_code"), feature = "unsafe_layout"))]
// SAFETY: `value` is always `Some` until dropped
unsafe { self.value.as_ref().unwrap_unchecked() }
}
}
#[rustfmt::skip]
impl<T, F: FnOnce(T, &S), S> DerefMut for ScopeGuard<T, F, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
#[cfg(any(feature = "safe_code", not(feature = "unsafe_layout")))]
{ self.value.as_mut().unwrap() }
#[cfg(all(not(feature = "safe_code"), feature = "unsafe_layout"))]
// SAFETY: `value` is always `Some` until dropped
unsafe { self.value.as_mut().unwrap_unchecked() }
}
}
impl<T, F: FnOnce(T, &S), S> Drop for ScopeGuard<T, F, S> {
/// On drop, invokes the callback with the guarded value and a reference to the current state.
fn drop(&mut self) {
let (value, callback) = {
#[cfg(any(feature = "safe_code", not(feature = "unsafe_layout")))]
{
let value = self.value.take().unwrap();
let callback = self.callback.take().unwrap();
(value, callback)
}
#[cfg(all(not(feature = "safe_code"), feature = "unsafe_layout"))]
{
// SAFETY: `value` is always `Some` until dropped
let value = unsafe { self.value.take().unwrap_unchecked() };
// SAFETY: `callback` is always `Some` until dropped
let callback = unsafe { self.callback.take().unwrap_unchecked() };
(value, callback)
}
};
callback(value, &self.state);
}
}