use proc_macro::TokenStream;
use quote::quote;
use std::sync::OnceLock;
use std::sync::{Arc, Mutex};
use syn;
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.clone().to_string(),
})
}
orig_input
}
#[proc_macro]
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::Graph;
use rtest::Runner;
use clap::Parser;
let args = rtest::Args::parse();
let mut graph_config = rtest::GraphConfig::default();
#commands
let graph = rtest::SimpleExecutionModel::with_config(graph_config);
Runner::run(graph).print();
}
);
return code.into();
}