use proc_macro::TokenStream;
use proc_macro_crate::{crate_name, FoundCrate};
use quote::quote;
use syn::{parse_macro_input, ItemFn};
#[proc_macro_attribute]
pub fn pubky_testcase(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input_fn = parse_macro_input!(item as ItemFn);
let fn_block = &input_fn.block;
let fn_vis = &input_fn.vis;
let fn_attrs = &input_fn.attrs;
let fn_sig = &input_fn.sig;
fn get_crate_name() -> FoundCrate {
let lib_names = [
"pubky_test_utils",
"pubky-testnet",
"pubky_test_utils_macro",
];
for lib_name in lib_names.iter() {
match crate_name(lib_name) {
Ok(found) => return found,
Err(_e) => {
continue;
}
};
}
panic!(
"Failed to get crate name. Tested crates: {}",
lib_names.join(", ").as_str()
);
}
let found = get_crate_name();
let my_crate = match found {
FoundCrate::Itself => quote!(crate),
FoundCrate::Name(name) => {
let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
quote!(::#ident)
}
};
let is_async = input_fn.sig.asyncness.is_some();
let expanded = if is_async {
quote! {
#(#fn_attrs)*
#fn_vis #fn_sig {
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
if let Ok(rt) = tokio::runtime::Handle::try_current() {
rt.block_on(#my_crate::drop_test_databases());
} else {
if let Ok(rt) = tokio::runtime::Runtime::new() {
rt.block_on(#my_crate::drop_test_databases());
}
}
original_hook(panic_info);
}));
#fn_block
std::panic::set_hook(original_hook);
#my_crate::drop_test_databases().await;
}
}
} else {
quote! {
#(#fn_attrs)*
#fn_vis #fn_sig {
let result = std::panic::catch_unwind(|| {
#fn_block
});
if let Ok(rt) = tokio::runtime::Handle::try_current() {
rt.block_on(#my_crate::drop_test_databases());
} else {
let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
rt.block_on(#my_crate::drop_test_databases());
}
if let Err(panic) = result {
std::panic::resume_unwind(panic);
}
}
}
};
TokenStream::from(expanded)
}
#[cfg(test)]
mod tests {
#[test]
fn macro_compiles() {
assert!(true);
}
}