#![recursion_limit = "4096"]
extern crate darling;
extern crate proc_macro2;
extern crate syn;
#[macro_use]
extern crate quote;
use darling::util::Override;
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant};
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::spanned::Spanned;
#[derive(Debug)]
enum Validation {
None,
DefaultTrait,
Custom(syn::Path),
}
impl Default for Validation {
fn default() -> Self {
Validation::None
}
}
impl ToTokens for Validation {
fn to_tokens(&self, tokens: &mut TokenStream) {
let validation_impl = match self {
Validation::None => quote! { ::std::result::Result::Ok(()) },
Validation::DefaultTrait => quote! { <::preftool::ValidateOptions>::validate(self) },
Validation::Custom(p) => quote_spanned! { p.span()=> #p(self) },
};
let fn_impl = quote! {
fn validate(&self) -> ::preftool::ValidationResult<()> {
#validation_impl
}
};
fn_impl.to_tokens(tokens)
}
}
impl FromMeta for Validation {
fn from_meta(item: &syn::Meta) -> darling::Result<Self> {
let val = <Option<Override<syn::Path>> as FromMeta>::from_meta(item)?;
Ok(match val {
None => Validation::None,
Some(Override::Inherit) => Validation::DefaultTrait,
Some(Override::Explicit(path)) => Validation::Custom(path),
})
}
}
#[derive(Debug, FromField)]
#[darling(attributes(preftool), forward_attrs)]
struct FieldOptions {
ident: Option<syn::Ident>,
ty: syn::Type,
#[darling(default)]
flatten: darling::util::Flag,
#[darling(default)]
name: Option<String>,
#[darling(default)]
from: Option<syn::Path>,
#[darling(forward_attrs)]
attrs: Vec<syn::Attribute>,
}
impl ToTokens for FieldOptions {
fn to_tokens(&self, tokens: &mut TokenStream) {
let ident = self.ident.as_ref().unwrap();
let ty = &self.ty;
let section = if self.flatten.is_some() {
quote! { config }
} else {
let name = self.name.clone().unwrap_or_else(|| ident.to_string());
quote! { &config.section(#name) }
};
let bind_result = match &self.from {
None => quote! { ::preftool::Options::bind(&mut self.#ident, #section) },
Some(p) => {
quote! { <#p as ::preftool::ConfigProxy<#ty>>::bind_proxy(&mut self.#ident, #section) }
}
};
let out = quote! {
if let ::std::result::Result::Err(error) = #bind_result {
::preftool::ValidationError::append_to(error, &mut __errors);
}
};
out.to_tokens(tokens);
}
}
#[derive(Debug, FromVariant)]
#[darling(attributes(preftool))]
struct VariantOptions {
ident: syn::Ident,
fields: darling::ast::Fields<FieldOptions>,
}
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(preftool))]
struct DeriveOptions {
ident: syn::Ident,
generics: syn::Generics,
#[darling(default, rename = "validate")]
validation: Validation,
data: darling::ast::Data<VariantOptions, FieldOptions>,
}
impl ToTokens for DeriveOptions {
fn to_tokens(&self, tokens: &mut TokenStream) {
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let DeriveOptions {
ident,
validation,
data,
..
} = self;
let field_impls = match data {
darling::ast::Data::Enum(_variants) => unimplemented!(),
darling::ast::Data::Struct(fields) => {
let fields = &fields.fields;
quote! { #(#fields)* }
}
};
let out = quote! {
impl #impl_generics ::preftool::Options for #ident #ty_generics #where_clause {
fn bind_config<C: ::preftool::Configuration>(&mut self, config: &C) -> ::preftool::ValidationResult<()> {
let mut __errors: ::std::vec::Vec<::preftool::ValidationError> = ::std::vec::Vec::new();
#(#field_impls)*
if !__errors.is_empty() {
return Err(__errors.into())
}
Ok(())
}
#validation
}
};
out.to_tokens(tokens)
}
}
pub fn derive_options(input: &syn::DeriveInput) -> TokenStream {
match DeriveOptions::from_derive_input(input) {
Ok(val) => val.into_token_stream(),
Err(err) => err.write_errors(),
}
}