rtest-derive 0.1.3

heper crate with derive macros for rtest
Documentation
use proc_macro::TokenStream;
use quote::quote;
use std::sync::{Arc, Mutex, OnceLock};

struct TestInfo {
    fn_name: String,
    input: String,
}

static FUNCTION_NAMES: OnceLock<Arc<Mutex<Vec<TestInfo>>>> = OnceLock::new();

#[proc_macro_derive(FromContext)]
pub fn from_context(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
    let name = &ast.ident;
    let name_str = name.to_string();

    let code = quote!(
        impl rtest::FromContext for #name {
            type Context = rtest::Context;

            fn from_context(context: &Self::Context) -> Option<rtest::Resource<Self>> {
                context.extract::<#name>()
            }

            fn into_context(context: &Self::Context, resource: rtest::Resource<#name>) {
                context.inject(resource)
            }

            fn get_resource_id() -> rtest::ResourceId {
                #name_str.to_string()
            }
        }
    );
    code.into()
}

#[proc_macro_attribute]
pub fn rtest(_attr: TokenStream, orig_input: TokenStream) -> TokenStream {
    {
        let mutex = FUNCTION_NAMES.get_or_init(|| Arc::new(Mutex::new(Vec::new())));
        let mut list = mutex.lock().unwrap();
        let input = orig_input.clone();
        let ast = syn::parse_macro_input!(input as syn::ItemFn);
        list.push(TestInfo {
            fn_name: ast.sig.ident.to_string(),
            input: orig_input.to_string(),
        })
    }
    orig_input
}

#[proc_macro]
/// input must be the Context, Context must be clonable
pub fn run(input: TokenStream) -> TokenStream {
    use std::str::FromStr;
    let list: Vec<_> = {
        let mutex = FUNCTION_NAMES.get_or_init(|| Arc::new(Mutex::new(Vec::new())));
        mutex.lock().unwrap().drain(..).collect()
    };

    let input_struct = syn::parse_macro_input!(input as syn::Path);

    let mut commands = quote!();
    for l in list.into_iter() {
        let name = l.fn_name.to_string();
        let input = TokenStream::from_str(&l.input).unwrap();
        let ast = syn::parse_macro_input!(input as syn::ItemFn);
        let fn_ident = ast.sig.ident;
        let f = quote! {
            let c = #input_struct.clone();
            let (reference, inputs, outputs) = rtest::describe_handler(& #fn_ident);
            graph_config.add(#name, Box::new(move || {
                rtest::call_handler(c.clone(), &mut #fn_ident)
            }), reference, inputs, outputs, Vec::new());
        };
        commands = quote!(
            #commands
            #f
        );
    }

    let code = quote!(
        {
            use rtest::{Runner, ExecutionStrategy, SimplePrinter, Printer};
            use clap::Parser;
            let args = rtest::Args::parse();

            let mut graph_config = rtest::StrategyConfig::default();
            #commands
            let graph = rtest::SimpleStrategy::with_config(graph_config);
            let data = Runner::run(graph);
            SimplePrinter::print(data);
        }
    );

    code.into()
}