preftool_derive_core/
lib.rs

1#![recursion_limit = "4096"]
2
3extern crate darling;
4extern crate proc_macro2;
5extern crate syn;
6#[macro_use]
7extern crate quote;
8
9use darling::util::Override;
10use darling::{FromDeriveInput, FromField, FromMeta, FromVariant};
11use proc_macro2::TokenStream;
12use quote::ToTokens;
13use syn::spanned::Spanned;
14
15#[derive(Debug)]
16enum Validation {
17  None,
18  DefaultTrait,
19  Custom(syn::Path),
20}
21
22impl Default for Validation {
23  fn default() -> Self {
24    Validation::None
25  }
26}
27
28impl ToTokens for Validation {
29  fn to_tokens(&self, tokens: &mut TokenStream) {
30    let validation_impl = match self {
31      Validation::None => quote! { ::std::result::Result::Ok(()) },
32      Validation::DefaultTrait => quote! { <::preftool::ValidateOptions>::validate(self) },
33      Validation::Custom(p) => quote_spanned! { p.span()=> #p(self) },
34    };
35
36    let fn_impl = quote! {
37      fn validate(&self) -> ::preftool::ValidationResult<()> {
38        #validation_impl
39      }
40    };
41
42    fn_impl.to_tokens(tokens)
43  }
44}
45
46impl FromMeta for Validation {
47  fn from_meta(item: &syn::Meta) -> darling::Result<Self> {
48    let val = <Option<Override<syn::Path>> as FromMeta>::from_meta(item)?;
49    Ok(match val {
50      None => Validation::None,
51      Some(Override::Inherit) => Validation::DefaultTrait,
52      Some(Override::Explicit(path)) => Validation::Custom(path),
53    })
54  }
55}
56
57#[derive(Debug, FromField)]
58#[darling(attributes(preftool), forward_attrs)]
59struct FieldOptions {
60  ident: Option<syn::Ident>,
61  ty: syn::Type,
62
63  #[darling(default)]
64  flatten: darling::util::Flag,
65
66  #[darling(default)]
67  name: Option<String>,
68
69  #[darling(default)]
70  from: Option<syn::Path>,
71
72  #[darling(forward_attrs)]
73  attrs: Vec<syn::Attribute>,
74}
75
76impl ToTokens for FieldOptions {
77  fn to_tokens(&self, tokens: &mut TokenStream) {
78    let ident = self.ident.as_ref().unwrap();
79    let ty = &self.ty;
80    let section = if self.flatten.is_some() {
81      quote! { config }
82    } else {
83      let name = self.name.clone().unwrap_or_else(|| ident.to_string());
84      quote! { &config.section(#name) }
85    };
86
87    let bind_result = match &self.from {
88      None => quote! { ::preftool::Options::bind(&mut self.#ident, #section) },
89      Some(p) => {
90        quote! { <#p as ::preftool::ConfigProxy<#ty>>::bind_proxy(&mut self.#ident, #section) }
91      }
92    };
93
94    let out = quote! {
95      if let ::std::result::Result::Err(error) = #bind_result {
96        ::preftool::ValidationError::append_to(error, &mut __errors);
97      }
98    };
99
100    out.to_tokens(tokens);
101  }
102}
103
104#[derive(Debug, FromVariant)]
105#[darling(attributes(preftool))]
106struct VariantOptions {
107  ident: syn::Ident,
108  fields: darling::ast::Fields<FieldOptions>,
109}
110
111#[derive(Debug, FromDeriveInput)]
112#[darling(attributes(preftool))]
113struct DeriveOptions {
114  ident: syn::Ident,
115  generics: syn::Generics,
116  #[darling(default, rename = "validate")]
117  validation: Validation,
118  data: darling::ast::Data<VariantOptions, FieldOptions>,
119}
120
121impl ToTokens for DeriveOptions {
122  fn to_tokens(&self, tokens: &mut TokenStream) {
123    let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
124    let DeriveOptions {
125      ident,
126      validation,
127      data,
128      ..
129    } = self;
130
131    let field_impls = match data {
132      darling::ast::Data::Enum(_variants) => unimplemented!(),
133      darling::ast::Data::Struct(fields) => {
134        let fields = &fields.fields;
135        quote! { #(#fields)* }
136      }
137    };
138
139    let out = quote! {
140      impl #impl_generics ::preftool::Options for #ident #ty_generics #where_clause {
141        fn bind_config<C: ::preftool::Configuration>(&mut self, config: &C) -> ::preftool::ValidationResult<()> {
142          let mut __errors: ::std::vec::Vec<::preftool::ValidationError> = ::std::vec::Vec::new();
143          #(#field_impls)*
144          if !__errors.is_empty() {
145            return Err(__errors.into())
146          }
147
148          Ok(())
149        }
150
151        #validation
152      }
153    };
154
155    out.to_tokens(tokens)
156  }
157}
158
159pub fn derive_options(input: &syn::DeriveInput) -> TokenStream {
160  match DeriveOptions::from_derive_input(input) {
161    Ok(val) => val.into_token_stream(),
162    Err(err) => err.write_errors(),
163  }
164}