config_macros/
config_macros.rs1use 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 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}