bon_macros/builder/
mod.rs

1mod 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}