1use proc_macro2::TokenStream;
2use quote::quote;
3
4use crate::ast::{Fields, Style};
5use crate::codegen::Field;
6
7pub struct FieldsGen<'a> {
8 fields: &'a Fields<Field<'a>>,
9 allow_unknown_fields: bool,
10}
11
12impl<'a> FieldsGen<'a> {
13 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.
21 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.
36 pub(in crate::codegen) fn core_loop(&self) -> TokenStream {
37 let arms = self.fields.as_ref().map(Field::as_match);
38
39 // If we're allowing unknown fields, then handling one is a no-op.
40 // Otherwise, we're going to push a new spanned error pointing at the field.
41 let handle_unknown = if self.allow_unknown_fields {
42 quote!()
43 } else {
44 // We can't call `unknown_field_with_alts` with an empty slice, or else it fails to
45 // infer the type of the slice item.
46 let err_fn = if arms.is_empty() {
47 quote!(unknown_field(__other))
48 } else {
49 let names = self.fields.as_ref().map(Field::as_name);
50 let names = names.iter();
51 quote!(unknown_field_with_alts(__other, &[#(#names),*]))
52 };
53
54 quote! {
55 __errors.push(::darling::Error::#err_fn.with_span(__inner));
56 }
57 };
58 let arms = arms.iter();
59
60 quote!(
61 for __item in __items {
62 match *__item {
63 ::darling::export::NestedMeta::Meta(ref __inner) => {
64 let __name = ::darling::util::path_to_string(__inner.path());
65 match __name.as_str() {
66 #(#arms)*
67 __other => { #handle_unknown }
68 }
69 }
70 ::darling::export::NestedMeta::Lit(ref __inner) => {
71 __errors.push(::darling::Error::unsupported_format("literal")
72 .with_span(__inner));
73 }
74 }
75 }
76 )
77 }
78
79 pub fn require_fields(&self) -> TokenStream {
80 match *self.fields {
81 Fields {
82 style: Style::Struct,
83 ref fields,
84 ..
85 } => {
86 let checks = fields.iter().map(Field::as_presence_check);
87 quote!(#(#checks)*)
88 }
89 _ => panic!("FieldsGen doesn't support tuples for requirement checks"),
90 }
91 }
92
93 pub(in crate::codegen) fn initializers(&self) -> TokenStream {
94 let inits = self.fields.as_ref().map(Field::as_initializer);
95 let inits = inits.iter();
96
97 quote!(#(#inits),*)
98 }
99}
100