1#![allow(clippy::useless_let_if_seq)]
2use std::borrow::Cow;
3
4use proc_macro2::{Span, TokenStream};
5use quote::{ToTokens, TokenStreamExt};
6use syn;
7
8use BuilderFieldType;
9use BuilderPattern;
10use DeprecationNotes;
11use 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)]
43pub 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
75impl<'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`
232fn 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.
241fn 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]
287macro_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)]
310mod 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