1 | use proc_macro2::{Span, TokenStream}; |
2 | use quote::{ToTokens, TokenStreamExt}; |
3 | use syn; |
4 | use BuilderPattern; |
5 | use DEFAULT_STRUCT_NAME; |
6 | |
7 | use crate::{change_span, BlockContents, DefaultExpression}; |
8 | |
9 | /// Initializer for the target struct fields, implementing `quote::ToTokens`. |
10 | /// |
11 | /// Lives in the body of `BuildMethod`. |
12 | /// |
13 | /// # Examples |
14 | /// |
15 | /// Will expand to something like the following (depending on settings): |
16 | /// |
17 | /// ```rust,ignore |
18 | /// # extern crate proc_macro2; |
19 | /// # #[macro_use ] |
20 | /// # extern crate quote; |
21 | /// # extern crate syn; |
22 | /// # #[macro_use ] |
23 | /// # extern crate derive_builder_core; |
24 | /// # use derive_builder_core::{DeprecationNotes, Initializer, BuilderPattern}; |
25 | /// # fn main() { |
26 | /// # let mut initializer = default_initializer!(); |
27 | /// # initializer.default_value = Some("42" .parse().unwrap()); |
28 | /// # initializer.builder_pattern = BuilderPattern::Owned; |
29 | /// # |
30 | /// # assert_eq!(quote!(#initializer).to_string(), quote!( |
31 | /// foo: match self.foo { |
32 | /// Some(value) => value, |
33 | /// None => { 42 }, |
34 | /// }, |
35 | /// # ).to_string()); |
36 | /// # } |
37 | /// ``` |
38 | #[derive (Debug, Clone)] |
39 | pub struct Initializer<'a> { |
40 | /// Path to the root of the derive_builder crate. |
41 | pub crate_root: &'a syn::Path, |
42 | /// Name of the target field. |
43 | pub field_ident: &'a syn::Ident, |
44 | /// Whether the builder implements a setter for this field. |
45 | pub field_enabled: bool, |
46 | /// How the build method takes and returns `self` (e.g. mutably). |
47 | pub builder_pattern: BuilderPattern, |
48 | /// Default value for the target field. |
49 | /// |
50 | /// This takes precedence over a default struct identifier. |
51 | pub default_value: Option<&'a DefaultExpression>, |
52 | /// Whether the build_method defines a default struct. |
53 | pub use_default_struct: bool, |
54 | /// Span where the macro was told to use a preexisting error type, instead of creating one, |
55 | /// to represent failures of the `build` method. |
56 | /// |
57 | /// An initializer can force early-return if a field has no set value and no default is |
58 | /// defined. In these cases, it will convert from `derive_builder::UninitializedFieldError` |
59 | /// into the return type of its enclosing `build` method. That conversion is guaranteed to |
60 | /// work for generated error types, but if the caller specified an error type to use instead |
61 | /// they may have forgotten the conversion from `UninitializedFieldError` into their specified |
62 | /// error type. |
63 | pub custom_error_type_span: Option<Span>, |
64 | /// Method to use to to convert the builder's field to the target field |
65 | /// |
66 | /// For sub-builder fields, this will be `build` (or similar) |
67 | pub conversion: FieldConversion<'a>, |
68 | } |
69 | |
70 | impl<'a> ToTokens for Initializer<'a> { |
71 | fn to_tokens(&self, tokens: &mut TokenStream) { |
72 | let struct_field = &self.field_ident; |
73 | let builder_field = struct_field; |
74 | |
75 | // This structure prevents accidental failure to add the trailing `,` due to incautious `return` |
76 | let append_rhs = |tokens: &mut TokenStream| { |
77 | if !self.field_enabled { |
78 | let default = self.default(); |
79 | tokens.append_all(quote!( |
80 | #default |
81 | )); |
82 | } else { |
83 | match &self.conversion { |
84 | FieldConversion::Block(conv) => { |
85 | conv.to_tokens(tokens); |
86 | } |
87 | FieldConversion::Move => tokens.append_all(quote!( self.#builder_field )), |
88 | FieldConversion::OptionOrDefault => { |
89 | let match_some = self.match_some(); |
90 | let match_none = self.match_none(); |
91 | tokens.append_all(quote!( |
92 | match self.#builder_field { |
93 | #match_some, |
94 | #match_none, |
95 | } |
96 | )); |
97 | } |
98 | } |
99 | } |
100 | }; |
101 | |
102 | tokens.append_all(quote!(#struct_field:)); |
103 | append_rhs(tokens); |
104 | tokens.append_all(quote!(,)); |
105 | } |
106 | } |
107 | |
108 | impl<'a> Initializer<'a> { |
109 | /// To be used inside of `#struct_field: match self.#builder_field { ... }` |
110 | fn match_some(&'a self) -> MatchSome { |
111 | match self.builder_pattern { |
112 | BuilderPattern::Owned => MatchSome::Move, |
113 | BuilderPattern::Mutable | BuilderPattern::Immutable => MatchSome::Clone { |
114 | crate_root: self.crate_root, |
115 | }, |
116 | } |
117 | } |
118 | |
119 | /// To be used inside of `#struct_field: match self.#builder_field { ... }` |
120 | fn match_none(&'a self) -> MatchNone<'a> { |
121 | match self.default_value { |
122 | Some(expr) => MatchNone::DefaultTo { |
123 | expr, |
124 | crate_root: self.crate_root, |
125 | }, |
126 | None => { |
127 | if self.use_default_struct { |
128 | MatchNone::UseDefaultStructField(self.field_ident) |
129 | } else { |
130 | MatchNone::ReturnError { |
131 | crate_root: self.crate_root, |
132 | field_name: self.field_ident.to_string(), |
133 | span: self.custom_error_type_span, |
134 | } |
135 | } |
136 | } |
137 | } |
138 | } |
139 | |
140 | fn default(&'a self) -> TokenStream { |
141 | let crate_root = self.crate_root; |
142 | match self.default_value { |
143 | Some(expr) => expr.with_crate_root(crate_root).into_token_stream(), |
144 | None if self.use_default_struct => { |
145 | let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site()); |
146 | let field_ident = self.field_ident; |
147 | quote!(#struct_ident.#field_ident) |
148 | } |
149 | None => { |
150 | quote!(#crate_root::export::core::default::Default::default()) |
151 | } |
152 | } |
153 | } |
154 | } |
155 | |
156 | #[derive (Debug, Clone)] |
157 | pub enum FieldConversion<'a> { |
158 | /// Usual conversion: unwrap the Option from the builder, or (hope to) use a default value |
159 | OptionOrDefault, |
160 | /// Custom conversion is a block contents expression |
161 | Block(&'a BlockContents), |
162 | /// Custom conversion is just to move the field from the builder |
163 | Move, |
164 | } |
165 | |
166 | /// To be used inside of `#struct_field: match self.#builder_field { ... }` |
167 | enum MatchNone<'a> { |
168 | /// Inner value must be a valid Rust expression |
169 | DefaultTo { |
170 | expr: &'a DefaultExpression, |
171 | crate_root: &'a syn::Path, |
172 | }, |
173 | /// Inner value must be the field identifier |
174 | /// |
175 | /// The default struct must be in scope in the build_method. |
176 | UseDefaultStructField(&'a syn::Ident), |
177 | /// Inner value must be the field name |
178 | ReturnError { |
179 | crate_root: &'a syn::Path, |
180 | field_name: String, |
181 | span: Option<Span>, |
182 | }, |
183 | } |
184 | |
185 | impl<'a> ToTokens for MatchNone<'a> { |
186 | fn to_tokens(&self, tokens: &mut TokenStream) { |
187 | match *self { |
188 | MatchNone::DefaultTo { expr, crate_root } => { |
189 | let expr = expr.with_crate_root(crate_root); |
190 | tokens.append_all(quote!(None => #expr)); |
191 | } |
192 | MatchNone::UseDefaultStructField(field_ident) => { |
193 | let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site()); |
194 | tokens.append_all(quote!( |
195 | None => #struct_ident.#field_ident |
196 | )) |
197 | } |
198 | MatchNone::ReturnError { |
199 | ref field_name, |
200 | ref span, |
201 | crate_root, |
202 | } => { |
203 | let conv_span = span.unwrap_or_else(Span::call_site); |
204 | // If the conversion fails, the compiler error should point to the error declaration |
205 | // rather than the crate root declaration, but the compiler will see the span of #crate_root |
206 | // and produce an undesired behavior (possibly because that's the first span in the bad expression?). |
207 | // Creating a copy with deeply-rewritten spans preserves the desired error behavior. |
208 | let crate_root = change_span(crate_root.into_token_stream(), conv_span); |
209 | let err_conv = quote_spanned!(conv_span => #crate_root::export::core::convert::Into::into( |
210 | #crate_root::UninitializedFieldError::from(#field_name) |
211 | )); |
212 | tokens.append_all(quote!( |
213 | None => return #crate_root::export::core::result::Result::Err(#err_conv) |
214 | )); |
215 | } |
216 | } |
217 | } |
218 | } |
219 | |
220 | /// To be used inside of `#struct_field: match self.#builder_field { ... }` |
221 | enum MatchSome<'a> { |
222 | Move, |
223 | Clone { crate_root: &'a syn::Path }, |
224 | } |
225 | |
226 | impl ToTokens for MatchSome<'_> { |
227 | fn to_tokens(&self, tokens: &mut TokenStream) { |
228 | match *self { |
229 | Self::Move => tokens.append_all(iter:quote!( |
230 | Some(value) => value |
231 | )), |
232 | Self::Clone { crate_root: &Path } => tokens.append_all(iter:quote!( |
233 | Some(ref value) => #crate_root::export::core::clone::Clone::clone(value) |
234 | )), |
235 | } |
236 | } |
237 | } |
238 | |
239 | /// Helper macro for unit tests. This is _only_ public in order to be accessible |
240 | /// from doc-tests too. |
241 | #[doc (hidden)] |
242 | #[macro_export ] |
243 | macro_rules! default_initializer { |
244 | () => { |
245 | Initializer { |
246 | // Deliberately don't use the default value here - make sure |
247 | // that all test cases are passing crate_root through properly. |
248 | crate_root: &parse_quote!(::db), |
249 | field_ident: &syn::Ident::new("foo" , ::proc_macro2::Span::call_site()), |
250 | field_enabled: true, |
251 | builder_pattern: BuilderPattern::Mutable, |
252 | default_value: None, |
253 | use_default_struct: false, |
254 | conversion: FieldConversion::OptionOrDefault, |
255 | custom_error_type_span: None, |
256 | } |
257 | }; |
258 | } |
259 | |
260 | #[cfg (test)] |
261 | mod tests { |
262 | #[allow (unused_imports)] |
263 | use super::*; |
264 | |
265 | #[test ] |
266 | fn immutable() { |
267 | let mut initializer = default_initializer!(); |
268 | initializer.builder_pattern = BuilderPattern::Immutable; |
269 | |
270 | assert_eq!( |
271 | quote!(#initializer).to_string(), |
272 | quote!( |
273 | foo: match self.foo { |
274 | Some(ref value) => ::db::export::core::clone::Clone::clone(value), |
275 | None => return ::db::export::core::result::Result::Err(::db::export::core::convert::Into::into( |
276 | ::db::UninitializedFieldError::from("foo" ) |
277 | )), |
278 | }, |
279 | ) |
280 | .to_string() |
281 | ); |
282 | } |
283 | |
284 | #[test ] |
285 | fn mutable() { |
286 | let mut initializer = default_initializer!(); |
287 | initializer.builder_pattern = BuilderPattern::Mutable; |
288 | |
289 | assert_eq!( |
290 | quote!(#initializer).to_string(), |
291 | quote!( |
292 | foo: match self.foo { |
293 | Some(ref value) => ::db::export::core::clone::Clone::clone(value), |
294 | None => return ::db::export::core::result::Result::Err(::db::export::core::convert::Into::into( |
295 | ::db::UninitializedFieldError::from("foo" ) |
296 | )), |
297 | }, |
298 | ) |
299 | .to_string() |
300 | ); |
301 | } |
302 | |
303 | #[test ] |
304 | fn owned() { |
305 | let mut initializer = default_initializer!(); |
306 | initializer.builder_pattern = BuilderPattern::Owned; |
307 | |
308 | assert_eq!( |
309 | quote!(#initializer).to_string(), |
310 | quote!( |
311 | foo: match self.foo { |
312 | Some(value) => value, |
313 | None => return ::db::export::core::result::Result::Err(::db::export::core::convert::Into::into( |
314 | ::db::UninitializedFieldError::from("foo" ) |
315 | )), |
316 | }, |
317 | ) |
318 | .to_string() |
319 | ); |
320 | } |
321 | |
322 | #[test ] |
323 | fn default_value() { |
324 | let mut initializer = default_initializer!(); |
325 | let default_value = DefaultExpression::explicit::<syn::Expr>(parse_quote!(42)); |
326 | initializer.default_value = Some(&default_value); |
327 | |
328 | assert_eq!( |
329 | quote!(#initializer).to_string(), |
330 | quote!( |
331 | foo: match self.foo { |
332 | Some(ref value) => ::db::export::core::clone::Clone::clone(value), |
333 | None => { 42 }, |
334 | }, |
335 | ) |
336 | .to_string() |
337 | ); |
338 | } |
339 | |
340 | #[test ] |
341 | fn default_struct() { |
342 | let mut initializer = default_initializer!(); |
343 | initializer.use_default_struct = true; |
344 | |
345 | assert_eq!( |
346 | quote!(#initializer).to_string(), |
347 | quote!( |
348 | foo: match self.foo { |
349 | Some(ref value) => ::db::export::core::clone::Clone::clone(value), |
350 | None => __default.foo, |
351 | }, |
352 | ) |
353 | .to_string() |
354 | ); |
355 | } |
356 | |
357 | #[test ] |
358 | fn setter_disabled() { |
359 | let mut initializer = default_initializer!(); |
360 | initializer.field_enabled = false; |
361 | |
362 | assert_eq!( |
363 | quote!(#initializer).to_string(), |
364 | quote!(foo: ::db::export::core::default::Default::default(),).to_string() |
365 | ); |
366 | } |
367 | |
368 | #[test ] |
369 | fn no_std() { |
370 | let initializer = default_initializer!(); |
371 | |
372 | assert_eq!( |
373 | quote!(#initializer).to_string(), |
374 | quote!( |
375 | foo: match self.foo { |
376 | Some(ref value) => ::db::export::core::clone::Clone::clone(value), |
377 | None => return ::db::export::core::result::Result::Err(::db::export::core::convert::Into::into( |
378 | ::db::UninitializedFieldError::from("foo" ) |
379 | )), |
380 | }, |
381 | ) |
382 | .to_string() |
383 | ); |
384 | } |
385 | } |
386 | |