1use quote::quote;
2use syn::{
3 parse_macro_input, parse_quote, AttributeArgs, ItemFn, Lit, Meta, MetaNameValue, NestedMeta,
4 Path,
5};
6
7#[proc_macro_attribute]
8pub fn festive(
9 args: proc_macro::TokenStream,
10 input: proc_macro::TokenStream,
11) -> proc_macro::TokenStream {
12 let args = parse_macro_input!(args as AttributeArgs);
13 let timeout: syn::Expr = syn::parse_str(&format!("{:?}", parse_args(&args))).unwrap();
14
15 let ItemFn {
16 attrs,
17 vis,
18 sig,
19 block,
20 } = parse_macro_input!(input as ItemFn);
21 let fun_name = sig.ident.to_string();
22
23 (quote! {
24 #(
25 #attrs
26 )*
27 #[test]
28 #vis #sig {
29 fn inner() #block
30
31 struct _Anon;
32 let fork_id = ::festive::ForkId::of(::std::any::TypeId::of::<_Anon>());
33
34 let path = format!("{}::{}", ::std::module_path!(), #fun_name);
35
36 let res = ::festive::fork(
37 &path,
38 fork_id,
39 #timeout,
40 inner,
41 ).expect("forking test failed");
42
43 let stringout = format!(
44 "Child stdout:: {}\nChild stderr: {}",
45 String::from_utf8_lossy(&res.stdout),
46 String::from_utf8_lossy(&res.stderr)
47 );
48
49 assert!(res.status.success(), stringout);
50 }
51 })
52 .into()
53}
54
55fn parse_args(args: &[NestedMeta]) -> Option<u64> {
56 let timeout_path: Path = parse_quote!(timeout_ms);
57 for a in args {
58 match a {
59 NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. }))
60 if path == &timeout_path =>
61 {
62 if let Lit::Int(li) = lit {
63 return li.base10_parse().ok();
64 } else {
65 panic!("timeout_ms should have an int value")
66 }
67 }
68 _ => {}
69 }
70 }
71 None
72}