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::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 meta_list = darling::ast::NestedMeta::parse_meta_list(input.config.clone())?;
58            let config = TopLevelConfig::parse_for_fn(&meta_list)?;
59
60            item_fn::generate(config, item_fn, &namespace)?
61        }
62        syn::Item::Struct(struct_item) => {
63            bail!(
64                &struct_item.struct_token,
65                "to generate a builder for a struct, use `#[derive(bon::Builder)]` instead; \
66                 `#[bon::builder]` syntax is supported only for functions starting with bon v3"
67            )
68        }
69        _ => bail!(
70            &Span::call_site(),
71            "only `fn` items are supported by the `#[bon::builder]` attribute"
72        ),
73    };
74
75    let output = [generate_completion_triggers(input.config), main_output].concat();
76
77    Ok(output)
78}
79
80fn generate_completion_triggers(params: TokenStream) -> TokenStream {
81    let meta = util::ide::parse_comma_separated_meta
82        .parse2(params)
83        .unwrap_or_default();
84
85    util::ide::generate_completion_triggers(meta)
86}