argster_macros/
lib.rs

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    // println!("{}", help);
73    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}