1 | use std::borrow::Cow; |
2 | |
3 | use syn::{parse_quote_spanned, spanned::Spanned}; |
4 | |
5 | use crate::codegen; |
6 | use crate::options::{Core, DefaultExpression, ParseAttribute}; |
7 | use crate::util::{Callable, Flag, SpannedValue}; |
8 | use crate::{Error, FromMeta, Result}; |
9 | |
10 | #[derive (Debug, Clone)] |
11 | pub struct InputField { |
12 | pub ident: syn::Ident, |
13 | pub attr_name: Option<String>, |
14 | pub ty: syn::Type, |
15 | pub default: Option<DefaultExpression>, |
16 | pub with: Option<Callable>, |
17 | |
18 | /// If `true`, generated code will not look for this field in the input meta item, |
19 | /// instead always falling back to either `InputField::default` or `Default::default`. |
20 | pub skip: Option<SpannedValue<bool>>, |
21 | pub post_transform: Option<codegen::PostfixTransform>, |
22 | pub multiple: Option<bool>, |
23 | pub flatten: Flag, |
24 | } |
25 | |
26 | impl InputField { |
27 | /// Generate a view into this field that can be used for code generation. |
28 | pub fn as_codegen_field(&self) -> codegen::Field<'_> { |
29 | codegen::Field { |
30 | ident: &self.ident, |
31 | name_in_attr: self |
32 | .attr_name |
33 | .as_ref() |
34 | .map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed), |
35 | ty: &self.ty, |
36 | default_expression: self.as_codegen_default(), |
37 | with_callable: self.with.as_ref().map(|w| w.as_ref()).map_or_else( |
38 | || { |
39 | Cow::Owned( |
40 | parse_quote_spanned!(self.ty.span()=> ::darling::FromMeta::from_meta), |
41 | ) |
42 | }, |
43 | Cow::Borrowed, |
44 | ), |
45 | skip: *self.skip.unwrap_or_default(), |
46 | post_transform: self.post_transform.as_ref(), |
47 | multiple: self.multiple.unwrap_or_default(), |
48 | flatten: self.flatten.is_present(), |
49 | } |
50 | } |
51 | |
52 | /// Generate a codegen::DefaultExpression for this field. This requires the field name |
53 | /// in the `Inherit` case. |
54 | fn as_codegen_default(&self) -> Option<codegen::DefaultExpression<'_>> { |
55 | self.default.as_ref().map(|expr| match *expr { |
56 | DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path), |
57 | DefaultExpression::Inherit => codegen::DefaultExpression::Inherit(&self.ident), |
58 | DefaultExpression::Trait { span } => codegen::DefaultExpression::Trait { span }, |
59 | }) |
60 | } |
61 | |
62 | fn new(ident: syn::Ident, ty: syn::Type) -> Self { |
63 | InputField { |
64 | ident, |
65 | ty, |
66 | attr_name: None, |
67 | default: None, |
68 | with: None, |
69 | skip: None, |
70 | post_transform: Default::default(), |
71 | multiple: None, |
72 | flatten: Default::default(), |
73 | } |
74 | } |
75 | |
76 | pub fn from_field(f: &syn::Field, parent: Option<&Core>) -> Result<Self> { |
77 | let ident = f |
78 | .ident |
79 | .clone() |
80 | .unwrap_or_else(|| syn::Ident::new("__unnamed" , ::proc_macro2::Span::call_site())); |
81 | let ty = f.ty.clone(); |
82 | let base = Self::new(ident, ty).parse_attributes(&f.attrs)?; |
83 | |
84 | Ok(if let Some(container) = parent { |
85 | base.with_inherited(container) |
86 | } else { |
87 | base |
88 | }) |
89 | } |
90 | |
91 | /// Apply inherited settings from the container. This is done _after_ parsing |
92 | /// to ensure deference to explicit field-level settings. |
93 | fn with_inherited(mut self, parent: &Core) -> Self { |
94 | // explicit renamings take precedence over rename rules on the container, |
95 | // but in the absence of an explicit name we apply the rule. |
96 | if self.attr_name.is_none() { |
97 | self.attr_name = Some(parent.rename_rule.apply_to_field(self.ident.to_string())); |
98 | } |
99 | |
100 | // Determine the default expression for this field, based on three pieces of information: |
101 | // 1. Will we look for this field in the attribute? |
102 | // 1. Is there a locally-defined default? |
103 | // 1. Did the parent define a default? |
104 | self.default = match (&self.skip, self.default.is_some(), parent.default.is_some()) { |
105 | // If we have a default, use it. |
106 | (_, true, _) => self.default, |
107 | |
108 | // If there isn't an explicit default but the struct sets a default, we'll |
109 | // inherit from that. |
110 | (_, false, true) => Some(DefaultExpression::Inherit), |
111 | |
112 | // If we're skipping the field and no defaults have been expressed then we should |
113 | // use the ::darling::export::Default trait, and set the span to the skip keyword |
114 | // so that an error caused by the skipped field's type not implementing `Default` |
115 | // will correctly identify why darling is trying to use `Default`. |
116 | (Some(v), false, false) if **v => Some(DefaultExpression::Trait { span: v.span() }), |
117 | |
118 | // If we don't have or need a default, then leave it blank. |
119 | (_, false, false) => None, |
120 | }; |
121 | |
122 | self |
123 | } |
124 | } |
125 | |
126 | impl ParseAttribute for InputField { |
127 | fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> { |
128 | let path = mi.path(); |
129 | |
130 | if path.is_ident("rename" ) { |
131 | if self.attr_name.is_some() { |
132 | return Err(Error::duplicate_field_path(path).with_span(mi)); |
133 | } |
134 | |
135 | self.attr_name = FromMeta::from_meta(mi)?; |
136 | |
137 | if self.flatten.is_present() { |
138 | return Err( |
139 | Error::custom("`flatten` and `rename` cannot be used together" ).with_span(mi), |
140 | ); |
141 | } |
142 | } else if path.is_ident("default" ) { |
143 | if self.default.is_some() { |
144 | return Err(Error::duplicate_field_path(path).with_span(mi)); |
145 | } |
146 | self.default = FromMeta::from_meta(mi)?; |
147 | } else if path.is_ident("with" ) { |
148 | if self.with.is_some() { |
149 | return Err(Error::duplicate_field_path(path).with_span(mi)); |
150 | } |
151 | |
152 | self.with = Some(FromMeta::from_meta(mi)?); |
153 | |
154 | if self.flatten.is_present() { |
155 | return Err( |
156 | Error::custom("`flatten` and `with` cannot be used together" ).with_span(mi), |
157 | ); |
158 | } |
159 | } else if path.is_ident("skip" ) { |
160 | if self.skip.is_some() { |
161 | return Err(Error::duplicate_field_path(path).with_span(mi)); |
162 | } |
163 | |
164 | self.skip = FromMeta::from_meta(mi)?; |
165 | |
166 | if self.skip.map(|v| *v).unwrap_or_default() && self.flatten.is_present() { |
167 | return Err( |
168 | Error::custom("`flatten` and `skip` cannot be used together" ).with_span(mi), |
169 | ); |
170 | } |
171 | } else if path.is_ident("map" ) || path.is_ident("and_then" ) { |
172 | let transformer = path.get_ident().unwrap().clone(); |
173 | if let Some(post_transform) = &self.post_transform { |
174 | if transformer == post_transform.transformer { |
175 | return Err(Error::duplicate_field_path(path).with_span(mi)); |
176 | } else { |
177 | return Err(Error::custom(format!( |
178 | "Options ` {}` and ` {}` are mutually exclusive" , |
179 | transformer, post_transform.transformer |
180 | )) |
181 | .with_span(mi)); |
182 | } |
183 | } |
184 | |
185 | self.post_transform = Some(codegen::PostfixTransform::new( |
186 | transformer, |
187 | FromMeta::from_meta(mi)?, |
188 | )); |
189 | } else if path.is_ident("multiple" ) { |
190 | if self.multiple.is_some() { |
191 | return Err(Error::duplicate_field_path(path).with_span(mi)); |
192 | } |
193 | |
194 | self.multiple = FromMeta::from_meta(mi)?; |
195 | |
196 | if self.multiple == Some(true) && self.flatten.is_present() { |
197 | return Err( |
198 | Error::custom("`flatten` and `multiple` cannot be used together" ).with_span(mi), |
199 | ); |
200 | } |
201 | } else if path.is_ident("flatten" ) { |
202 | if self.flatten.is_present() { |
203 | return Err(Error::duplicate_field_path(path).with_span(mi)); |
204 | } |
205 | |
206 | self.flatten = FromMeta::from_meta(mi)?; |
207 | |
208 | let mut conflicts = Error::accumulator(); |
209 | |
210 | if self.multiple == Some(true) { |
211 | conflicts.push( |
212 | Error::custom("`flatten` and `multiple` cannot be used together" ).with_span(mi), |
213 | ); |
214 | } |
215 | |
216 | if self.attr_name.is_some() { |
217 | conflicts.push( |
218 | Error::custom("`flatten` and `rename` cannot be used together" ).with_span(mi), |
219 | ); |
220 | } |
221 | |
222 | if self.with.is_some() { |
223 | conflicts.push( |
224 | Error::custom("`flatten` and `with` cannot be used together" ).with_span(mi), |
225 | ); |
226 | } |
227 | |
228 | if self.skip.map(|v| *v).unwrap_or_default() { |
229 | conflicts.push( |
230 | Error::custom("`flatten` and `skip` cannot be used together" ).with_span(mi), |
231 | ); |
232 | } |
233 | |
234 | conflicts.finish()?; |
235 | } else { |
236 | return Err(Error::unknown_field_path(path).with_span(mi)); |
237 | } |
238 | |
239 | Ok(()) |
240 | } |
241 | } |
242 | |