1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{FnArg, ItemFn, parse_macro_input};
4
5#[proc_macro_attribute]
9pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
10 let ast = parse_macro_input!(item as ItemFn);
11 let sig = &ast.sig;
12 let FnArg::Typed(arg) = sig.inputs.first().expect("no arguments found") else {
13 panic!("first argument must be a clap::Parser");
14 };
15 let arg_ty = &arg.ty;
16 let out = &sig.output;
17 let block = &ast.block;
18 let fn_name = &sig.ident;
19
20 let inner_name = syn::Ident::new(&format!("{}_inner", fn_name), fn_name.span());
21
22 let output = quote! {
23 #[tokio::main]
24 async fn #fn_name() {
25 use ::clapper::prelude::*;
26
27 let args = #arg_ty::parse();
28
29 let terminated = ::std::sync::Arc::new(::std::sync::atomic::AtomicBool::new(false));
30
31 ::clapper::ctrlc::set_handler({
32 let terminated = terminated.clone();
33 move || {
34 terminated.store(true, ::std::sync::atomic::Ordering::Relaxed);
35 }
36 }).expect("failed to set sigterm handler");
37
38 if let Err(err) = #inner_name(args, terminated).await {
39 eprintln!("Error: {err}");
40 ::std::process::exit(err.exit_code());
41 }
42 }
43
44 async fn #inner_name(args: #arg_ty, terminated: ::std::sync::Arc<::std::sync::atomic::AtomicBool>) #out #block
45 };
46
47 output.into()
48}