Skip to main content

thisconfig_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, Error, Expr, Lit, Meta, parse_macro_input};
4
5#[proc_macro_attribute]
6pub fn config(args: TokenStream, input: TokenStream) -> TokenStream {
7    let input = parse_macro_input!(input as DeriveInput);
8
9    match config_impl(args, &input) {
10        Ok(tokens) => tokens,
11        Err(err) => err.to_compile_error().into(),
12    }
13}
14
15fn config_impl(args: TokenStream, input: &DeriveInput) -> syn::Result<TokenStream> {
16    let name = &input.ident;
17    let meta = syn::parse::<Meta>(args)?;
18
19    let nv = meta.require_name_value().map_err(|_| {
20        Error::new_spanned(&meta, r#"expected format: #[config(key = "section_name")]"#)
21    })?;
22
23    if !nv.path.is_ident("key") {
24        return Err(Error::new_spanned(&nv.path, "expected `key` attribute"));
25    }
26
27    let Expr::Lit(expr_lit) = &nv.value else {
28        return Err(Error::new_spanned(
29            &nv.value,
30            "expected string literal for key",
31        ));
32    };
33
34    let Lit::Str(lit_str) = &expr_lit.lit else {
35        return Err(Error::new_spanned(
36            &expr_lit.lit,
37            "expected string literal for key",
38        ));
39    };
40
41    let expanded = quote! {
42        #input
43
44        impl ::thisconfig::ConfigItem for #name {
45            fn key() -> &'static str {
46                #lit_str
47            }
48        }
49    };
50
51    Ok(expanded.into())
52}