darling_core\codegen/
field.rs1use std::borrow::Cow;
2
3use proc_macro2::TokenStream;
4use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
5use syn::{spanned::Spanned, Ident, Path, Type};
6
7use crate::codegen::{DefaultExpression, PostfixTransform};
8use crate::usage::{self, IdentRefSet, IdentSet, UsesTypeParams};
9
10#[derive(Debug, Clone)]
13pub struct Field<'a> {
14 pub name_in_attr: Cow<'a, String>,
17
18 pub ident: &'a Ident,
21
22 pub ty: &'a Type,
24 pub default_expression: Option<DefaultExpression<'a>>,
25 pub with_path: Cow<'a, Path>,
26 pub post_transform: Option<&'a PostfixTransform>,
27 pub skip: bool,
28 pub multiple: bool,
29 pub flatten: bool,
32}
33
34impl<'a> Field<'a> {
35 pub fn as_name(&'a self) -> Option<&'a str> {
40 if self.skip || self.flatten {
41 None
42 } else {
43 Some(&self.name_in_attr)
44 }
45 }
46
47 pub fn as_declaration(&'a self) -> Declaration<'a> {
48 Declaration(self)
49 }
50
51 pub fn as_flatten_initializer(
52 &'a self,
53 parent_field_names: Vec<&'a str>,
54 ) -> FlattenInitializer<'a> {
55 FlattenInitializer {
56 field: self,
57 parent_field_names,
58 }
59 }
60
61 pub fn as_match(&'a self) -> MatchArm<'a> {
62 MatchArm(self)
63 }
64
65 pub fn as_initializer(&'a self) -> Initializer<'a> {
66 Initializer(self)
67 }
68
69 pub fn as_presence_check(&'a self) -> CheckMissing<'a> {
70 CheckMissing(self)
71 }
72}
73
74impl<'a> UsesTypeParams for Field<'a> {
75 fn uses_type_params<'b>(
76 &self,
77 options: &usage::Options,
78 type_set: &'b IdentSet,
79 ) -> IdentRefSet<'b> {
80 self.ty.uses_type_params(options, type_set)
81 }
82}
83
84pub struct Declaration<'a>(&'a Field<'a>);
86
87impl<'a> ToTokens for Declaration<'a> {
88 fn to_tokens(&self, tokens: &mut TokenStream) {
89 let field = self.0;
90 let ident = field.ident;
91 let ty = field.ty;
92
93 tokens.append_all(if field.multiple {
94 quote!(let mut #ident: #ty = ::darling::export::Default::default();)
96 } else {
97 quote!(let mut #ident: (bool, ::darling::export::Option<#ty>) = (false, None);)
98 });
99
100 if field.flatten {
106 tokens.append_all(quote! {
107 let mut __flatten: Vec<::darling::ast::NestedMeta> = vec![];
108 });
109 }
110 }
111}
112
113pub struct FlattenInitializer<'a> {
114 field: &'a Field<'a>,
115 parent_field_names: Vec<&'a str>,
116}
117
118impl<'a> ToTokens for FlattenInitializer<'a> {
119 fn to_tokens(&self, tokens: &mut TokenStream) {
120 let Self {
121 field,
122 parent_field_names,
123 } = self;
124 let ident = field.ident;
125
126 let add_parent_fields = if parent_field_names.is_empty() {
127 None
128 } else {
129 Some(quote! {
130 .map_err(|e| e.add_sibling_alts_for_unknown_field(&[#(#parent_field_names),*]))
131 })
132 };
133
134 tokens.append_all(quote! {
135 #ident = (true,
136 __errors.handle(
137 ::darling::FromMeta::from_list(&__flatten) #add_parent_fields
138 )
139 );
140 });
141 }
142}
143
144pub struct MatchArm<'a>(&'a Field<'a>);
146
147impl<'a> ToTokens for MatchArm<'a> {
148 fn to_tokens(&self, tokens: &mut TokenStream) {
149 let field = self.0;
150
151 if field.skip || field.flatten {
154 return;
155 }
156
157 let name_str = &field.name_in_attr;
158 let ident = field.ident;
159 let with_path = &field.with_path;
160 let post_transform = field.post_transform.as_ref();
161
162 let location = if field.multiple {
166 quote!(&format!("{}[{}]", #name_str, __len))
170 } else {
171 quote!(#name_str)
172 };
173
174 let extractor = quote_spanned!(with_path.span()=>#with_path(__inner)#post_transform.map_err(|e| e.with_span(&__inner).at(#location)));
183
184 tokens.append_all(if field.multiple {
185 quote!(
186 #name_str => {
187 let __len = #ident.len();
190 if let ::darling::export::Some(__val) = __errors.handle(#extractor) {
191 #ident.push(__val)
192 }
193 }
194 )
195 } else {
196 quote!(
197 #name_str => {
198 if !#ident.0 {
199 #ident = (true, __errors.handle(#extractor));
200 } else {
201 __errors.push(::darling::Error::duplicate_field(#name_str).with_span(&__inner));
202 }
203 }
204 )
205 });
206 }
207}
208
209pub struct Initializer<'a>(&'a Field<'a>);
211
212impl<'a> ToTokens for Initializer<'a> {
213 fn to_tokens(&self, tokens: &mut TokenStream) {
214 let field = self.0;
215 let ident = field.ident;
216 tokens.append_all(if field.multiple {
217 if let Some(ref expr) = field.default_expression {
218 quote_spanned!(expr.span()=> #ident: if !#ident.is_empty() {
219 #ident
220 } else {
221 #expr
222 })
223 } else {
224 quote!(#ident: #ident)
225 }
226 } else if let Some(ref expr) = field.default_expression {
227 quote_spanned!(expr.span()=> #ident: if let Some(__val) = #ident.1 {
228 __val
229 } else {
230 #expr
231 })
232 } else {
233 quote!(#ident: #ident.1.expect("Uninitialized fields without defaults were already checked"))
234 });
235 }
236}
237
238pub struct CheckMissing<'a>(&'a Field<'a>);
240
241impl<'a> ToTokens for CheckMissing<'a> {
242 fn to_tokens(&self, tokens: &mut TokenStream) {
243 if !self.0.multiple && self.0.default_expression.is_none() {
244 let ident = self.0.ident;
245 let ty = self.0.ty;
246 let name_in_attr = &self.0.name_in_attr;
247
248 let from_none_call =
251 quote_spanned!(ty.span()=> <#ty as ::darling::FromMeta>::from_none());
252
253 tokens.append_all(quote! {
254 if !#ident.0 {
255 match #from_none_call {
256 ::darling::export::Some(__type_fallback) => {
257 #ident.1 = ::darling::export::Some(__type_fallback);
258 }
259 ::darling::export::None => {
260 __errors.push(::darling::Error::missing_field(#name_in_attr))
261 }
262 }
263 }
264 })
265 }
266 }
267}