#![allow(unused, dead_code)]
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use syn::{
meta::ParseNestedMeta, parse_macro_input, Attribute, Data, DeriveInput, Ident, Lit, Meta, Type, LitStr, punctuated::Punctuated, Token
};
#[proc_macro_derive(ConfigFields, attributes(keys))]
pub fn config_field_macro(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_config_field_macro(&ast)
}
struct VariantDefinition {
ident: Ident,
keys: Vec<String>
}
impl VariantDefinition {
fn new(ident: Ident) -> Self {
Self {ident, keys: vec![]}
}
fn add_key(&mut self, key: String) {
self.keys.push(key);
}
}
fn impl_config_field_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let user_enum_name = &ast.ident;
let mut variant_defs: Vec<VariantDefinition> = vec![];
match &ast.data {
Data::Enum(data_enum) => {
for variant in data_enum.variants.iter() {
let mut this_variant = VariantDefinition::new(variant.ident.clone());
for attr in &variant.attrs {
match attr.path().get_ident() {
Some(ident) if ident.eq("keys") => {
let keys: Punctuated<LitStr, Token![,]> = attr
.parse_args_with(Punctuated::parse_terminated)
.expect("Failed to parse keys attribute");
for key in keys {
this_variant.add_key(key.value());
}
},
_ => {
todo!()
}
};
};
variant_defs.push(this_variant);
}
},
Data::Struct(_data_struct) => {
unimplemented!("ConfigFields can only be derived on Enums")
},
Data::Union(_data_union) => {
unimplemented!("ConfigFields can only be derived on Enums")
},
};
let mut match_arms: Vec<quote::__private::TokenStream> = Vec::new();
for variant in variant_defs.into_iter() {
let var_name = variant.ident;
let var_keys = variant.keys;
let field_keys: Vec<String> = match var_keys.is_empty() {
true => {
vec![var_name.to_string()]
},
false => {
var_keys
}
};
let key_actions: Vec<quote::__private::TokenStream> = field_keys.iter()
.map(|key| {
quote! {
if !self.has_key(#key) {
} else {
if let Some(field) = self.create_field(#key) {
return_fields.push(field);
}
}
}
})
.collect();
let match_arm = quote! {
#user_enum_name::#var_name => {
let mut return_fields = vec![];
#(#key_actions)*
if return_fields.is_empty() {
return None;
} else {
return Some(return_fields);
}
}
};
match_arms.push(match_arm);
}
let trait_name = format!("QuickfigConfigTrait{}", name);
let trait_ident = syn::Ident::new(&trait_name, name.span());
let impl_gen = quote! {
impl ::quickfig::core::ConfigFields for #name {}
trait #trait_ident<S>
where
S: ::quickfig::serde::de::DeserializeOwned + ::quickfig::core::config_types::DeserializedConfig,
{
type CF: ::quickfig::core::ConfigFields;
fn get<'a>(&'a self, user_enum: Self::CF) ->
std::option::Option<std::vec::Vec<::quickfig::core::Field<'a, S>>>;
}
impl #trait_ident<::quickfig::core::config_types::JSON> for ::quickfig::core::Config<::quickfig::core::config_types::JSON> {
type CF = #name;
fn get<'a>(&'a self, user_enum: Self::CF) -> std::option::Option<std::vec::Vec<::quickfig::core::Field<'a, ::quickfig::core::config_types::JSON>>> {
match user_enum {
#(#match_arms)*,
}
}
}
impl #trait_ident<::quickfig::core::config_types::TOML> for ::quickfig::core::Config<::quickfig::core::config_types::TOML> {
type CF = #name;
fn get<'a>(&'a self, user_enum: Self::CF) -> std::option::Option<std::vec::Vec<::quickfig::core::Field<'a, ::quickfig::core::config_types::TOML>>> {
match user_enum {
#(#match_arms)*,
}
}
}
};
impl_gen.into()
}