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}