use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{Ident, ItemFn};
#[proc_macro_attribute]
pub fn transaction(attr: TokenStream, item: TokenStream) -> TokenStream {
transaction_internal(attr, item).into()
}
fn transaction_internal(_attr: TokenStream, item: TokenStream) -> TokenStream2 {
let input = syn::parse::<ItemFn>(item).unwrap();
let ItemFn {
attrs,
vis,
sig,
block,
} = input;
let stmts = &block.stmts;
let ident = &sig.ident;
quote! {
#(#attrs)* #vis #sig {
::balter::transaction::transaction_hook(::balter::core::generate_labels!(#ident), async move {
#(#stmts)*
}).await
}
}
}
#[proc_macro_attribute]
pub fn scenario(attr: TokenStream, item: TokenStream) -> TokenStream {
scenario_internal(attr, item, false).into()
}
#[proc_macro_attribute]
pub fn scenario_linkme(attr: TokenStream, item: TokenStream) -> TokenStream {
scenario_internal(attr, item, true).into()
}
fn scenario_internal(_attr: TokenStream, item: TokenStream, linkme: bool) -> TokenStream2 {
let input = syn::parse::<ItemFn>(item).expect("Macro only works on fn() items");
let ItemFn {
attrs,
vis,
sig,
block,
} = input;
let stmts = &block.stmts;
let new_name = Ident::new(&format!("__balter_{}", sig.ident), Span::call_site());
let mut new_sig = sig.clone();
new_sig.ident = new_name.clone();
let mut scen_sig = sig.clone();
let scen_name = sig.ident.clone();
scen_sig.asyncness = None;
scen_sig.output = syn::parse(
quote! {
-> impl ::balter::scenario::ConfigurableScenario<::balter::prelude::RunStatistics>
}
.into(),
)
.expect("Scenario signature is invalid");
let res = quote! {
#(#attrs)* #vis #scen_sig {
::balter::scenario::Scenario::new(stringify!(#scen_name), #new_name)
}
#(#attrs)* #vis #new_sig {
#(#stmts)*
}
};
if linkme {
let mut linkme_sig = sig.clone();
let linkme_name = Ident::new(&format!("__balter_distr_{}", sig.ident), Span::call_site());
linkme_sig.ident = linkme_name.clone();
linkme_sig.asyncness = None;
linkme_sig.output = syn::parse(
quote! {
-> ::core::pin::Pin<Box<dyn ::balter::prelude::DistributedScenario<Output=::balter::prelude::RunStatistics>>>
}
.into(),
)
.expect("Scenario signature is invalid");
let static_name = Ident::new(
&format!("__BALTER_{}", sig.ident.to_string().to_ascii_uppercase()),
Span::call_site(),
);
let mut linkme = quote! {
#[::balter::runtime::distributed_slice(::balter::runtime::BALTER_SCENARIOS)]
static #static_name: (&'static str, fn() -> ::core::pin::Pin<Box<dyn ::balter::prelude::DistributedScenario<Output=::balter::prelude::RunStatistics>>>) = (stringify!(#scen_name), #linkme_name);
#(#attrs)* #vis #linkme_sig {
Box::pin(::balter::scenario::Scenario::new(stringify!(#scen_name), #new_name))
}
};
linkme.extend(res);
linkme
} else {
res
}
}