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 | |
7 | use 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)] |
39 | pub 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 | |
69 | impl<'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` |
226 | fn 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. |
235 | fn 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 ] |
281 | macro_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)] |
303 | mod 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 | } |
631 | |