referrerpolicy=no-referrer-when-downgrade

sp_core/
defer.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Contains the [`crate::defer!`] macro for *deferring* the execution
19//! of code until the current scope is dropped.
20//! This helps with *always* executing cleanup code.
21
22/// Executes the wrapped closure on drop.
23///
24/// Should be used together with the [`crate::defer!`] macro.
25#[must_use]
26pub struct DeferGuard<F: FnOnce()>(pub Option<F>);
27
28impl<F: FnOnce()> DeferGuard<F> {
29	/// Creates a new `DeferGuard` with the given closure.
30	pub fn new(f: F) -> Self {
31		Self(Some(f))
32	}
33}
34
35impl<F: FnOnce()> Drop for DeferGuard<F> {
36	fn drop(&mut self) {
37		self.0.take().map(|f| f());
38	}
39}
40
41/// Executes the given code when the current scope is dropped.
42///
43/// Multiple calls to [`crate::defer!`] will execute the passed codes in reverse order.
44/// This also applies to panic stack unwinding.
45///
46/// # Example
47///
48/// ```rust
49/// use sp_core::defer;
50///
51/// let message = std::cell::RefCell::new("".to_string());
52/// {
53/// 	defer!(
54/// 		message.borrow_mut().push_str("world!");
55/// 	);
56/// 	defer!(
57/// 		message.borrow_mut().push_str("Hello ");
58/// 	);
59/// }
60/// assert_eq!(*message.borrow(), "Hello world!");
61/// ```
62#[macro_export]
63macro_rules! defer(
64	( $( $code:tt )* ) => {
65		let _guard = $crate::defer::DeferGuard(Some(|| { $( $code )* }));
66	};
67);
68
69#[cfg(test)]
70mod test {
71	#[test]
72	fn defer_guard_works() {
73		let mut called = false;
74		{
75			defer!(
76				called = true;
77			);
78		}
79		assert!(called, "DeferGuard should have executed the closure");
80	}
81
82	#[test]
83	/// `defer` executes the code in reverse order of being called.
84	fn defer_guard_order_works() {
85		let called = std::cell::RefCell::new(1);
86
87		defer!(
88			assert_eq!(*called.borrow(), 3);
89		);
90		defer!(
91			assert_eq!(*called.borrow(), 2);
92			*called.borrow_mut() = 3;
93		);
94		defer!({
95			assert_eq!(*called.borrow(), 1);
96			*called.borrow_mut() = 2;
97		});
98	}
99
100	#[test]
101	#[allow(unused_braces)]
102	#[allow(clippy::unnecessary_operation)]
103	fn defer_guard_syntax_works() {
104		let called = std::cell::RefCell::new(0);
105		{
106			defer!(*called.borrow_mut() += 1);
107			defer!(*called.borrow_mut() += 1;); // With ;
108			defer!({ *called.borrow_mut() += 1 });
109			defer!({ *called.borrow_mut() += 1 };); // With ;
110		}
111		assert_eq!(*called.borrow(), 4);
112	}
113
114	#[test]
115	/// `defer` executes the code even in case of a panic.
116	fn defer_guard_panic_unwind_works() {
117		use std::panic::{catch_unwind, AssertUnwindSafe};
118		let mut called = false;
119
120		let should_panic = catch_unwind(AssertUnwindSafe(|| {
121			defer!(called = true);
122			panic!();
123		}));
124
125		assert!(should_panic.is_err(), "DeferGuard should have panicked");
126		assert!(called, "DeferGuard should have executed the closure");
127	}
128
129	#[test]
130	/// `defer` executes the code even in case another `defer` panics.
131	fn defer_guard_defer_panics_unwind_works() {
132		use std::panic::{catch_unwind, AssertUnwindSafe};
133		let counter = std::cell::RefCell::new(0);
134
135		let should_panic = catch_unwind(AssertUnwindSafe(|| {
136			defer!(*counter.borrow_mut() += 1);
137			defer!(
138				*counter.borrow_mut() += 1;
139				panic!();
140			);
141			defer!(*counter.borrow_mut() += 1);
142		}));
143
144		assert!(should_panic.is_err(), "DeferGuard should have panicked");
145		assert_eq!(*counter.borrow(), 3, "DeferGuard should have executed the closure");
146	}
147}