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 76
extern crate proc_macro; extern crate proc_macro2; use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(ModuleConfig)] pub fn derive_module_config(input: TokenStream) -> TokenStream { let dinput = parse_macro_input!(input as DeriveInput); impl_module_config(dinput) } fn impl_module_config(dinput: DeriveInput) -> proc_macro::TokenStream { let struct_ident = &dinput.ident; let (_impl_generics, ty_generics, where_clause) = dinput.generics.split_for_impl(); let mut from_config = quote! {}; let mut load_config = quote! {}; if let syn::Data::Struct(data) = dinput.data { if let syn::Fields::Named(fields_named) = data.fields { let mut load_tokens = quote! {}; let mut from_tokens = quote! {}; for field in fields_named.named.iter() { let ident = field.ident.as_ref().unwrap(); let ty = &field.ty; let new_load_tokens = quote! { if let Some(config_str) = config.get(stringify!(#ident)) { new_module_config.#ident = new_module_config.#ident.load_config(config_str); } }; let new_from_tokens = quote! { #ident: <#ty>::from_config(config.get(stringify!(#ident))?)?, }; load_tokens = quote! { #load_tokens #new_load_tokens }; from_tokens = quote! { #from_tokens #new_from_tokens } } load_config = quote! { fn load_config(&self, config: &'a toml::Value) -> Self { let mut new_module_config = self.clone(); if let toml::Value::Table(config) = config { #load_tokens } new_module_config } }; from_config = quote! { fn from_config(config: &'a toml::Value) -> Option<Self> { let config = config.as_table()?; Some(#struct_ident { #from_tokens }) } }; } } TokenStream::from(quote! { impl<'a> ModuleConfig<'a> for #struct_ident #ty_generics #where_clause { #from_config #load_config } }) }