core_crypto_macros/
idempotent.rs1use crate::{compile_error, doc_attributes, items};
2use proc_macro::TokenStream;
3
4const ASYNC_ERROR_MSG: &str = "This requires access to the keystore, it has to be async";
5
6pub(crate) fn idempotent(item: TokenStream) -> TokenStream {
7 let ast = match syn::parse2::<syn::ItemFn>(item.clone().into()) {
8 Ok(ast) => ast,
9 Err(e) => return compile_error(item, e),
10 };
11 if ast.sig.asyncness.is_none() {
12 return compile_error(item, syn::Error::new_spanned(ast, ASYNC_ERROR_MSG));
13 }
14
15 let doc_attributes = doc_attributes(&ast);
16 let (ret, name, inputs, body, attrs, vis) = items(&ast);
17
18 let result: proc_macro2::TokenStream = quote::quote! {
19 #(#doc_attributes)*
20 #(#attrs)*
21 #vis async fn #name(#inputs) #ret {
22 let prev_count = self.count_entities().await;
23
24 let _result = #body;
25
26 let next_count = self.count_entities().await;
27 assert_eq!(prev_count, next_count, "'{}()' leaks entities", stringify!(#name));
28
29 _result
30 }
31 };
32 result.into()
33}
34
35pub(crate) fn dispotent(item: TokenStream) -> TokenStream {
36 let ast = match syn::parse2::<syn::ItemFn>(item.clone().into()) {
37 Ok(ast) => ast,
38 Err(e) => return compile_error(item, e),
39 };
40 if ast.sig.asyncness.is_none() {
41 return compile_error(item, syn::Error::new_spanned(ast, ASYNC_ERROR_MSG));
42 }
43
44 let doc_attributes = doc_attributes(&ast);
45 let (ret, name, inputs, body, attrs, vis) = items(&ast);
46
47 let result: proc_macro2::TokenStream = quote::quote! {
48 #(#doc_attributes)*
49 #(#attrs)*
50 #vis async fn #name(#inputs) #ret {
51 let prev_count = self.count_entities().await;
52
53 let _result = #body;
54
55 let next_count = self.count_entities().await;
56 assert_ne!(prev_count, next_count, "'{}()' does not create entities", stringify!(#name));
57
58 _result
59 }
60 };
61 result.into()
62}