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}