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