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> { |
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 | |