2019-09-30 12:10:35 +00:00
|
|
|
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! {};
|
2021-04-06 20:12:37 +00:00
|
|
|
let mut fields = quote! {};
|
2019-09-30 12:10:35 +00:00
|
|
|
|
|
|
|
for field in fields_named.named.iter() {
|
|
|
|
let ident = field.ident.as_ref().unwrap();
|
|
|
|
|
|
|
|
let new_load_tokens = quote! {
|
2021-03-31 18:13:23 +00:00
|
|
|
stringify!(#ident) => self.#ident.load_config(v),
|
2019-09-30 12:10:35 +00:00
|
|
|
};
|
|
|
|
|
2021-04-06 20:12:37 +00:00
|
|
|
let new_field = quote! {
|
|
|
|
stringify!(#ident),
|
|
|
|
};
|
|
|
|
|
2019-09-30 12:10:35 +00:00
|
|
|
load_tokens = quote! {
|
|
|
|
#load_tokens
|
|
|
|
#new_load_tokens
|
|
|
|
};
|
2021-04-06 20:12:37 +00:00
|
|
|
|
|
|
|
fields = quote! {
|
|
|
|
#fields
|
|
|
|
#new_field
|
|
|
|
};
|
2019-09-30 12:10:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
load_config = quote! {
|
2021-03-31 18:13:23 +00:00
|
|
|
fn load_config(&mut self, config: &'a toml::Value) {
|
2019-09-30 12:10:35 +00:00
|
|
|
if let toml::Value::Table(config) = config {
|
2021-03-31 18:13:23 +00:00
|
|
|
config.iter().for_each(|(k, v)| {
|
|
|
|
match k.as_str() {
|
|
|
|
#load_tokens
|
|
|
|
unknown => {
|
|
|
|
::log::warn!("Unknown config key '{}'", unknown);
|
2021-04-06 20:12:37 +00:00
|
|
|
|
|
|
|
let did_you_mean = ::std::array::IntoIter::new([#fields])
|
|
|
|
.filter_map(|field| {
|
|
|
|
let score = ::strsim::jaro_winkler(unknown, field);
|
|
|
|
(score > 0.8).then(|| (score, field))
|
|
|
|
})
|
|
|
|
.max_by(
|
|
|
|
|(score_a, _field_a), (score_b, _field_b)| {
|
|
|
|
score_a.partial_cmp(score_b).unwrap_or(::std::cmp::Ordering::Equal)
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Some((_score, field)) = did_you_mean {
|
|
|
|
::log::warn!("Did you mean '{}'?", field);
|
|
|
|
}
|
2021-03-31 18:13:23 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
});
|
2019-09-30 12:10:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
from_config = quote! {
|
|
|
|
fn from_config(config: &'a toml::Value) -> Option<Self> {
|
2021-03-31 18:13:23 +00:00
|
|
|
let mut out = Self::default();
|
|
|
|
out.load_config(config);
|
|
|
|
Some(out)
|
2019-09-30 12:10:35 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TokenStream::from(quote! {
|
|
|
|
impl<'a> ModuleConfig<'a> for #struct_ident #ty_generics #where_clause {
|
|
|
|
#from_config
|
|
|
|
#load_config
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|