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
use proc_macro::TokenStream;
use quote::quote;

struct FlagSets {
    d: bool,
    i: bool,
    g: bool,
    s: bool,
    m: bool,
    y: bool,
    u: bool,
    v: bool,
}
fn new_empty_flagsets() -> FlagSets {
    FlagSets {
        d: false,
        i: false,
        g: false,
        s: false,
        m: false,
        y: false,
        u: false,
        v: false,
    }
}
fn check_flags(source: &str) -> Result<(), char> {
    let mut flags = new_empty_flagsets();
    let mut inner = |ch: char| -> Option<()> {
        match ch {
            'd' => (!flags.d).then(|| flags.d = true)?,
            'i' => (!flags.i).then(|| flags.i = true)?,
            'g' => (!flags.g).then(|| flags.g = true)?,
            's' => (!flags.s).then(|| flags.s = true)?,
            'm' => (!flags.m).then(|| flags.m = true)?,
            'y' => (!flags.y).then(|| flags.y = true)?,
            'u' => (!flags.u && !flags.v).then(|| flags.u = true)?,
            'v' => (!flags.u && !flags.v).then(|| flags.v = true)?,
            _ => return None,
        };
        Some(())
    };
    for ch in source.chars() {
        if inner(ch).is_none() {
            return Err(ch);
        }
    }
    Ok(())
}

/// Checks validity of a flags string literal at compile time and inserts a therefore
/// safe runtime call to [`Flags::new_unchecked`](struct.Flags.html#method.new_unchecked).
#[proc_macro]
pub fn flags(item: TokenStream) -> TokenStream {
    let item = syn::parse_macro_input!(item as syn::Lit);
    let item = match item {
        syn::Lit::Str(v) => v,
        _ => panic!("Not a string literal"),
    };
    let value = item.value();
    if let Err(e) = check_flags(&value) {
        panic!("Invalid flags at char: '{}'", e);
    }
    quote!(::js_regexp::Flags::new_unchecked(#item)).into()
}