1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use std::ops::Deref;
use syn::export::TokenStream2;
use syn::{FnArg, Ident, Item, Pat};
#[proc_macro_attribute]
pub fn fire(_head: TokenStream, body: TokenStream) -> TokenStream {
match syn::parse::<Item>(body).unwrap() {
Item::Fn(func) => {
let ident = &func.sig.ident;
let inputs = &func.sig.inputs;
let args = inputs
.iter()
.map(|fnc| match fnc {
FnArg::Typed(pt) => match pt.pat.deref() {
Pat::Ident(pat_ident) => &pat_ident.ident,
_ => panic!("complex pattern is not supported!"),
},
_ => panic!("associated function is not supported!"),
})
.collect::<Vec<_>>();
let app_fn = gen_app(ident, &args);
let match_fn = gen_match(ident, &args);
let fire_ident = format_ident!("{}_fire", ident);
let stdin_ident = format_ident!("{}_stdin", ident);
let func_ident = format_ident!("{}_app", ident);
let gen = quote! {
use fire_rs::{App, Arg};
use std::ffi::OsString;
#func
#app_fn
#match_fn
fn #fire_ident() {
let app = #func_ident();
#stdin_ident(app);
}
};
gen.into()
}
_ => panic!("gg"),
}
}
fn gen_app(ident: &Ident, args: &[&Ident]) -> TokenStream2 {
let func_ident = format_ident!("{}_app", ident);
quote! {
fn #func_ident<'a, 'b>() -> App<'a, 'b> {
let mut app = App::new("demo")
.arg(Arg::with_name("args")
.takes_value(true)
.multiple(true));
#(
let args = Arg::with_name(stringify!(#args))
.takes_value(true)
.long(stringify!(#args));
app = app.arg(args);
)*
app
}
}
}
fn gen_match(ident: &Ident, args: &[&Ident]) -> TokenStream2 {
let common = quote! {
let mut ifs = false;
#(
ifs = ifs || matches.is_present(stringify!(#args));
)*
if ifs {
#ident(#(matches.value_of(stringify!(#args)).unwrap().parse().unwrap()),*);
}
else {
let mut v = matches.values_of("args").unwrap_or_default();
#ident(#(
{
let #args = 0;
v.next().unwrap().parse().unwrap()
}
),*);
}
};
let stdin_ident = format_ident!("{}_stdin", ident);
let slice_ident = format_ident!("{}_slice", ident);
quote! {
fn #stdin_ident(app: App) {
let matches = app.get_matches();
#common
}
fn #slice_ident<I, T>(app: App, itr: I) where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone
{
let matches = app.get_matches_from(itr);
#common
}
}
}