config_macros/
config_macros.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::spanned::Spanned;
4
5#[proc_macro_derive(ConfParsable)]
6pub fn derive_conf_tree(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7  let ast = syn::parse_macro_input!(input as syn::DeriveInput);
8
9  let toks = conf_tree(&ast).unwrap_or_else(|err| err.to_compile_error());
10  // println!("{}", &toks);
11  toks.into()
12}
13
14fn conf_tree(ast: &syn::DeriveInput) -> syn::Result<TokenStream> {
15  let name = &ast.ident;
16  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
17
18  let fields = if let syn::Data::Struct(ref data) = ast.data {
19    &data.fields
20  } else {
21    return Err(syn::Error::new(ast.span(), "Expected struct"));
22  };
23
24  let field_names = fields
25    .iter()
26    .map(|f| {
27      f.ident
28        .as_ref()
29        .ok_or_else(|| syn::Error::new(f.span(), "Expected field name"))
30    })
31    .collect::<syn::Result<Vec<_>>>()?;
32
33  let field_strs = field_names
34    .iter()
35    .map(|ident| ident.to_string())
36    .collect::<Vec<_>>();
37
38  Ok(quote! {
39    impl #impl_generics crate::config::ConfTree for #name #ty_generics #where_clause {
40
41      fn get_child(&self, name: &str) -> Result<&dyn crate::config::ConfOpt, String> {
42        match name {
43          #(#field_strs => Ok(&self.#field_names),)*
44          _ => Err(format!("unknown option {}", name)),
45        }
46      }
47
48      fn get_child_mut(&mut self, name: &str) -> Result<&mut dyn crate::config::ConfOpt, String> {
49        match name {
50          #(#field_strs => Ok(&mut self.#field_names),)*
51          _ => Err(format!("unknown option {}", name)),
52        }
53      }
54    }
55  })
56}