1use core::{
2 fmt,
3 marker::PhantomData,
4 mem,
5 ops::{Deref, DerefMut},
6};
7
8pub trait Strategy {
10 fn should_run() -> bool;
12}
13
14#[derive(Debug)]
16pub enum Always {}
17
18impl Strategy for Always {
19 #[inline(always)]
20 fn should_run() -> bool {
21 true
22 }
23}
24
25#[cfg(feature = "std")]
30#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
31#[derive(Debug)]
32pub enum OnSuccess {}
33
34#[cfg(feature = "std")]
35impl Strategy for OnSuccess {
36 #[inline]
37 fn should_run() -> bool {
38 !std::thread::panicking()
39 }
40}
41
42#[cfg(feature = "std")]
44#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
45#[derive(Debug)]
46pub enum OnUnwind {}
47
48#[cfg(feature = "std")]
49impl Strategy for OnUnwind {
50 #[inline]
51 fn should_run() -> bool {
52 std::thread::panicking()
53 }
54}
55
56#[must_use = "store the guard so its deferred action runs at the intended scope exit"]
64pub struct DeferGuard<F: FnOnce(), S: Strategy = Always> {
65 action: Option<F>,
66 strategy: PhantomData<fn() -> S>,
67}
68
69impl<F: FnOnce()> DeferGuard<F> {
70 #[inline]
72 pub const fn new(action: F) -> Self {
73 Self::with_strategy(action)
74 }
75}
76
77impl<F: FnOnce(), S: Strategy> DeferGuard<F, S> {
78 #[inline]
80 pub const fn with_strategy(action: F) -> Self {
81 Self {
82 action: Some(action),
83 strategy: PhantomData,
84 }
85 }
86
87 #[inline]
89 pub fn disarm(mut self) -> F {
90 self.action
91 .take()
92 .expect("an armed defer guard always contains its action")
93 }
94
95 #[inline]
97 pub fn run_now(mut self) {
98 if let Some(action) = self.action.take() {
99 action();
100 }
101 }
102}
103
104impl<F: FnOnce(), S: Strategy> Drop for DeferGuard<F, S> {
105 #[inline]
106 fn drop(&mut self) {
107 let Some(action) = self.action.take() else {
108 return;
109 };
110
111 if S::should_run() {
112 action();
113 }
114 }
115}
116
117impl<F: FnOnce(), S: Strategy> fmt::Debug for DeferGuard<F, S> {
118 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
119 formatter
120 .debug_struct("DeferGuard")
121 .field("armed", &self.action.is_some())
122 .finish()
123 }
124}
125
126enum ScopeState<T, F> {
127 Armed { value: T, action: F },
128 Disarmed,
129}
130
131#[must_use = "store the guard so its deferred action runs at the intended scope exit"]
138pub struct ScopeGuard<T, F, S = Always>
139where
140 F: FnOnce(T),
141 S: Strategy,
142{
143 state: ScopeState<T, F>,
144 strategy: PhantomData<fn() -> S>,
145}
146
147impl<T, F, S> ScopeGuard<T, F, S>
148where
149 F: FnOnce(T),
150 S: Strategy,
151{
152 #[inline]
154 pub const fn with_strategy(value: T, action: F) -> Self {
155 Self {
156 state: ScopeState::Armed { value, action },
157 strategy: PhantomData,
158 }
159 }
160
161 #[inline]
163 pub fn into_inner(mut self) -> T {
164 let (value, action) = match mem::replace(&mut self.state, ScopeState::Disarmed) {
165 ScopeState::Armed { value, action } => (value, action),
166 ScopeState::Disarmed => unreachable!("a live scope guard is armed"),
167 };
168 drop(action);
169 value
170 }
171
172 #[inline]
174 pub fn into_parts(mut self) -> (T, F) {
175 match mem::replace(&mut self.state, ScopeState::Disarmed) {
176 ScopeState::Armed { value, action } => (value, action),
177 ScopeState::Disarmed => unreachable!("a live scope guard is armed"),
178 }
179 }
180
181 #[inline]
183 pub fn run_now(mut self) {
184 if let ScopeState::Armed { value, action } =
185 mem::replace(&mut self.state, ScopeState::Disarmed)
186 {
187 action(value);
188 }
189 }
190
191 fn value(&self) -> &T {
192 match &self.state {
193 ScopeState::Armed { value, .. } => value,
194 ScopeState::Disarmed => unreachable!("a live scope guard is armed"),
195 }
196 }
197
198 fn value_mut(&mut self) -> &mut T {
199 match &mut self.state {
200 ScopeState::Armed { value, .. } => value,
201 ScopeState::Disarmed => unreachable!("a live scope guard is armed"),
202 }
203 }
204}
205
206impl<T, F, S> Deref for ScopeGuard<T, F, S>
207where
208 F: FnOnce(T),
209 S: Strategy,
210{
211 type Target = T;
212
213 #[inline]
214 fn deref(&self) -> &Self::Target {
215 self.value()
216 }
217}
218
219impl<T, F, S> DerefMut for ScopeGuard<T, F, S>
220where
221 F: FnOnce(T),
222 S: Strategy,
223{
224 #[inline]
225 fn deref_mut(&mut self) -> &mut Self::Target {
226 self.value_mut()
227 }
228}
229
230impl<T, F, S> Drop for ScopeGuard<T, F, S>
231where
232 F: FnOnce(T),
233 S: Strategy,
234{
235 #[inline]
236 fn drop(&mut self) {
237 let ScopeState::Armed { value, action } =
238 mem::replace(&mut self.state, ScopeState::Disarmed)
239 else {
240 return;
241 };
242
243 if S::should_run() {
244 action(value);
245 }
246 }
247}
248
249impl<T, F, S> fmt::Debug for ScopeGuard<T, F, S>
250where
251 T: fmt::Debug,
252 F: FnOnce(T),
253 S: Strategy,
254{
255 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
256 formatter
257 .debug_struct("ScopeGuard")
258 .field("value", self.value())
259 .finish()
260 }
261}
262
263#[inline]
265pub const fn defer<F: FnOnce()>(action: F) -> DeferGuard<F> {
266 DeferGuard::new(action)
267}
268
269#[cfg(feature = "std")]
271#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
272#[inline]
273pub const fn defer_on_success<F: FnOnce()>(action: F) -> DeferGuard<F, OnSuccess> {
274 DeferGuard::with_strategy(action)
275}
276
277#[cfg(feature = "std")]
279#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
280#[inline]
281pub const fn defer_on_unwind<F: FnOnce()>(action: F) -> DeferGuard<F, OnUnwind> {
282 DeferGuard::with_strategy(action)
283}
284
285#[inline]
287pub const fn guard<T, F>(value: T, action: F) -> ScopeGuard<T, F>
288where
289 F: FnOnce(T),
290{
291 ScopeGuard::with_strategy(value, action)
292}
293
294#[cfg(feature = "std")]
296#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
297#[inline]
298pub const fn guard_on_success<T, F>(value: T, action: F) -> ScopeGuard<T, F, OnSuccess>
299where
300 F: FnOnce(T),
301{
302 ScopeGuard::with_strategy(value, action)
303}
304
305#[cfg(feature = "std")]
307#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
308#[inline]
309pub const fn guard_on_unwind<T, F>(value: T, action: F) -> ScopeGuard<T, F, OnUnwind>
310where
311 F: FnOnce(T),
312{
313 ScopeGuard::with_strategy(value, action)
314}
315
316#[cfg(feature = "std")]
320#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
321#[macro_export]
322macro_rules! defer_on_success {
323 (move $($body:tt)*) => {
324 let _defer_on_success_guard = $crate::defer_on_success(move || { $($body)* });
325 };
326 ($($body:tt)*) => {
327 let _defer_on_success_guard = $crate::defer_on_success(|| { $($body)* });
328 };
329}
330
331#[cfg(feature = "std")]
336#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
337#[macro_export]
338macro_rules! defer_on_unwind {
339 (move $($body:tt)*) => {
340 let _defer_on_unwind_guard = $crate::defer_on_unwind(move || { $($body)* });
341 };
342 ($($body:tt)*) => {
343 let _defer_on_unwind_guard = $crate::defer_on_unwind(|| { $($body)* });
344 };
345}