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::parsing::BonCratePath;
5use crate::util::prelude::*;
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)]
16 bon: BonCratePath,
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 mut config = TopLevelConfig::parse_for_fn(&orig_fn, None)?;
114
115 if let BonCratePath::Explicit(path) = config.bon {
116 bail!(
117 &path,
118 "`crate` parameter should be specified via `#[bon(crate = path::to::bon)]` \
119 when impl block syntax is used; no need to specify it in the method's \
120 `#[builder]` attribute"
121 );
122 }
123
124 config.bon.clone_from(&impl_params.bon);
125
126 let fn_item = SyntaxVariant {
127 orig: orig_fn,
128 norm: norm_fn,
129 };
130
131 let ctx = FnInputCtx::new(FnInputCtxParams {
132 namespace: &namespace,
133 fn_item,
134 impl_ctx: Some(impl_ctx.clone()),
135 config,
136 })?;
137
138 let adapted_fn = ctx.adapted_fn()?;
139 let warnings = ctx.warnings();
140
141 let mut output = ctx.into_builder_gen_ctx()?.output()?;
142 output.other_items.extend(warnings);
143
144 Result::<_>::Ok((adapted_fn, output))
145 })
146 .collect::<Result<Vec<_>>>()?;
147
148 let new_impl_items = outputs.iter().flat_map(|(adapted_fn, output)| {
149 let start_fn = &output.start_fn;
150 [syn::parse_quote!(#adapted_fn), syn::parse_quote!(#start_fn)]
151 });
152
153 norm_selfful_impl_block.items = other_items;
154 norm_selfful_impl_block.items.extend(new_impl_items);
155
156 let other_items = outputs.iter().map(|(_, output)| &output.other_items);
157
158 Ok(quote! {
159 #norm_selfful_impl_block
166
167 #(#other_items)*
168 })
169}
170
171fn conv_impl_item_fn_into_fn_item(func: syn::ImplItemFn) -> Result<syn::ItemFn> {
172 let syn::ImplItemFn {
173 attrs,
174 vis,
175 defaultness,
176 sig,
177 block,
178 } = func;
179
180 if let Some(defaultness) = &defaultness {
181 bail!(defaultness, "Default functions are not supported yet");
182 }
183
184 Ok(syn::ItemFn {
185 attrs,
186 vis,
187 sig,
188 block: Box::new(block),
189 })
190}