1 | #![allow (clippy::useless_let_if_seq)] |
2 | use std::borrow::Cow; |
3 | |
4 | use proc_macro2::{Span, TokenStream}; |
5 | use quote::{ToTokens, TokenStreamExt}; |
6 | use syn; |
7 | |
8 | use BuilderFieldType; |
9 | use BuilderPattern; |
10 | use DeprecationNotes; |
11 | use Each; |
12 | |
13 | /// Setter for the struct fields in the build method, implementing |
14 | /// `quote::ToTokens`. |
15 | /// |
16 | /// # Examples |
17 | /// |
18 | /// Will expand to something like the following (depending on settings): |
19 | /// |
20 | /// ```rust,ignore |
21 | /// # extern crate proc_macro2; |
22 | /// # #[macro_use ] |
23 | /// # extern crate quote; |
24 | /// # extern crate syn; |
25 | /// # #[macro_use ] |
26 | /// # extern crate derive_builder_core; |
27 | /// # use derive_builder_core::{Setter, BuilderPattern}; |
28 | /// # fn main() { |
29 | /// # let mut setter = default_setter!(); |
30 | /// # setter.pattern = BuilderPattern::Mutable; |
31 | /// # |
32 | /// # assert_eq!(quote!(#setter).to_string(), quote!( |
33 | /// # #[allow(unused_mut)] |
34 | /// pub fn foo(&mut self, value: Foo) -> &mut Self { |
35 | /// let mut new = self; |
36 | /// new.foo = ::derive_builder::export::core::option::Option::Some(value); |
37 | /// new |
38 | /// } |
39 | /// # ).to_string()); |
40 | /// # } |
41 | /// ``` |
42 | #[derive (Debug, Clone)] |
43 | pub struct Setter<'a> { |
44 | /// Path to the root of the derive_builder crate. |
45 | pub crate_root: &'a syn::Path, |
46 | /// Enables code generation for this setter fn. |
47 | pub setter_enabled: bool, |
48 | /// Enables code generation for the `try_` variant of this setter fn. |
49 | pub try_setter: bool, |
50 | /// Visibility of the setter, e.g. `syn::Visibility::Public`. |
51 | pub visibility: Cow<'a, syn::Visibility>, |
52 | /// How the setter method takes and returns `self` (e.g. mutably). |
53 | pub pattern: BuilderPattern, |
54 | /// Attributes which will be attached to this setter fn. |
55 | pub attrs: &'a [syn::Attribute], |
56 | /// Name of this setter fn. |
57 | pub ident: syn::Ident, |
58 | /// Name of the target field. |
59 | pub field_ident: &'a syn::Ident, |
60 | /// Type of the builder field. |
61 | /// |
62 | /// The corresonding builder field will be `Option<field_type>`. |
63 | pub field_type: BuilderFieldType<'a>, |
64 | /// Make the setter generic over `Into<T>`, where `T` is the field type. |
65 | pub generic_into: bool, |
66 | /// Make the setter remove the Option wrapper from the setter, remove the need to call Some(...). |
67 | /// when combined with into, the into is used on the content Type of the Option. |
68 | pub strip_option: bool, |
69 | /// Emit deprecation notes to the user. |
70 | pub deprecation_notes: &'a DeprecationNotes, |
71 | /// Emit extend method. |
72 | pub each: Option<&'a Each>, |
73 | } |
74 | |
75 | impl<'a> ToTokens for Setter<'a> { |
76 | fn to_tokens(&self, tokens: &mut TokenStream) { |
77 | if self.setter_enabled { |
78 | let crate_root = self.crate_root; |
79 | let pattern = self.pattern; |
80 | let vis = &self.visibility; |
81 | let field_ident = self.field_ident; |
82 | let ident = &self.ident; |
83 | let attrs = self.attrs; |
84 | let deprecation_notes = self.deprecation_notes; |
85 | |
86 | let self_param: TokenStream; |
87 | let return_ty: TokenStream; |
88 | let self_into_return_ty: TokenStream; |
89 | |
90 | match pattern { |
91 | BuilderPattern::Owned => { |
92 | self_param = quote!(self); |
93 | return_ty = quote!(Self); |
94 | self_into_return_ty = quote!(self); |
95 | } |
96 | BuilderPattern::Mutable => { |
97 | self_param = quote!(&mut self); |
98 | return_ty = quote!(&mut Self); |
99 | self_into_return_ty = quote!(self); |
100 | } |
101 | BuilderPattern::Immutable => { |
102 | self_param = quote!(&self); |
103 | return_ty = quote!(Self); |
104 | self_into_return_ty = |
105 | quote!(#crate_root::export::core::clone::Clone::clone(self)); |
106 | } |
107 | }; |
108 | |
109 | let ty_params: TokenStream; |
110 | let param_ty: TokenStream; |
111 | let mut into_value: TokenStream; |
112 | |
113 | let (field_type, builder_field_is_option) = self.field_type.setter_type_info(); |
114 | |
115 | let (ty, stripped_option) = { |
116 | if self.strip_option { |
117 | match extract_type_from_option(field_type) { |
118 | Some(ty) => (ty, true), |
119 | None => (field_type, false), |
120 | } |
121 | } else { |
122 | (field_type, false) |
123 | } |
124 | }; |
125 | |
126 | if self.generic_into { |
127 | ty_params = quote!(<VALUE: #crate_root::export::core::convert::Into<#ty>>); |
128 | param_ty = quote!(VALUE); |
129 | into_value = quote!(value.into()); |
130 | } else { |
131 | ty_params = quote!(); |
132 | param_ty = quote!(#ty); |
133 | into_value = quote!(value); |
134 | } |
135 | // If both `stripped_option` and `builder_field_is_option`, the target field is `Option<field_type>`, |
136 | // the builder field is `Option<Option<field_type>>`, and the setter takes `file_type`, so we must wrap it twice. |
137 | if stripped_option { |
138 | into_value = wrap_expression_in_some(crate_root, into_value); |
139 | } |
140 | if builder_field_is_option { |
141 | into_value = wrap_expression_in_some(crate_root, into_value); |
142 | } |
143 | |
144 | tokens.append_all(quote!( |
145 | #(#attrs)* |
146 | #[allow(unused_mut)] |
147 | #vis fn #ident #ty_params (#self_param, value: #param_ty) |
148 | -> #return_ty |
149 | { |
150 | #deprecation_notes |
151 | let mut new = #self_into_return_ty; |
152 | new.#field_ident = #into_value; |
153 | new |
154 | } |
155 | )); |
156 | |
157 | if self.try_setter { |
158 | let try_ty_params = |
159 | quote!(<VALUE: #crate_root::export::core::convert::TryInto<#ty>>); |
160 | let try_ident = syn::Ident::new(&format!("try_ {}" , ident), Span::call_site()); |
161 | |
162 | let mut converted = quote! {converted}; |
163 | if builder_field_is_option { |
164 | converted = wrap_expression_in_some(crate_root, converted); |
165 | } |
166 | |
167 | tokens.append_all(quote!( |
168 | #(#attrs)* |
169 | #vis fn #try_ident #try_ty_params (#self_param, value: VALUE) |
170 | -> #crate_root::export::core::result::Result<#return_ty, VALUE::Error> |
171 | { |
172 | let converted : #ty = value.try_into()?; |
173 | let mut new = #self_into_return_ty; |
174 | new.#field_ident = #converted; |
175 | Ok(new) |
176 | } |
177 | )); |
178 | } |
179 | |
180 | if let Some(each) = self.each { |
181 | let ident_each = &each.name; |
182 | |
183 | // Access the collection to extend, initialising with default value if necessary. |
184 | let get_initialized_collection = if stripped_option { |
185 | // Outer (builder) Option -> Inner (field) Option -> collection. |
186 | quote!(get_or_insert_with(|| Some( |
187 | #crate_root::export::core::default::Default::default() |
188 | )) |
189 | .get_or_insert_with(#crate_root::export::core::default::Default::default)) |
190 | } else { |
191 | // Outer (builder) Option -> collection. |
192 | quote!(get_or_insert_with( |
193 | #crate_root::export::core::default::Default::default |
194 | )) |
195 | }; |
196 | |
197 | let ty_params: TokenStream; |
198 | let param_ty: TokenStream; |
199 | let into_item: TokenStream; |
200 | |
201 | if each.into { |
202 | ty_params = quote!(<VALUE, FROM_VALUE: #crate_root::export::core::convert::Into<VALUE>>); |
203 | param_ty = quote!(FROM_VALUE); |
204 | into_item = quote!(#crate_root::export::core::convert::Into::into(item)); |
205 | } else { |
206 | ty_params = quote!(<VALUE>); |
207 | param_ty = quote!(VALUE); |
208 | into_item = quote!(item); |
209 | } |
210 | |
211 | tokens.append_all(quote!( |
212 | #(#attrs)* |
213 | #[allow(unused_mut)] |
214 | #vis fn #ident_each #ty_params(#self_param, item: #param_ty) -> #return_ty |
215 | where |
216 | #ty: #crate_root::export::core::default::Default + #crate_root::export::core::iter::Extend<VALUE>, |
217 | { |
218 | #deprecation_notes |
219 | let mut new = #self_into_return_ty; |
220 | new.#field_ident |
221 | .#get_initialized_collection |
222 | .extend(#crate_root::export::core::option::Option::Some(#into_item)); |
223 | new |
224 | } |
225 | )); |
226 | } |
227 | } |
228 | } |
229 | } |
230 | |
231 | /// Returns expression wrapping `bare_value` in `Some` |
232 | fn wrap_expression_in_some(crate_root: &syn::Path, bare_value: impl ToTokens) -> TokenStream { |
233 | quote!( #crate_root::export::core::option::Option::Some(#bare_value) ) |
234 | } |
235 | |
236 | // adapted from https://stackoverflow.com/a/55277337/469066 |
237 | // Note that since syn is a parser, it works with tokens. |
238 | // We cannot know for sure that this is an Option. |
239 | // The user could, for example, `type MaybeString = std::option::Option<String>` |
240 | // We cannot handle those arbitrary names. |
241 | fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> { |
242 | use syn::punctuated::Pair; |
243 | use syn::token::Colon2; |
244 | use syn::{GenericArgument, Path, PathArguments, PathSegment}; |
245 | |
246 | fn extract_type_path(ty: &syn::Type) -> Option<&Path> { |
247 | match *ty { |
248 | syn::Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path), |
249 | _ => None, |
250 | } |
251 | } |
252 | |
253 | // TODO store (with lazy static) precomputed parsing of Option when support of rust 1.18 will be removed (incompatible with lazy_static) |
254 | // TODO maybe optimization, reverse the order of segments |
255 | fn extract_option_segment(path: &Path) -> Option<Pair<&PathSegment, &Colon2>> { |
256 | let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| { |
257 | acc.push_str(&v.ident.to_string()); |
258 | acc.push('|' ); |
259 | acc |
260 | }); |
261 | vec!["Option|" , "std|option|Option|" , "core|option|Option|" ] |
262 | .into_iter() |
263 | .find(|s| idents_of_path == *s) |
264 | .and_then(|_| path.segments.last().map(Pair::End)) |
265 | } |
266 | |
267 | extract_type_path(ty) |
268 | .and_then(extract_option_segment) |
269 | .and_then(|pair_path_segment| { |
270 | let type_params = &pair_path_segment.into_value().arguments; |
271 | // It should have only on angle-bracketed param ("<String>"): |
272 | match *type_params { |
273 | PathArguments::AngleBracketed(ref params) => params.args.first(), |
274 | _ => None, |
275 | } |
276 | }) |
277 | .and_then(|generic_arg| match *generic_arg { |
278 | GenericArgument::Type(ref ty) => Some(ty), |
279 | _ => None, |
280 | }) |
281 | } |
282 | |
283 | /// Helper macro for unit tests. This is _only_ public in order to be accessible |
284 | /// from doc-tests too. |
285 | #[doc (hidden)] |
286 | #[macro_export ] |
287 | macro_rules! default_setter { |
288 | () => { |
289 | Setter { |
290 | // Deliberately don't use the default value here - make sure |
291 | // that all test cases are passing crate_root through properly. |
292 | crate_root: &parse_quote!(::db), |
293 | setter_enabled: true, |
294 | try_setter: false, |
295 | visibility: ::std::borrow::Cow::Owned(parse_quote!(pub)), |
296 | pattern: BuilderPattern::Mutable, |
297 | attrs: &vec![], |
298 | ident: syn::Ident::new("foo" , ::proc_macro2::Span::call_site()), |
299 | field_ident: &syn::Ident::new("foo" , ::proc_macro2::Span::call_site()), |
300 | field_type: BuilderFieldType::Optional(Box::leak(Box::new(parse_quote!(Foo)))), |
301 | generic_into: false, |
302 | strip_option: false, |
303 | deprecation_notes: &Default::default(), |
304 | each: None, |
305 | } |
306 | }; |
307 | } |
308 | |
309 | #[cfg (test)] |
310 | mod tests { |
311 | #[allow (unused_imports)] |
312 | use super::*; |
313 | |
314 | #[test ] |
315 | fn immutable() { |
316 | let mut setter = default_setter!(); |
317 | setter.pattern = BuilderPattern::Immutable; |
318 | |
319 | assert_eq!( |
320 | quote!(#setter).to_string(), |
321 | quote!( |
322 | #[allow(unused_mut)] |
323 | pub fn foo(&self, value: Foo) -> Self { |
324 | let mut new = ::db::export::core::clone::Clone::clone(self); |
325 | new.foo = ::db::export::core::option::Option::Some(value); |
326 | new |
327 | } |
328 | ) |
329 | .to_string() |
330 | ); |
331 | } |
332 | |
333 | #[test ] |
334 | fn mutable() { |
335 | let mut setter = default_setter!(); |
336 | setter.pattern = BuilderPattern::Mutable; |
337 | |
338 | assert_eq!( |
339 | quote!(#setter).to_string(), |
340 | quote!( |
341 | #[allow(unused_mut)] |
342 | pub fn foo(&mut self, value: Foo) -> &mut Self { |
343 | let mut new = self; |
344 | new.foo = ::db::export::core::option::Option::Some(value); |
345 | new |
346 | } |
347 | ) |
348 | .to_string() |
349 | ); |
350 | } |
351 | |
352 | #[test ] |
353 | fn owned() { |
354 | let mut setter = default_setter!(); |
355 | setter.pattern = BuilderPattern::Owned; |
356 | |
357 | assert_eq!( |
358 | quote!(#setter).to_string(), |
359 | quote!( |
360 | #[allow(unused_mut)] |
361 | pub fn foo(self, value: Foo) -> Self { |
362 | let mut new = self; |
363 | new.foo = ::db::export::core::option::Option::Some(value); |
364 | new |
365 | } |
366 | ) |
367 | .to_string() |
368 | ); |
369 | } |
370 | |
371 | #[test ] |
372 | fn private() { |
373 | let vis = Cow::Owned(syn::Visibility::Inherited); |
374 | |
375 | let mut setter = default_setter!(); |
376 | setter.visibility = vis; |
377 | |
378 | assert_eq!( |
379 | quote!(#setter).to_string(), |
380 | quote!( |
381 | #[allow(unused_mut)] |
382 | fn foo(&mut self, value: Foo) -> &mut Self { |
383 | let mut new = self; |
384 | new.foo = ::db::export::core::option::Option::Some(value); |
385 | new |
386 | } |
387 | ) |
388 | .to_string() |
389 | ); |
390 | } |
391 | |
392 | #[test ] |
393 | fn generic() { |
394 | let mut setter = default_setter!(); |
395 | setter.generic_into = true; |
396 | |
397 | #[rustfmt::skip] |
398 | assert_eq!( |
399 | quote!(#setter).to_string(), |
400 | quote!( |
401 | #[allow(unused_mut)] |
402 | pub fn foo<VALUE: ::db::export::core::convert::Into<Foo>>( |
403 | &mut self, |
404 | value: VALUE |
405 | ) -> &mut Self { |
406 | let mut new = self; |
407 | new.foo = ::db::export::core::option::Option::Some(value.into()); |
408 | new |
409 | } |
410 | ) |
411 | .to_string() |
412 | ); |
413 | } |
414 | |
415 | #[test ] |
416 | fn strip_option() { |
417 | let ty = parse_quote!(Option<Foo>); |
418 | let mut setter = default_setter!(); |
419 | setter.strip_option = true; |
420 | setter.field_type = BuilderFieldType::Optional(&ty); |
421 | |
422 | #[rustfmt::skip] |
423 | assert_eq!( |
424 | quote!(#setter).to_string(), |
425 | quote!( |
426 | #[allow(unused_mut)] |
427 | pub fn foo(&mut self, value: Foo) -> &mut Self { |
428 | let mut new = self; |
429 | new.foo = ::db::export::core::option::Option::Some( |
430 | ::db::export::core::option::Option::Some(value) |
431 | ); |
432 | new |
433 | } |
434 | ) |
435 | .to_string() |
436 | ); |
437 | } |
438 | |
439 | #[test ] |
440 | fn strip_option_into() { |
441 | let ty = parse_quote!(Option<Foo>); |
442 | let mut setter = default_setter!(); |
443 | setter.strip_option = true; |
444 | setter.generic_into = true; |
445 | setter.field_type = BuilderFieldType::Optional(&ty); |
446 | |
447 | #[rustfmt::skip] |
448 | assert_eq!( |
449 | quote!(#setter).to_string(), |
450 | quote!( |
451 | #[allow(unused_mut)] |
452 | pub fn foo<VALUE: ::db::export::core::convert::Into<Foo>>( |
453 | &mut self, |
454 | value: VALUE |
455 | ) -> &mut Self { |
456 | let mut new = self; |
457 | new.foo = ::db::export::core::option::Option::Some( |
458 | ::db::export::core::option::Option::Some(value.into()) |
459 | ); |
460 | new |
461 | } |
462 | ) |
463 | .to_string() |
464 | ); |
465 | } |
466 | |
467 | // including try_setter |
468 | #[test ] |
469 | fn full() { |
470 | //named!(outer_attrs -> Vec<syn::Attribute>, many0!(syn::Attribute::parse_outer)); |
471 | //let attrs = outer_attrs.parse_str("#[some_attr]").unwrap(); |
472 | let attrs: Vec<syn::Attribute> = vec![parse_quote!(#[some_attr])]; |
473 | |
474 | let mut deprecated = DeprecationNotes::default(); |
475 | deprecated.push("Some example." .to_string()); |
476 | |
477 | let mut setter = default_setter!(); |
478 | setter.attrs = attrs.as_slice(); |
479 | setter.generic_into = true; |
480 | setter.deprecation_notes = &deprecated; |
481 | setter.try_setter = true; |
482 | |
483 | assert_eq!( |
484 | quote!(#setter).to_string(), |
485 | quote!( |
486 | #[some_attr] |
487 | #[allow(unused_mut)] |
488 | pub fn foo <VALUE: ::db::export::core::convert::Into<Foo>>(&mut self, value: VALUE) -> &mut Self { |
489 | #deprecated |
490 | let mut new = self; |
491 | new.foo = ::db::export::core::option::Option::Some(value.into()); |
492 | new |
493 | } |
494 | |
495 | #[some_attr] |
496 | pub fn try_foo<VALUE: ::db::export::core::convert::TryInto<Foo>>(&mut self, value: VALUE) |
497 | -> ::db::export::core::result::Result<&mut Self, VALUE::Error> { |
498 | let converted : Foo = value.try_into()?; |
499 | let mut new = self; |
500 | new.foo = ::db::export::core::option::Option::Some(converted); |
501 | Ok(new) |
502 | } |
503 | ).to_string() |
504 | ); |
505 | } |
506 | |
507 | #[test ] |
508 | fn no_std() { |
509 | let mut setter = default_setter!(); |
510 | setter.pattern = BuilderPattern::Immutable; |
511 | |
512 | assert_eq!( |
513 | quote!(#setter).to_string(), |
514 | quote!( |
515 | #[allow(unused_mut)] |
516 | pub fn foo(&self, value: Foo) -> Self { |
517 | let mut new = ::db::export::core::clone::Clone::clone(self); |
518 | new.foo = ::db::export::core::option::Option::Some(value); |
519 | new |
520 | } |
521 | ) |
522 | .to_string() |
523 | ); |
524 | } |
525 | |
526 | #[test ] |
527 | fn no_std_generic() { |
528 | let mut setter = default_setter!(); |
529 | setter.generic_into = true; |
530 | |
531 | #[rustfmt::skip] |
532 | assert_eq!( |
533 | quote!(#setter).to_string(), |
534 | quote!( |
535 | #[allow(unused_mut)] |
536 | pub fn foo<VALUE: ::db::export::core::convert::Into<Foo>>( |
537 | &mut self, |
538 | value: VALUE |
539 | ) -> &mut Self { |
540 | let mut new = self; |
541 | new.foo = ::db::export::core::option::Option::Some(value.into()); |
542 | new |
543 | } |
544 | ) |
545 | .to_string() |
546 | ); |
547 | } |
548 | |
549 | #[test ] |
550 | fn setter_disabled() { |
551 | let mut setter = default_setter!(); |
552 | setter.setter_enabled = false; |
553 | |
554 | assert_eq!(quote!(#setter).to_string(), quote!().to_string()); |
555 | } |
556 | |
557 | #[test ] |
558 | fn try_setter() { |
559 | let mut setter: Setter = default_setter!(); |
560 | setter.pattern = BuilderPattern::Mutable; |
561 | setter.try_setter = true; |
562 | |
563 | #[rustfmt::skip] |
564 | assert_eq!( |
565 | quote!(#setter).to_string(), |
566 | quote!( |
567 | #[allow(unused_mut)] |
568 | pub fn foo(&mut self, value: Foo) -> &mut Self { |
569 | let mut new = self; |
570 | new.foo = ::db::export::core::option::Option::Some(value); |
571 | new |
572 | } |
573 | |
574 | pub fn try_foo<VALUE: ::db::export::core::convert::TryInto<Foo>>( |
575 | &mut self, |
576 | value: VALUE |
577 | ) -> ::db::export::core::result::Result<&mut Self, VALUE::Error> { |
578 | let converted: Foo = value.try_into()?; |
579 | let mut new = self; |
580 | new.foo = ::db::export::core::option::Option::Some(converted); |
581 | Ok(new) |
582 | } |
583 | ) |
584 | .to_string() |
585 | ); |
586 | } |
587 | |
588 | #[test ] |
589 | fn extract_type_from_option_on_simple_type() { |
590 | let ty_foo = parse_quote!(Foo); |
591 | assert_eq!(extract_type_from_option(&ty_foo), None); |
592 | |
593 | for s in vec![ |
594 | parse_quote!(Option<Foo>), |
595 | parse_quote!(std::option::Option<Foo>), |
596 | parse_quote!(::std::option::Option<Foo>), |
597 | parse_quote!(core::option::Option<Foo>), |
598 | parse_quote!(::core::option::Option<Foo>), |
599 | ] { |
600 | assert_eq!(extract_type_from_option(&s), Some(&ty_foo)); |
601 | } |
602 | } |
603 | } |
604 | |