use proc_macro::TokenStream;
use quote::quote;
use syn::{ExprCall, Item, ItemFn, ItemMod};
pub fn add_fn_before_each(test_mod: &mut ItemMod, arg_fn: &ExprCall) {
generic_each_adder(test_mod, arg_fn, &add_fn_before);
}
pub fn add_fn_after_each(test_mod: &mut ItemMod, arg_fn: &ExprCall) {
add_struct_dropper_to_after_each(test_mod);
add_impl_dropper_to_after_each(test_mod);
generic_each_adder(test_mod, arg_fn, &add_after_fn_dropper_already_defined);
}
fn generic_each_adder(
test_mod: &mut ItemMod,
arg_fn: &ExprCall,
adder: &dyn Fn(&ItemFn, &ExprCall) -> TokenStream,
) {
let mut new_item: Vec<Item>;
match &test_mod.content {
None => panic!("no test inside the module"),
Some((_, original_items)) => {
new_item = original_items.clone();
for (item, i) in original_items.iter().zip(0..original_items.len()) {
if let Item::Fn(current_item_fn) = item {
for attr in current_item_fn.attrs.clone() {
let attribut_string = format!("{:?}", attr.clone().tokens.to_string());
if attribut_string == String::from("\"\"") {
let token_fn_modified = adder(¤t_item_fn, arg_fn);
let syn_item: ItemFn = syn::parse(token_fn_modified).unwrap();
new_item[i] = Item::Fn(syn_item);
break;
}
}
}
}
}
}
let new_content = (test_mod.content.clone().unwrap().0, new_item);
test_mod.content = Some(new_content);
}
pub fn add_fn_before(function: &ItemFn, args: &ExprCall) -> TokenStream {
let ItemFn {
attrs,
vis,
sig,
block,
} = function;
let stmts = &block.stmts;
TokenStream::from(quote! {
#(#attrs)* #vis #sig {
#args;
#(#stmts)*
}
})
}
pub fn add_fn_after(function: &ItemFn, args: &ExprCall) -> TokenStream {
let ItemFn {
attrs,
vis,
sig,
block,
} = function;
let stmts = &block.stmts;
TokenStream::from(quote! {
#(#attrs)* #vis #sig {
struct ScopeCall<F: FnMut()> {
c: F
}
impl<F: FnMut()> Drop for ScopeCall<F> {
fn drop(&mut self) {
(self.c)();
}
}
let _scope_call = ScopeCall { c: || -> () { #args; } };
#(#stmts)*
}
})
}
fn add_struct_dropper_to_after_each(test_mod: &mut ItemMod) {
let token_struct_dropper = quote! {
struct ScopeCall<F: FnMut()> {
c: F
}
};
generic_adder_element_to_after_each(test_mod, token_struct_dropper);
}
fn add_impl_dropper_to_after_each(test_mod: &mut ItemMod) {
let token_struct_dropper = quote! {
impl<F: FnMut()> Drop for ScopeCall<F> {
fn drop(&mut self) {
(self.c)();
}
}
};
generic_adder_element_to_after_each(test_mod, token_struct_dropper);
}
fn generic_adder_element_to_after_each(
test_mod: &mut ItemMod,
token: quote::__private::TokenStream,
) {
let mut items_with_with_struct: Vec<Item>;
match &test_mod.content {
None => panic!("no test inside the module"),
Some((_, original_items)) => {
items_with_with_struct = original_items.clone();
let syn_item: Item = syn::parse2(token).unwrap();
items_with_with_struct.insert(0, syn_item);
}
}
let new_content = (test_mod.content.clone().unwrap().0, items_with_with_struct);
test_mod.content = Some(new_content);
}
fn add_after_fn_dropper_already_defined(function: &ItemFn, args: &ExprCall) -> TokenStream {
let ItemFn {
attrs,
vis,
sig,
block,
} = function;
let stmts = &block.stmts;
TokenStream::from(quote! {
#(#attrs)* #vis #sig {
let _scope_call = ScopeCall { c: || -> () { #args; } };
#(#stmts)*
}
})
}