proc_macro_hack_impl/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3
4struct Hack {
5    attrs: String,
6    name: String,
7    name_impl: String,
8}
9
10#[proc_macro_derive(ProcMacroHackExpr)]
11pub fn hack_expr(input: TokenStream) -> TokenStream {
12    let hack = parse(input);
13
14    let rules = format!("
15        {attrs}
16        #[macro_export]
17        macro_rules! {name} {{
18            ($($tt:tt)*) => {{{{
19                #[derive({name_impl})]
20                #[allow(unused)]
21                enum ProcMacroHack {{
22                    Input = (stringify!($($tt)*), 0).1
23                }}
24
25                proc_macro_call!()
26            }}}}
27        }}
28    ", attrs=hack.attrs, name=hack.name, name_impl=hack.name_impl);
29
30    rules.parse().unwrap()
31}
32
33#[proc_macro_derive(ProcMacroHackItem)]
34pub fn hack_item(input: TokenStream) -> TokenStream {
35    let hack = parse(input);
36
37    let rules = format!("
38        {attrs}
39        #[macro_export]
40        macro_rules! {name} {{
41            ($($tt:tt)*) => {{
42                #[derive({name_impl})]
43                #[allow(unused)]
44                enum ProcMacroHack {{
45                    Input = (stringify!($($tt)*), 0).1
46                }}
47            }}
48        }}
49    ", attrs=hack.attrs, name=hack.name, name_impl=hack.name_impl);
50
51    rules.parse().unwrap()
52}
53
54/// Parses an input that looks like:
55///
56/// ```rust,ignore
57/// #[allow(unused, non_camel_case_types)]
58/// $( #[$attr] )*
59/// enum NAME {
60///     NAME_IMPL
61/// }
62/// ```
63fn parse(input: TokenStream) -> Hack {
64    let source = input.to_string();
65
66    let mut front = source.split_whitespace();
67    let next = front.next().unwrap();
68    if next == "#[allow(unused" {
69        assert_eq!(Some(","), front.next());
70    } else {
71        assert_eq!("#[allow(unused,", next);
72    }
73    assert_eq!(Some("non_camel_case_types)]"), front.next());
74
75    let mut back = source.split_whitespace().rev();
76    assert_eq!(Some("}"), back.next());
77    let name_impl = back.next().unwrap();
78    assert_eq!(Some("{"), back.next());
79    let name = back.next().unwrap();
80    assert_eq!(Some("enum"), back.next());
81
82    let start_attrs = source.find(']').unwrap() + 1;
83    let end = source.rfind('}').unwrap();
84    let end = source[..end].rfind(name_impl).unwrap();
85    let end = source[..end].rfind('{').unwrap();
86    let end = source[..end].rfind(name).unwrap();
87    let end = source[..end].rfind("enum").unwrap();
88
89    Hack {
90        attrs: source[start_attrs..end].trim().to_owned(),
91        name: name.to_owned(),
92        name_impl: name_impl.to_owned(),
93    }
94}