aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/entry.rs
blob: 6237e41ecd36e06bbb32f3f60728bccb8e84c15f (plain) (blame)
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
use proc_macro::TokenStream;
use quote::quote;
use syn::{Ident, LitStr, parse_macro_input};

enum EntryInput {
    Typed { ident: Ident, strings: Vec<String> },
    Untyped { strings: Vec<String> },
}

impl syn::parse::Parse for EntryInput {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        // entry!(EntryType, ["a", "b", "c"])
        // entry!["a", "b", "c"]  — comes in as just ["a", "b", "c"]
        if input.peek(Ident) && input.peek2(syn::Token![,]) {
            // entry!(EntryType, ["a", "b", "c"])
            let ident: Ident = input.parse()?;
            let _comma: syn::Token![,] = input.parse()?;
            let content;
            syn::bracketed!(content in input);
            let strings = parse_strings(&content)?;
            Ok(EntryInput::Typed { ident, strings })
        } else {
            // entry!["a", "b", "c"]  — bare bracket content
            let strings = parse_strings(input)?;
            Ok(EntryInput::Untyped { strings })
        }
    }
}

fn parse_strings(input: &syn::parse::ParseBuffer) -> syn::Result<Vec<String>> {
    let mut strings = Vec::new();
    while !input.is_empty() {
        let s: LitStr = input.parse()?;
        strings.push(s.value());
        if input.peek(syn::Token![,]) {
            let _comma: syn::Token![,] = input.parse()?;
        }
    }
    Ok(strings)
}

pub fn entry(input: TokenStream) -> TokenStream {
    let parsed = parse_macro_input!(input as EntryInput);

    let string_exprs = match &parsed {
        EntryInput::Typed { .. } | EntryInput::Untyped { .. } => {
            let strings = match &parsed {
                EntryInput::Typed { strings, .. } => strings,
                EntryInput::Untyped { strings } => strings,
            };
            strings
                .iter()
                .map(|s| {
                    let lit = syn::LitStr::new(s, proc_macro2::Span::call_site());
                    quote! { #lit.to_string() }
                })
                .collect::<Vec<_>>()
        }
    };

    let expanded = match parsed {
        EntryInput::Typed { ident, .. } => {
            quote! {
                #ident::new(vec![#(#string_exprs),*])
            }
        }
        EntryInput::Untyped { .. } => {
            quote! {
                vec![#(#string_exprs),*].into()
            }
        }
    };

    expanded.into()
}