bon_macros/builder/
mod.rs1mod builder_gen;
2
3pub(crate) mod item_impl;
4
5mod item_fn;
6mod item_struct;
7
8use crate::normalization::{ExpandCfg, Expansion, GenericsNamespace};
9use crate::util;
10use crate::util::prelude::*;
11use builder_gen::TopLevelConfig;
12use syn::parse::Parser;
13use syn::visit::Visit;
14
15pub(crate) fn generate_from_derive(item: TokenStream) -> TokenStream {
16 try_generate_from_derive(item).unwrap_or_else(Error::write_errors)
17}
18
19fn try_generate_from_derive(item: TokenStream) -> Result<TokenStream> {
20 match syn::parse2(item)? {
21 syn::Item::Struct(item_struct) => item_struct::generate(item_struct),
22 _ => bail!(
23 &Span::call_site(),
24 "only `struct` items are supported by the `#[derive(bon::Builder)]` attribute"
25 ),
26 }
27}
28
29pub(crate) fn generate_from_attr(params: TokenStream, item: TokenStream) -> TokenStream {
30 crate::error::handle_errors(item.clone(), || {
31 try_generate_from_attr(params.clone(), item)
32 })
33 .unwrap_or_else(|fallback| [generate_completion_triggers(params), fallback].concat())
34}
35
36fn try_generate_from_attr(params: TokenStream, item: TokenStream) -> Result<TokenStream> {
37 let item = syn::parse2(item)?;
38
39 let ctx = ExpandCfg {
40 current_macro: format_ident!("builder"),
41 config: params,
42 item,
43 };
44
45 let input = match ctx.expand_cfg()? {
46 Expansion::Expanded(input) => input,
47 Expansion::Recurse(output) => return Ok(output),
48 };
49
50 let main_output = match *input.item {
51 syn::Item::Fn(item_fn) => {
52 let mut namespace = GenericsNamespace::default();
53
54 namespace.visit_token_stream(input.config.clone());
55 namespace.visit_item_fn(&item_fn);
56
57 let config = TopLevelConfig::parse_for_fn(&item_fn, Some(input.config.clone()))?;
58
59 item_fn::generate(config, item_fn, &namespace)?
60 }
61 syn::Item::Struct(struct_item) => {
62 bail!(
63 &struct_item.struct_token,
64 "to generate a builder for a struct, use `#[derive(bon::Builder)]` instead; \
65 `#[bon::builder]` syntax is supported only for functions starting with bon v3"
66 )
67 }
68 _ => bail!(
69 &Span::call_site(),
70 "only `fn` items are supported by the `#[bon::builder]` attribute"
71 ),
72 };
73
74 let output = [generate_completion_triggers(input.config), main_output].concat();
75
76 Ok(output)
77}
78
79fn generate_completion_triggers(params: TokenStream) -> TokenStream {
80 let meta = util::ide::parse_comma_separated_meta
81 .parse2(params)
82 .unwrap_or_default();
83
84 util::ide::generate_completion_triggers(meta)
85}