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, 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_callable: Cow<'a, syn::Expr>,
29 pub post_transform: Option<&'a PostfixTransform>,
30 pub skip: bool,
31 pub multiple: bool,
32 pub flatten: bool,
35}
36
37impl<'a> Field<'a> {
38 pub fn as_name(&'a self) -> Option<&'a str> {
43 if self.skip || self.flatten {
44 None
45 } else {
46 Some(&self.name_in_attr)
47 }
48 }
49
50 pub fn as_declaration(&'a self) -> Declaration<'a> {
51 Declaration(self)
52 }
53
54 pub fn as_flatten_initializer(
55 &'a self,
56 parent_field_names: Vec<&'a str>,
57 ) -> FlattenInitializer<'a> {
58 FlattenInitializer {
59 field: self,
60 parent_field_names,
61 }
62 }
63
64 pub fn as_match(&'a self) -> MatchArm<'a> {
65 MatchArm(self)
66 }
67
68 pub fn as_initializer(&'a self) -> Initializer<'a> {
69 Initializer(self)
70 }
71
72 pub fn as_presence_check(&'a self) -> CheckMissing<'a> {
73 CheckMissing(self)
74 }
75}
76
77impl UsesTypeParams for Field<'_> {
78 fn uses_type_params<'b>(
79 &self,
80 options: &usage::Options,
81 type_set: &'b IdentSet,
82 ) -> IdentRefSet<'b> {
83 self.ty.uses_type_params(options, type_set)
84 }
85}
86
87pub struct Declaration<'a>(&'a Field<'a>);
89
90impl ToTokens for Declaration<'_> {
91 fn to_tokens(&self, tokens: &mut TokenStream) {
92 let field = self.0;
93 let ident = field.ident;
94 let ty = field.ty;
95
96 tokens.append_all(if field.multiple {
97 quote!(let mut #ident: #ty = ::darling::export::Default::default();)
99 } else {
100 quote!(let mut #ident: (bool, ::darling::export::Option<#ty>) = (false, None);)
101 });
102
103 if field.flatten {
109 tokens.append_all(quote! {
110 let mut __flatten: Vec<::darling::ast::NestedMeta> = vec![];
111 });
112 }
113 }
114}
115
116pub struct FlattenInitializer<'a> {
117 field: &'a Field<'a>,
118 parent_field_names: Vec<&'a str>,
119}
120
121impl ToTokens for FlattenInitializer<'_> {
122 fn to_tokens(&self, tokens: &mut TokenStream) {
123 let Self {
124 field,
125 parent_field_names,
126 } = self;
127 let ident = field.ident;
128
129 let add_parent_fields = if parent_field_names.is_empty() {
130 None
131 } else {
132 Some(quote! {
133 .map_err(|e| e.add_sibling_alts_for_unknown_field(&[#(#parent_field_names),*]))
134 })
135 };
136
137 tokens.append_all(quote! {
138 #ident = (true,
139 __errors.handle(
140 ::darling::FromMeta::from_list(&__flatten) #add_parent_fields
141 )
142 );
143 });
144 }
145}
146
147pub struct MatchArm<'a>(&'a Field<'a>);
149
150impl ToTokens for MatchArm<'_> {
151 fn to_tokens(&self, tokens: &mut TokenStream) {
152 let field = self.0;
153
154 if field.skip || field.flatten {
157 return;
158 }
159
160 let name_str = &field.name_in_attr;
161 let ident = field.ident;
162 let with_callable = &field.with_callable;
163 let post_transform = field.post_transform.as_ref();
164
165 let location = if field.multiple {
169 quote!(&format!("{}[{}]", #name_str, __len))
173 } else {
174 quote!(#name_str)
175 };
176
177 let extractor = quote_spanned!(with_callable.span()=>
186 ::darling::export::identity::<fn(&::syn::Meta) -> ::darling::Result<_>>(#with_callable)(__inner)
187 #post_transform
188 .map_err(|e| e.with_span(&__inner).at(#location))
189 );
190
191 tokens.append_all(if field.multiple {
192 quote!(
193 #name_str => {
194 let __len = #ident.len();
197 if let ::darling::export::Some(__val) = __errors.handle(#extractor) {
198 #ident.push(__val)
199 }
200 }
201 )
202 } else {
203 quote!(
204 #name_str => {
205 if !#ident.0 {
206 #ident = (true, __errors.handle(#extractor));
207 } else {
208 __errors.push(::darling::Error::duplicate_field(#name_str).with_span(&__inner));
209 }
210 }
211 )
212 });
213 }
214}
215
216pub struct Initializer<'a>(&'a Field<'a>);
218
219impl ToTokens for Initializer<'_> {
220 fn to_tokens(&self, tokens: &mut TokenStream) {
221 let field = self.0;
222 let ident = field.ident;
223 tokens.append_all(if field.multiple {
224 if let Some(ref expr) = field.default_expression {
225 quote_spanned!(expr.span()=> #ident: if !#ident.is_empty() {
226 #ident
227 } else {
228 #expr
229 })
230 } else {
231 quote!(#ident: #ident)
232 }
233 } else if let Some(ref expr) = field.default_expression {
234 quote_spanned!(expr.span()=> #ident: if let Some(__val) = #ident.1 {
235 __val
236 } else {
237 #expr
238 })
239 } else {
240 quote!(#ident: #ident.1.expect("Uninitialized fields without defaults were already checked"))
241 });
242 }
243}
244
245pub struct CheckMissing<'a>(&'a Field<'a>);
247
248impl ToTokens for CheckMissing<'_> {
249 fn to_tokens(&self, tokens: &mut TokenStream) {
250 if !self.0.multiple && self.0.default_expression.is_none() {
251 let ident = self.0.ident;
252 let ty = self.0.ty;
253 let name_in_attr = &self.0.name_in_attr;
254
255 let from_none_call =
258 quote_spanned!(ty.span()=> <#ty as ::darling::FromMeta>::from_none());
259
260 tokens.append_all(quote! {
261 if !#ident.0 {
262 match #from_none_call {
263 ::darling::export::Some(__type_fallback) => {
264 #ident.1 = ::darling::export::Some(__type_fallback);
265 }
266 ::darling::export::None => {
267 __errors.push(::darling::Error::missing_field(#name_in_attr))
268 }
269 }
270 }
271 })
272 }
273 }
274}