proclock_macro/
lib.rs

1mod macro_args;
2
3use crate::macro_args::MacroArgs;
4use darling::FromMeta;
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::{AttributeArgs, ItemFn};
8
9/// _⚠ Use with caution - this macro panics on error - See `Panics` section below._
10///
11/// Wraps the annotated function with a blocking lock that is released when
12/// the function is returned.
13///
14/// # Args:
15/// - `name`: The name of the lock. Can be any relative / absolute path.
16/// - `absolute`: Indicates whether the provided `name` should be created at the [`temp_dir`](std::env::temp_dir())
17/// or as an absolute path (at the root directory). Default is `false`.
18/// # Example
19/// ```rust
20/// use proclock_macro::proclock;
21///
22/// #[proclock(name = "my_lock.lock", absolute = false)]
23/// fn my_locked_function() {}
24/// ```
25/// # Panics
26/// This macro will panic if the underlying locking function call fails.
27#[proc_macro_attribute]
28pub fn proclock(args: TokenStream, input: TokenStream) -> TokenStream {
29    let options = syn::parse_macro_input!(args as AttributeArgs);
30    let function = syn::parse_macro_input!(input as ItemFn);
31
32    let MacroArgs { name, absolute } = match MacroArgs::from_list(&options) {
33        Ok(v) => v,
34        Err(e) => return TokenStream::from(e.write_errors()),
35    };
36
37    let locking_code = quote! {
38        use proclock_api::{lock, try_lock, LockPath};
39        let lock_path = if #absolute {
40            LockPath::FullPath(#name)
41        } else {
42            LockPath::Tmp(#name)
43        };
44
45        let _guard = lock(&lock_path).unwrap();
46    };
47
48    let ItemFn {
49        attrs,
50        vis,
51        sig,
52        block: body,
53    } = &function;
54
55    let result = quote! {
56        #(#attrs)*
57        #vis #sig {
58            #locking_code
59            #body
60        }
61    };
62
63    let res: proc_macro::TokenStream = result.into();
64    res
65}