• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use proc_macro2::TokenStream;
2 use quote::quote;
3 
4 use crate::ast::{Fields, Style};
5 use crate::codegen::Field;
6 
7 pub struct FieldsGen<'a> {
8     fields: &'a Fields<Field<'a>>,
9     allow_unknown_fields: bool,
10 }
11 
12 impl<'a> FieldsGen<'a> {
new(fields: &'a Fields<Field<'a>>, allow_unknown_fields: bool) -> Self13     pub fn new(fields: &'a Fields<Field<'a>>, allow_unknown_fields: bool) -> Self {
14         Self {
15             fields,
16             allow_unknown_fields,
17         }
18     }
19 
20     /// Create declarations for all the fields in the struct.
declarations(&self) -> TokenStream21     pub(in crate::codegen) fn declarations(&self) -> TokenStream {
22         match *self.fields {
23             Fields {
24                 style: Style::Struct,
25                 ref fields,
26                 ..
27             } => {
28                 let vdr = fields.iter().map(Field::as_declaration);
29                 quote!(#(#vdr)*)
30             }
31             _ => panic!("FieldsGen doesn't support tuples yet"),
32         }
33     }
34 
35     /// Generate the loop which walks meta items looking for property matches.
core_loop(&self) -> TokenStream36     pub(in crate::codegen) fn core_loop(&self) -> TokenStream {
37         let arms = self.fields.as_ref().map(Field::as_match);
38         // If there is a flatten field, buffer the unknown field so it can be passed
39         // to the flatten function with all other unknown fields.
40         let handle_unknown = if self.fields.iter().any(|f| f.flatten) {
41             quote! {
42                 __flatten.push(::darling::ast::NestedMeta::Meta(__inner.clone()));
43             }
44         }
45         // If we're allowing unknown fields, then handling one is a no-op.
46         else if self.allow_unknown_fields {
47             quote!()
48         }
49         // Otherwise, we're going to push a new spanned error pointing at the field.
50         else {
51             let mut names = self.fields.iter().filter_map(Field::as_name).peekable();
52             // We can't call `unknown_field_with_alts` with an empty slice, or else it fails to
53             // infer the type of the slice item.
54             let err_fn = if names.peek().is_none() {
55                 quote!(unknown_field(__other))
56             } else {
57                 quote!(unknown_field_with_alts(__other, &[#(#names),*]))
58             };
59 
60             quote! {
61                 __errors.push(::darling::Error::#err_fn.with_span(__inner));
62             }
63         };
64         let arms = arms.iter();
65 
66         quote!(
67             for __item in __items {
68                 match *__item {
69                     ::darling::export::NestedMeta::Meta(ref __inner) => {
70                         let __name = ::darling::util::path_to_string(__inner.path());
71                         match __name.as_str() {
72                             #(#arms)*
73                             __other => { #handle_unknown }
74                         }
75                     }
76                     ::darling::export::NestedMeta::Lit(ref __inner) => {
77                         __errors.push(::darling::Error::unsupported_format("literal")
78                             .with_span(__inner));
79                     }
80                 }
81             }
82         )
83     }
84 
require_fields(&self) -> TokenStream85     pub fn require_fields(&self) -> TokenStream {
86         match *self.fields {
87             Fields {
88                 style: Style::Struct,
89                 ref fields,
90                 ..
91             } => {
92                 let checks = fields.iter().map(Field::as_presence_check);
93                 quote!(#(#checks)*)
94             }
95             _ => panic!("FieldsGen doesn't support tuples for requirement checks"),
96         }
97     }
98 
initializers(&self) -> TokenStream99     pub(in crate::codegen) fn initializers(&self) -> TokenStream {
100         let inits = self.fields.as_ref().map(Field::as_initializer);
101         let inits = inits.iter();
102 
103         quote!(#(#inits),*)
104     }
105 }
106