static_map_macros/
lib.rs

1extern crate fxhash;
2extern crate proc_macro;
3#[macro_use]
4extern crate quote;
5extern crate syn;
6
7use proc_macro::TokenStream;
8
9mod builder;
10use builder::Builder;
11
12type Key<'a> = syn::Lit;
13type Value<'a> = &'a str;
14
15const LEADING: &str = "enum __StaticMap__ {\n    A =\n        static_map!(@ zero";
16const TRAILING: &str = "),\n}";
17
18fn trim(input: &str) -> &str {
19    assert!(input.starts_with(LEADING));
20    assert!(input.ends_with(TRAILING));
21
22    let (_, input) = input.split_at(LEADING.len());
23    let (input, _) = input.split_at(input.len() - TRAILING.len());
24    input.trim()
25}
26
27#[proc_macro_derive(StaticMapMacro)]
28pub fn static_map_macro(input: TokenStream) -> TokenStream {
29    let s = input.to_string();
30    let result = build_static_map(trim(&s));
31
32    let wrapper = quote! {
33        macro_rules! __static_map__construct_map {
34            () => ( #result )
35        }
36    };
37
38    wrapper.parse().unwrap()
39}
40
41fn build_static_map(input: &str) -> quote::Tokens {
42    // TODO: Splitting by @ and ? like this is not hygenic.
43    //  This could fail if a value is "This @ is a ? value";
44    let mut tt = input.split('@');
45
46    // Extract the defualt value, which should be first token listed.
47    // `.split()` always returns at least an empty string for the first iteration.
48    let default_value = tt.next().unwrap();
49    
50    // "@ Key ? Value" pairs follow
51    let count = input.chars().filter(|&c| c == '@').count();
52    let mut builder = Builder::with_capacity(count);
53
54    // Determine the default key from the first key-value piar
55    let mut pair = tt
56        .next()
57        .expect("staticmap! requires at least one key/value pair")
58        .split('?');
59
60    let default_key = syn::parse::lit(pair.next().unwrap()).expect("failed to parse key type");
61    let value = pair.next().unwrap();
62    builder.insert(default_key.clone(), value);
63
64    for pair in tt {
65        let mut pair = pair.split('?');
66        
67        // Each pair is gauranteed to have a `?`.  So unwrapping is safe.
68        let key = syn::parse::lit(pair.next().unwrap()).expect("failed to parse key type");
69        let value = pair.next().unwrap();
70        builder.insert(key, value);
71    }
72
73    builder.build(lit_default(&default_key), default_value)
74}
75
76fn lit_default(lit: &syn::Lit) -> syn::Lit {
77    use syn::Lit::*;
78    use syn::Lit;
79
80    match *lit {
81        Str(_, _) => Lit::from(""),
82        Byte(_) => Lit::from(0u8),
83        Char(_) => Lit::from(0 as char),
84        Int(_, ty) => {
85            use syn::IntTy::*;
86            use syn::IntTy;
87            match ty {
88                Isize => Lit::from(0isize),
89                I8 => Lit::from(0i8),
90                I16 => Lit::from(0i16),
91                I32 => Lit::from(0i32),
92                I64 => Lit::from(0i64),
93                Usize => Lit::from(0usize),
94                U8 => Lit::from(0u8),
95                U16 => Lit::from(0u16),
96                U32 => Lit::from(0u32),
97                U64 => Lit::from(0u64),
98                Unsuffixed => Lit::Int(0, IntTy::Unsuffixed),
99            }
100        },
101        ref lit => panic!("staticmap! unsupported key type `{:?}`", lit),
102    }
103}