bon_macros/builder/
item_impl.rs1use super::builder_gen::input_fn::{FnInputCtx, FnInputCtxParams, ImplCtx};
2use super::builder_gen::TopLevelConfig;
3use crate::normalization::{GenericsNamespace, SyntaxVariant};
4use crate::util::prelude::*;
5use darling::ast::NestedMeta;
6use darling::FromMeta;
7use std::rc::Rc;
8use syn::visit::Visit;
9use syn::visit_mut::VisitMut;
10
11#[derive(FromMeta)]
12pub(crate) struct ImplInputParams {
13 #[darling(rename = "crate", default, map = Some, with = crate::parsing::parse_bon_crate_path)]
16 bon: Option<syn::Path>,
17}
18
19#[allow(clippy::needless_pass_by_value)]
21pub(crate) fn generate(
22 impl_params: ImplInputParams,
23 mut orig_impl_block: syn::ItemImpl,
24) -> Result<TokenStream> {
25 let mut namespace = GenericsNamespace::default();
26 namespace.visit_item_impl(&orig_impl_block);
27
28 if let Some((_, trait_path, _)) = &orig_impl_block.trait_ {
29 bail!(trait_path, "Impls of traits are not supported yet");
30 }
31
32 let (builder_fns, other_items): (Vec<_>, Vec<_>) =
33 orig_impl_block.items.into_iter().partition(|item| {
34 let fn_item = match item {
35 syn::ImplItem::Fn(fn_item) => fn_item,
36 _ => return false,
37 };
38
39 fn_item
40 .attrs
41 .iter()
42 .any(|attr| attr.path().is_ident("builder"))
43 });
44
45 if builder_fns.is_empty() {
46 bail!(
47 &Span::call_site(),
48 "there are no #[builder] functions in the impl block, so there is no \
49 need for a #[bon] attribute here"
50 );
51 }
52
53 orig_impl_block.items = builder_fns;
54
55 let mut norm_impl_block = orig_impl_block.clone();
68
69 crate::normalization::NormalizeLifetimes::new(&namespace)
70 .visit_item_impl_mut(&mut norm_impl_block);
71
72 crate::normalization::NormalizeImplTraits::new(&namespace)
73 .visit_item_impl_mut(&mut norm_impl_block);
74
75 let mut norm_selfful_impl_block = norm_impl_block.clone();
80
81 crate::normalization::NormalizeSelfTy {
82 self_ty: &norm_impl_block.self_ty.clone(),
83 }
84 .visit_item_impl_mut(&mut norm_impl_block);
85
86 let impl_ctx = Rc::new(ImplCtx {
87 self_ty: norm_impl_block.self_ty,
88 generics: norm_impl_block.generics,
89 allow_attrs: norm_impl_block
90 .attrs
91 .iter()
92 .filter_map(syn::Attribute::to_allow)
93 .collect(),
94 });
95
96 let outputs = orig_impl_block
97 .items
98 .into_iter()
99 .zip(norm_impl_block.items)
100 .map(|(orig_item, norm_item)| {
101 let norm_fn = match norm_item {
102 syn::ImplItem::Fn(norm_fn) => norm_fn,
103 _ => unreachable!(),
104 };
105 let orig_fn = match orig_item {
106 syn::ImplItem::Fn(orig_fn) => orig_fn,
107 _ => unreachable!(),
108 };
109
110 let norm_fn = conv_impl_item_fn_into_fn_item(norm_fn)?;
111 let orig_fn = conv_impl_item_fn_into_fn_item(orig_fn)?;
112
113 let meta = orig_fn
114 .attrs
115 .iter()
116 .filter(|attr| attr.path().is_ident("builder"))
117 .map(|attr| {
118 if let syn::Meta::List(_) = attr.meta {
119 crate::parsing::require_non_empty_paren_meta_list_or_name_value(
120 &attr.meta,
121 )?;
122 }
123 let meta_list = darling::util::parse_attribute_to_meta_list(attr)?;
124 NestedMeta::parse_meta_list(meta_list.tokens).map_err(Into::into)
125 })
126 .collect::<Result<Vec<_>>>()?
127 .into_iter()
128 .flatten()
129 .collect::<Vec<_>>();
130
131 let mut config = TopLevelConfig::parse_for_fn(&meta)?;
132
133 if let Some(bon) = config.bon {
134 bail!(
135 &bon,
136 "`crate` parameter should be specified via `#[bon(crate = path::to::bon)]` \
137 when impl block syntax is used; no need to specify it in the method's \
138 `#[builder]` attribute"
139 );
140 }
141
142 config.bon.clone_from(&impl_params.bon);
143
144 let fn_item = SyntaxVariant {
145 orig: orig_fn,
146 norm: norm_fn,
147 };
148
149 let ctx = FnInputCtx::new(FnInputCtxParams {
150 namespace: &namespace,
151 fn_item,
152 impl_ctx: Some(impl_ctx.clone()),
153 config,
154 });
155
156 Result::<_>::Ok((ctx.adapted_fn()?, ctx.into_builder_gen_ctx()?.output()?))
157 })
158 .collect::<Result<Vec<_>>>()?;
159
160 let new_impl_items = outputs.iter().flat_map(|(adapted_fn, output)| {
161 let start_fn = &output.start_fn;
162 [syn::parse_quote!(#adapted_fn), syn::parse_quote!(#start_fn)]
163 });
164
165 norm_selfful_impl_block.items = other_items;
166 norm_selfful_impl_block.items.extend(new_impl_items);
167
168 let other_items = outputs.iter().map(|(_, output)| &output.other_items);
169
170 Ok(quote! {
171 #norm_selfful_impl_block
178
179 #(#other_items)*
180 })
181}
182
183fn conv_impl_item_fn_into_fn_item(func: syn::ImplItemFn) -> Result<syn::ItemFn> {
184 let syn::ImplItemFn {
185 attrs,
186 vis,
187 defaultness,
188 sig,
189 block,
190 } = func;
191
192 if let Some(defaultness) = &defaultness {
193 bail!(defaultness, "Default functions are not supported yet");
194 }
195
196 Ok(syn::ItemFn {
197 attrs,
198 vis,
199 sig,
200 block: Box::new(block),
201 })
202}