preftool_derive_core/
lib.rs1#![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}