derive_builder_core/
setter.rs

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