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
extern crate proc_macro;
extern crate proc_macro2;
use self::proc_macro::TokenStream;
use self::proc_macro2::TokenStream as TokenStream2;
use syn::{parse_macro_input, ItemImpl};
use quote::quote;
use syn::*;
#[proc_macro_attribute]
pub fn cmdr(_meta: TokenStream, code: TokenStream) -> TokenStream {
let input = parse_macro_input!(code as ItemImpl);
let command_matches = format_command_match(&get_methods(&input));
if let Type::Path(self_type) = &*input.self_ty {
let output = TokenStream::from(quote!(
#input
impl cmdr::Scope for #self_type {
fn command(&mut self, line: Line) -> CommandResult {
match line {
Line::Empty => self.empty(),
#(#command_matches),*,
_ => self.default(line)
}
}
}
));
output
}
else {
panic!("Unable to parse impl type")
}
}
fn format_command_match(methods: &Vec<(Ident, String)>) -> Vec<TokenStream2> {
let mut result: Vec<TokenStream2> = Vec::new();
for (method, name) in methods {
result.push(quote!(Line::Command(#name, args) => self.#method(args)));
}
result
}
fn get_methods(input: &ItemImpl) -> Vec<(Ident, String)> {
let mut result: Vec<(Ident, String)> = Vec::new();
for item in &input.items {
if let ImplItem::Method(method) = item {
let ident = &method.sig.ident;
let name = ident.to_string();
if name.starts_with("do_") {
result.push((ident.clone(), name[3..].to_owned()))
}
}
}
result
}