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 | // 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 | |
85 | 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 | |
99 | 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 | |