1use help::generate_help;
2use proc_macro::TokenStream;
3use quote::{quote, ToTokens};
4use syn::{parse_macro_input, ImplItem, ImplItemFn, ItemImpl};
5
6use crate::function::{generate_command, Command};
7
8mod doc;
9mod function;
10mod help;
11
12#[proc_macro_attribute]
13pub fn command(_attr: TokenStream, item: TokenStream) -> TokenStream {
14 let mut tree = parse_macro_input!(item as ItemImpl);
15 let commands = tree
16 .items
17 .iter()
18 .filter_map(|ex| match ex {
19 ImplItem::Fn(func) => Some(func),
20 _ => None,
21 })
22 .map(generate_command)
23 .collect::<Result<Vec<Command>, proc_macro2::TokenStream>>();
24
25 if let Err(err) = commands {
26 return err.into();
27 }
28
29 let commands = commands.unwrap();
30
31 let main_generator = commands.iter().map(|Command { name, tokens, .. }| {
32 quote!(
33 #name => {
34 #tokens
35 }
36 )
37 });
38
39 let argster_main = quote!(
40 fn __argster_main() -> Result<(), (Option<&'static str>, ::argster::from_args::Error)> {
41 let mut iter = ::std::env::args().skip(1);
42 let command = iter.next().ok_or_else(|| (None, ::argster::from_args::Error::NoCommand))?;
43 let args = ::argster::parse_args(iter);
44
45 if (args.get("--help").is_some() || args.get("-h").is_some()) {
46 Self::__argster_help(Some(command), None);
47 return Ok(());
48 }
49
50 match command.as_str() {
51 #(#main_generator),*,
52 "help" | "--help" | "-h" => Self::__argster_help(::argster::from_args::FromArgsItem::from_args_item(args.get("")).map_err(|x| (Some("help"), x.with_name("input")))?, None),
53 _ => panic!("Unknown command {}", command),
54 };
55 Ok(())
56 }
57 )
58 .into();
59
60 let main = quote!(
61 fn main() {
62 match Self::__argster_main() {
63 Ok(_) => (),
64 Err((name, ex)) => Self::__argster_help(name.map(|f| f.to_string()), Some(ex)),
65 }
66 }
67 )
68 .into();
69
70 let help = generate_help(&commands);
71
72 tree.items
74 .push(ImplItem::Fn(parse_macro_input!(argster_main as ImplItemFn)));
75
76 tree.items
77 .push(ImplItem::Fn(parse_macro_input!(main as ImplItemFn)));
78
79 tree.items
80 .push(ImplItem::Fn(parse_macro_input!(help as ImplItemFn)));
81
82 tree.to_token_stream().into()
83}