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#[derive(Debug, Clone)]
39pub struct Setter<'a> {
40 pub crate_root: &'a syn::Path,
42 pub setter_enabled: bool,
44 pub try_setter: bool,
46 pub visibility: Cow<'a, syn::Visibility>,
48 pub pattern: BuilderPattern,
50 pub attrs: &'a [syn::Attribute],
52 pub ident: syn::Ident,
54 pub field_ident: &'a syn::Ident,
56 pub field_type: BuilderFieldType<'a>,
60 pub generic_into: bool,
62 pub strip_option: bool,
65 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 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 let get_initialized_collection = if stripped_option {
180 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 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
225fn 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
230fn 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 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 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#[doc(hidden)]
280#[macro_export]
281macro_rules! default_setter {
282 () => {
283 Setter {
284 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 #[test]
500 fn full() {
501 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}