defer_rs_impl/lib.rs
1use syn::{
2 parse::{Parse, ParseStream},
3 Stmt,
4};
5
6// This will be no longer needed when either the `index` macro meta variable expression (#122808) or `std::ops::Fn::call` method land in stable,
7#[doc(hidden)]
8#[proc_macro]
9pub fn call_indexed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10 let input = syn::parse_macro_input!(input as syn::ExprCall);
11
12 let func = input.func;
13 let args = input.args.iter();
14 let i = (0..args.len()).map(syn::Index::from);
15 quote::quote! {
16 {
17 #func(#(___deferred_code_captured_args.#i, )*);
18 }
19 }
20 .into()
21}
22
23struct DeferStmtExpr {
24 move_kw: Option<syn::token::Move>,
25 deferred: Vec<Stmt>,
26}
27
28impl Parse for DeferStmtExpr {
29 fn parse(input: ParseStream) -> syn::Result<Self> {
30 Ok(Self {
31 move_kw: input.parse()?,
32 deferred: input.call(syn::Block::parse_within)?,
33 })
34 }
35}
36
37
38/// A macro for deferring execution of code until the closest scope containing a previously invoked [`defer_scope_init!`] macro ends.
39///
40/// Use `defer_scope!` when you want to defer execution not to the end of the current active scope, but to the end of a larger parent scope.
41/// The specific parent scope is determined by invoking `defer_scope_init!`.
42///
43/// **Important Notes**:
44/// - The [`defer_scope_init!`] macro **must** be invoked before using `defer_scope!`, and both macros must share a scope.
45/// - You can invoke the `defer_scope!` macro multiple times for a given `defer_scope_init!` invocation.
46///
47/// # Examples
48///
49/// ## Basic usage:
50///
51/// ```rust
52/// use defer_rs::{defer_scope, defer_scope_init};
53///
54/// defer_scope_init!();
55/// defer_scope! {
56/// println!("This will be executed when `defer_scope_init!()`'s scope exits.");
57/// }
58/// ```
59/// ### Expands to:
60/// ```rust
61/// let mut ___deferred_code_group = ::defer_rs::DeferGroup::new();
62/// ___deferred_code_group.add(Box::new(( || {
63/// println!("This will be executed when `defer_scope_init!()`'s scope exits.");
64/// })));
65/// ```
66///
67/// Ignoring the ability to specify the scope and the need for invoking `defer_scope_init!` beforehand,
68/// `defer_scope!` is otherwise identical to [`defer!`](https://docs.rs/defer_rs/latest/defer_rs/macro.defer.html).
69///
70/// For more usage examples, refer to the documentation for the [`defer!`](https://docs.rs/defer_rs/latest/defer_rs/macro.defer.html) macro,
71/// simply replace `defer!` with `defer_scope!` and add an invocation of [`defer_scope_init!`] beforehand.
72///
73/// See also: [`DeferGroup`](https://docs.rs/defer_rs/latest/defer_rs/struct.DeferGroup.html), [`defer_scope_init!`], and [`defer!`](https://docs.rs/defer_rs/latest/defer_rs/macro.defer.html).
74// THIS DOC COMMENT MUST BE KEPT IN SYNC WITH THE DOC COMMENT ON THE FAKE `cfg(doc)` `defer_scope!` DECLARTIVE MACRO IN THE PARENT `defer_rs` CRATE!
75#[doc(hidden)]
76// A proc_macro is used instead of `macro_rules` to bypass identifier hygiene
77#[proc_macro]
78pub fn defer_scope(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
79 let ast: syn::Result<syn::ExprCall> = syn::parse(input.clone());
80 if let Ok(call) = ast {
81 let func = call.func;
82 let args = call.args.iter();
83 let i = (0..args.len()).map(syn::Index::from);
84 quote::quote! {
85
86 let ___deferred_code_captured_args = ( #( #args, )* );
87 {
88 ___deferred_code_group.add(::std::boxed::Box::new( move || {
89 #func(#(___deferred_code_captured_args.#i, )*);
90 }));
91 }
92 }
93 .into()
94 } else {
95 let DeferStmtExpr { move_kw, deferred } = syn::parse(input).unwrap();
96 quote::quote! {
97 {
98 ___deferred_code_group.add(::std::boxed::Box::new(#move_kw || {
99 #(#deferred)*;
100 }));
101 }
102 }
103 .into()
104 }
105}
106
107
108/// Initializes a [DeferGroup], which is an empty collection of closures to run at the end of the scope containing the invocation.
109/// It provides no functionality by itself and should be called before any [defer_scope!] invocation(s).
110///
111/// No arguments should be passed to the macro invocation.
112///
113/// # Usage
114///
115/// ```rust
116/// defer_rs::defer_scope_init!();
117/// ```
118/// ## Expands to:
119/// ```rust
120/// let mut ___deferred_code_group = ::defer_rs::DeferGroup::new();
121/// ```
122///
123/// For more detailed examples, refer to the documentation for [defer_scope!].
124///
125/// See also: [`DeferGroup`](https://docs.rs/defer_rs/latest/defer_rs/struct.DeferGroup.html), [`defer_scope!`], and [`defer!`](https://docs.rs/defer_rs/latest/defer_rs/macro.defer.html).
126// THIS DOC COMMENT MUST BE KEPT IN SYNC WITH THE DOC COMMENT ON THE FAKE `cfg(doc)` `defer_scope_init!` DECLARTIVE MACRO IN THE PARENT `defer_rs` CRATE!
127#[doc(hidden)]
128// This is used to bypass `macro_rules` identifier hygiene
129#[proc_macro]
130pub fn defer_scope_init(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
131 if !input.is_empty() {
132 return quote::quote! {compile_error!("deferfn_init! doesn't take any arguments")}.into();
133 }
134 "let mut ___deferred_code_group = ::defer_rs::DeferGroup::new();"
135 .parse()
136 .unwrap()
137}