bon_macros/builder/builder_gen/setters/
mod.rs1use super::member::{SetterClosure, WithConfig};
2use super::{BuilderGenCtx, NamedMember};
3use crate::parsing::ItemSigConfig;
4use crate::util::prelude::*;
5use std::iter;
6
7pub(crate) struct SettersCtx<'a> {
8 base: &'a BuilderGenCtx,
9 member: &'a NamedMember,
10}
11
12impl<'a> SettersCtx<'a> {
13 pub(crate) fn new(base: &'a BuilderGenCtx, member: &'a NamedMember) -> Self {
14 Self { base, member }
15 }
16
17 pub(crate) fn setter_methods(&self) -> Result<TokenStream> {
18 match SettersItems::new(self) {
19 SettersItems::Required(item) => self.setter_for_required_member(item),
20 SettersItems::Optional(setters) => self.setters_for_optional_member(setters),
21 }
22 }
23
24 fn setter_for_required_member(&self, item: SetterItem) -> Result<TokenStream> {
25 let inputs;
26 let expr;
27
28 let member_type = self.member.ty.norm.as_ref();
29
30 if let Some(with) = &self.member.config.with {
31 inputs = self.underlying_inputs_from_with(with)?;
32 expr = self.member_expr_from_with(with);
33 } else if self.member.config.into.is_present() {
34 inputs = vec![(
35 pat_ident("value"),
36 syn::parse_quote!(impl Into<#member_type>),
37 )];
38 expr = quote!(Into::into(value));
39 } else {
40 inputs = vec![(pat_ident("value"), member_type.clone())];
41 expr = quote!(value);
42 }
43
44 let body = SetterBody::SetMember {
45 expr: quote!(::core::option::Option::Some(#expr)),
46 };
47
48 Ok(self.setter_method(Setter {
49 item,
50 imp: SetterImpl { inputs, body },
51 }))
52 }
53
54 fn setters_for_optional_member(&self, items: OptionalSettersItems) -> Result<TokenStream> {
55 if let Some(with) = &self.member.config.with {
56 return self.setters_for_optional_member_having_with(with, items);
57 }
58
59 let underlying_ty = self.member.underlying_norm_ty();
60 let underlying_ty: syn::Type = if self.member.config.into.is_present() {
61 syn::parse_quote!(impl Into<#underlying_ty>)
62 } else {
63 underlying_ty.clone()
64 };
65
66 let some_fn = Setter {
67 item: items.some_fn,
68 imp: SetterImpl {
69 inputs: vec![(pat_ident("value"), underlying_ty.clone())],
70 body: SetterBody::Forward {
71 body: {
72 let option_fn_name = &items.option_fn.name;
73 quote! {
74 self.#option_fn_name(Some(value))
75 }
76 },
77 },
78 },
79 };
80
81 let option_fn = Setter {
82 item: items.option_fn,
83 imp: SetterImpl {
84 inputs: vec![(
85 pat_ident("value"),
86 syn::parse_quote!(Option<#underlying_ty>),
87 )],
88 body: SetterBody::SetMember {
89 expr: if self.member.config.into.is_present() {
90 quote! {
91 Option::map(value, Into::into)
92 }
93 } else {
94 quote!(value)
95 },
96 },
97 },
98 };
99
100 Ok([self.setter_method(some_fn), self.setter_method(option_fn)].concat())
101 }
102
103 fn setters_for_optional_member_having_with(
104 &self,
105 with: &WithConfig,
106 items: OptionalSettersItems,
107 ) -> Result<TokenStream> {
108 let inputs = self.underlying_inputs_from_with(with)?;
109
110 let idents = inputs.iter().map(|(pat, _)| &pat.ident);
111
112 let tuple_if_many = |val: TokenStream| -> TokenStream {
115 if inputs.len() == 1 {
116 val
117 } else {
118 quote!((#val))
119 }
120 };
121
122 let ident_maybe_tuple = tuple_if_many(quote!( #( #idents ),* ));
123
124 let some_fn = Setter {
125 item: items.some_fn,
126 imp: SetterImpl {
127 inputs: inputs.clone(),
128 body: SetterBody::Forward {
129 body: {
130 let option_fn_name = &items.option_fn.name;
131 quote! {
132 self.#option_fn_name(Some(#ident_maybe_tuple))
133 }
134 },
135 },
136 },
137 };
138
139 let option_fn_impl = SetterImpl {
140 inputs: {
141 let input_types = inputs.iter().map(|(_, ty)| ty);
142 let input_types = tuple_if_many(quote!(#( #input_types, )*));
143
144 vec![(pat_ident("value"), syn::parse_quote!(Option<#input_types>))]
145 },
146 body: SetterBody::SetMember {
147 expr: {
148 let expr = self.member_expr_from_with(with);
149 quote! {
150 match value {
153 Some(#ident_maybe_tuple) => Some(#expr),
154 None => None,
155 }
156 }
157 },
158 },
159 };
160
161 let option_fn = Setter {
162 item: items.option_fn,
163 imp: option_fn_impl,
164 };
165
166 Ok([self.setter_method(some_fn), self.setter_method(option_fn)].concat())
167 }
168
169 fn underlying_inputs_from_with(
179 &self,
180 with: &WithConfig,
181 ) -> Result<Vec<(syn::PatIdent, syn::Type)>> {
182 let inputs = match with {
183 WithConfig::Closure(closure) => closure
184 .inputs
185 .iter()
186 .map(|input| (input.pat.clone(), (*input.ty).clone()))
187 .collect(),
188 WithConfig::Some(some) => {
189 let input_ty = self
190 .member
191 .underlying_norm_ty()
192 .option_type_param()
193 .ok_or_else(|| {
194 if self.member.ty.norm.is_option() {
195 err!(
196 some,
197 "the underlying type of this member is not `Option`; \
198 by default, members of type `Option` are optional and their \
199 'underlying type' is the type under the `Option`; \
200 you might be missing #[builder(required)]` annotation \
201 for this member"
202 )
203 } else {
204 err!(
205 &self.member.underlying_norm_ty(),
206 "`with = Some` only works for members with the underlying \
207 type of `Option`;"
208 )
209 }
210 })?;
211
212 vec![(pat_ident("value"), input_ty.clone())]
213 }
214 WithConfig::FromIter(from_iter) => {
215 let collection_ty = self.member.underlying_norm_ty();
216
217 let well_known_single_arg_suffixes = ["Vec", "Set", "Deque", "Heap", "List"];
218
219 let err = || {
220 let mut from_iter_path = quote!(#from_iter).to_string();
221 from_iter_path.retain(|c| !c.is_whitespace());
222
223 err!(
224 collection_ty,
225 "the underlying type of this member is not a known collection type; \
226 only a collection type that matches the following patterns will be \
227 accepted by `#[builder(with = {from_iter_path})], where * at \
228 the beginning means the collection type may start with any prefix:\n\
229 - *Map<K, V>\n\
230 {}",
231 well_known_single_arg_suffixes
232 .iter()
233 .map(|suffix| { format!("- *{suffix}<T>") })
234 .join("\n")
235 )
236 };
237
238 let path = collection_ty.as_path_no_qself().ok_or_else(err)?;
239
240 let last_segment = path.segments.last().ok_or_else(err)?;
241 let args = match &last_segment.arguments {
242 syn::PathArguments::AngleBracketed(args) => &args.args,
243 _ => return Err(err()),
244 };
245
246 let last_segment_ident_str = last_segment.ident.to_string();
247
248 let item_ty = if well_known_single_arg_suffixes
249 .iter()
250 .any(|suffix| last_segment_ident_str.ends_with(suffix))
251 {
252 if args.is_empty() {
255 return Err(err());
256 }
257
258 let arg = args.first().ok_or_else(err)?;
259
260 quote!(#arg)
261 } else if last_segment_ident_str.ends_with("Map") {
262 if args.len() < 2 {
265 return Err(err());
266 }
267
268 let mut args = args.iter();
269 let key = args.next().ok_or_else(err)?;
270 let value = args.next().ok_or_else(err)?;
271
272 quote!((#key, #value))
273 } else {
274 return Err(err());
275 };
276
277 vec![(
278 pat_ident("iter"),
279 syn::parse_quote!(impl IntoIterator<Item = #item_ty>),
280 )]
281 }
282 };
283
284 Ok(inputs)
285 }
286
287 fn member_expr_from_with(&self, with: &WithConfig) -> TokenStream {
288 match with {
289 WithConfig::Closure(closure) => self.member_expr_from_with_closure(with, closure),
290 WithConfig::Some(some) => quote!(#some(value)),
291 WithConfig::FromIter(from_iter) => quote!(#from_iter(iter)),
292 }
293 }
294
295 fn member_expr_from_with_closure(
296 &self,
297 with: &WithConfig,
298 closure: &SetterClosure,
299 ) -> TokenStream {
300 let body = &closure.body;
301
302 let ty = self.member.underlying_norm_ty().to_token_stream();
303
304 let output = Self::maybe_wrap_in_result(with, ty);
305
306 if self.base.const_.is_some() {
311 let body = quote! {{
312 let value: #output = #body;
313 value
314 }};
315
316 if closure.output.is_none() {
317 return body;
318 }
319
320 return quote! {
321 match #body {
322 Ok(value) => value,
323 Err(err) => return Err(err),
324 }
325 };
326 }
327
328 let body = if matches!(body.as_ref(), syn::Expr::Block(_)) {
330 body.to_token_stream()
331 } else {
332 quote!({ #body })
333 };
334
335 let question_mark = closure
336 .output
337 .is_some()
338 .then(|| syn::Token));
339
340 quote! {
341 (move || -> #output #body)() #question_mark
342 }
343 }
344
345 fn maybe_wrap_in_result(with: &WithConfig, ty: TokenStream) -> TokenStream {
346 let closure = match with {
347 WithConfig::Closure(closure) => closure,
348 _ => return ty,
349 };
350
351 let output = match closure.output.as_ref() {
352 Some(output) => output,
353 None => return ty,
354 };
355 let result_path = &output.result_path;
356 let err_ty = output.err_ty.iter();
357 quote! {
358 #result_path< #ty #(, #err_ty )* >
359 }
360 }
361
362 fn setter_method(&self, setter: Setter) -> TokenStream {
363 let Setter { item, imp } = setter;
364
365 let maybe_mut = match imp.body {
366 SetterBody::Forward { .. } => None,
367 SetterBody::SetMember { .. } => Some(syn::Token)),
368 };
369
370 let body = match imp.body {
371 SetterBody::Forward { body } => body,
372 SetterBody::SetMember { expr } => {
373 let mut output = if !self.member.is_stateful() {
374 quote! {
375 self
376 }
377 } else {
378 let builder_ident = &self.base.builder_type.ident;
379
380 let maybe_receiver_field = self.base.receiver().map(|receiver| {
381 let ident = &receiver.field_ident;
382 quote!(#ident: self.#ident,)
383 });
384
385 let start_fn_args_fields_idents =
386 self.base.start_fn_args().map(|member| &member.ident);
387
388 let custom_fields_idents = self.base.custom_fields().map(|field| &field.ident);
389
390 quote! {
391 #builder_ident {
392 __unsafe_private_phantom: ::core::marker::PhantomData,
393 #( #custom_fields_idents: self.#custom_fields_idents, )*
394 #maybe_receiver_field
395 #( #start_fn_args_fields_idents: self.#start_fn_args_fields_idents, )*
396 __unsafe_private_named: self.__unsafe_private_named,
397 }
398 }
399 };
400
401 let result_output = self
402 .member
403 .config
404 .with
405 .as_ref()
406 .and_then(|with| with.as_closure()?.output.as_ref());
407
408 if let Some(result_output) = result_output {
409 let result_path = &result_output.result_path;
410 output = quote!(#result_path::Ok(#output));
411 }
412
413 let index = &self.member.index;
414 quote! {
415 self.__unsafe_private_named.#index = #expr;
416 #output
417 }
418 }
419 };
420
421 let state_mod = &self.base.state_mod.ident;
422
423 let mut return_type = if !self.member.is_stateful() {
424 quote! { Self }
425 } else {
426 let state_transition = format_ident!("Set{}", self.member.name.pascal_str);
427 let builder_ident = &self.base.builder_type.ident;
428 let generic_args = &self.base.generics.args;
429 let state_var = &self.base.state_var;
430
431 quote! {
432 #builder_ident<#(#generic_args,)* #state_mod::#state_transition<#state_var>>
433 }
434 };
435
436 if let Some(with) = &self.member.config.with {
437 return_type = Self::maybe_wrap_in_result(with, return_type);
438 }
439
440 let where_clause = (!self.member.config.overwritable.is_present()).then(|| {
441 let state_var = &self.base.state_var;
442 let member_pascal = &self.member.name.pascal;
443 quote! {
444 where #state_var::#member_pascal: #state_mod::IsUnset,
445 }
446 });
447
448 let SetterItem { name, vis, docs } = item;
449 let pats = imp.inputs.iter().map(|(pat, _)| pat);
450 let types = imp.inputs.iter().map(|(_, ty)| ty);
451 let const_ = &self.base.const_;
452 let fn_modifiers = self.member.respan(quote!(#vis #const_));
453
454 let self_ = quote!(self);
458
459 quote_spanned! {self.member.span=>
460 #( #docs )*
461 #[allow(
462 clippy::inline_always,
464 clippy::impl_trait_in_params,
469 clippy::missing_const_for_fn,
470 clippy::wrong_self_convention,
474 )]
475 #[inline(always)]
476 #(#fn_modifiers)* fn #name(#maybe_mut #self_, #( #pats: #types ),*) -> #return_type
477 #where_clause
478 {
479 #body
480 }
481 }
482 }
483}
484
485struct Setter {
486 item: SetterItem,
487 imp: SetterImpl,
488}
489
490struct SetterImpl {
491 inputs: Vec<(syn::PatIdent, syn::Type)>,
492 body: SetterBody,
493}
494
495enum SetterBody {
496 Forward { body: TokenStream },
498
499 SetMember { expr: TokenStream },
501}
502
503enum SettersItems {
504 Required(SetterItem),
505 Optional(OptionalSettersItems),
506}
507
508struct OptionalSettersItems {
509 some_fn: SetterItem,
510 option_fn: SetterItem,
511}
512
513struct SetterItem {
514 name: syn::Ident,
515 vis: syn::Visibility,
516 docs: Vec<syn::Attribute>,
517}
518
519impl SettersItems {
520 fn new(ctx: &SettersCtx<'_>) -> Self {
521 let SettersCtx { member, base } = ctx;
522 let builder_type = &base.builder_type;
523
524 let config = member.config.setters.as_ref();
525
526 let common_name = config.and_then(|config| config.name.as_deref());
527 let common_vis = config.and_then(|config| config.vis.as_deref());
528 let common_docs =
529 config.and_then(|config| config.doc.content.as_deref().map(Vec::as_slice));
530
531 let doc = |docs: &str| iter::once(syn::parse_quote!(#[doc = #docs]));
532
533 if member.is_required() {
534 let docs = common_docs.unwrap_or(&member.docs);
535
536 let header = "_**Required.**_\n\n";
537
538 let docs = doc(header).chain(docs.iter().cloned()).collect();
539
540 return Self::Required(SetterItem {
541 name: common_name.unwrap_or(&member.name.snake).clone(),
542 vis: common_vis.unwrap_or(&builder_type.vis).clone(),
543 docs,
544 });
545 }
546
547 let some_fn = config.and_then(|config| config.fns.some_fn.as_deref());
548 let some_fn_name = some_fn
549 .and_then(ItemSigConfig::name)
550 .or(common_name)
551 .unwrap_or(&member.name.snake)
552 .clone();
553
554 let option_fn = config.and_then(|config| config.fns.option_fn.as_deref());
555 let option_fn_name = option_fn
556 .and_then(ItemSigConfig::name)
557 .cloned()
558 .unwrap_or_else(|| {
559 let base_name = common_name.unwrap_or(&member.name.snake);
560 syn::Ident::new(&format!("maybe_{}", base_name.raw_name()), base_name.span())
565 });
566
567 let default = member.config.default.as_deref().and_then(|default| {
568 if let Some(setters) = &member.config.setters {
569 if let Some(default) = &setters.doc.default {
570 if default.skip.is_present() {
571 return None;
572 }
573 }
574 }
575
576 let default = default
577 .clone()
578 .or_else(|| well_known_default(&member.ty.norm))
579 .unwrap_or_else(|| {
580 let ty = &member.ty.norm;
581 syn::parse_quote!(<#ty as Default>::default())
582 });
583
584 let file = syn::parse_quote!(const _: () = #default;);
585 let file = prettyplease::unparse(&file);
586
587 let begin = file.find('=')?;
588 let default = file.get(begin + 1..)?.trim();
589 let default = default.strip_suffix(';')?;
590
591 Some(default.to_owned())
592 });
593
594 let default = default.as_deref();
595
596 let some_fn_docs = some_fn
599 .and_then(ItemSigConfig::docs)
600 .or(common_docs)
601 .unwrap_or(&member.docs);
602
603 let some_fn_docs =
604 optional_setter_docs(default, &some_fn_name, &option_fn_name, some_fn_docs);
605
606 let option_fn_docs = option_fn
607 .and_then(ItemSigConfig::docs)
608 .or(common_docs)
609 .unwrap_or(&member.docs);
610
611 let option_fn_docs =
612 optional_setter_docs(default, &some_fn_name, &option_fn_name, option_fn_docs);
613
614 let some_fn = SetterItem {
615 name: some_fn_name,
616 vis: some_fn
617 .and_then(ItemSigConfig::vis)
618 .or(common_vis)
619 .unwrap_or(&builder_type.vis)
620 .clone(),
621
622 docs: some_fn_docs,
623 };
624
625 let option_fn = config.and_then(|config| config.fns.option_fn.as_deref());
626 let option_fn = SetterItem {
627 name: option_fn_name,
628
629 vis: option_fn
630 .and_then(ItemSigConfig::vis)
631 .or(common_vis)
632 .unwrap_or(&builder_type.vis)
633 .clone(),
634
635 docs: option_fn_docs,
636 };
637
638 Self::Optional(OptionalSettersItems { some_fn, option_fn })
639 }
640}
641
642fn optional_setter_docs(
643 default: Option<&str>,
644 some_fn: &syn::Ident,
645 option_fn: &syn::Ident,
646 doc_comments: &[syn::Attribute],
647) -> Vec<syn::Attribute> {
648 let header = format!(
649 "_**Optional** ([Some](Self::{some_fn}()) / [Option](Self::{option_fn}()) setters)._"
650 );
651
652 let mut attrs = vec![syn::parse_quote!(#[doc = #header])];
653
654 if let Some(default) = default {
655 let sep = if doc_comments.is_empty() { "" } else { "\n\n" };
656
657 if default.contains('\n') || default.len() > 80 {
658 let tail = format!("\n{default}\n````{sep}");
663 attrs.extend([
664 syn::parse_quote!(#[doc = " _**Default:**_\n"]),
665 syn::parse_quote!(#[cfg_attr(doctest, doc = "````no_doctest")]),
666 syn::parse_quote!(#[cfg_attr(not(doctest), doc = "````")]),
667 syn::parse_quote!(#[doc = #tail]),
668 ]);
669 } else {
670 let doc = format!(" _**Default:**_ ```{default}```.{sep}");
671 attrs.push(syn::parse_quote!(#[doc = #doc]));
672 }
673 }
674
675 attrs.extend(doc_comments.iter().cloned());
676 attrs
677}
678
679fn well_known_default(ty: &syn::Type) -> Option<syn::Expr> {
680 let path = match ty {
681 syn::Type::Path(syn::TypePath { path, qself: None }) => path,
682 _ => return None,
683 };
684
685 use syn::parse_quote as pq;
686
687 let ident = path.get_ident()?.to_string();
688
689 let value = match ident.as_str() {
690 "u8" | "u16" | "u32" | "u64" | "u128" | "usize" | "i8" | "i16" | "i32" | "i64" | "i128"
691 | "isize" => pq!(0),
692 "f32" | "f64" => pq!(0.0),
693 "bool" => pq!(false),
694 "char" => pq!('\0'),
695 "String" => pq!(""),
696 _ => return None,
697 };
698
699 Some(value)
700}
701
702fn pat_ident(ident_name: &'static str) -> syn::PatIdent {
705 let ident = syn::Ident::new(ident_name, Span::call_site());
706 let pat: syn::Pat = syn::parse_quote!(#ident);
707 match pat {
708 syn::Pat::Ident(pat_ident) => pat_ident,
709 _ => unreachable!("can't parse something else than PatIdent here: {pat:?}"),
710 }
711}