bon_macros/builder/
item_impl.rsuse super::builder_gen::input_fn::{FnInputCtx, FnInputCtxParams, ImplCtx};
use super::builder_gen::TopLevelConfig;
use crate::normalization::{GenericsNamespace, SyntaxVariant};
use crate::util::prelude::*;
use darling::ast::NestedMeta;
use darling::FromMeta;
use std::rc::Rc;
use syn::visit::Visit;
use syn::visit_mut::VisitMut;
#[derive(FromMeta)]
pub(crate) struct ImplInputParams {
#[darling(rename = "crate", default, map = Some, with = crate::parsing::parse_bon_crate_path)]
bon: Option<syn::Path>,
}
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn generate(
impl_params: ImplInputParams,
mut orig_impl_block: syn::ItemImpl,
) -> Result<TokenStream> {
let mut namespace = GenericsNamespace::default();
namespace.visit_item_impl(&orig_impl_block);
if let Some((_, trait_path, _)) = &orig_impl_block.trait_ {
bail!(trait_path, "Impls of traits are not supported yet");
}
let (builder_fns, other_items): (Vec<_>, Vec<_>) =
orig_impl_block.items.into_iter().partition(|item| {
let fn_item = match item {
syn::ImplItem::Fn(fn_item) => fn_item,
_ => return false,
};
fn_item
.attrs
.iter()
.any(|attr| attr.path().is_ident("builder"))
});
if builder_fns.is_empty() {
bail!(
&Span::call_site(),
"there are no #[builder] functions in the impl block, so there is no \
need for a #[bon] attribute here"
);
}
orig_impl_block.items = builder_fns;
let mut norm_impl_block = orig_impl_block.clone();
crate::normalization::NormalizeLifetimes::new(&namespace)
.visit_item_impl_mut(&mut norm_impl_block);
crate::normalization::NormalizeImplTraits::new(&namespace)
.visit_item_impl_mut(&mut norm_impl_block);
let mut norm_selfful_impl_block = norm_impl_block.clone();
crate::normalization::NormalizeSelfTy {
self_ty: &norm_impl_block.self_ty.clone(),
}
.visit_item_impl_mut(&mut norm_impl_block);
let impl_ctx = Rc::new(ImplCtx {
self_ty: norm_impl_block.self_ty,
generics: norm_impl_block.generics,
allow_attrs: norm_impl_block
.attrs
.iter()
.filter_map(syn::Attribute::to_allow)
.collect(),
});
let outputs = orig_impl_block
.items
.into_iter()
.zip(norm_impl_block.items)
.map(|(orig_item, norm_item)| {
let norm_fn = match norm_item {
syn::ImplItem::Fn(norm_fn) => norm_fn,
_ => unreachable!(),
};
let orig_fn = match orig_item {
syn::ImplItem::Fn(orig_fn) => orig_fn,
_ => unreachable!(),
};
let norm_fn = conv_impl_item_fn_into_fn_item(norm_fn)?;
let orig_fn = conv_impl_item_fn_into_fn_item(orig_fn)?;
let meta = orig_fn
.attrs
.iter()
.filter(|attr| attr.path().is_ident("builder"))
.map(|attr| {
if let syn::Meta::List(_) = attr.meta {
crate::parsing::require_non_empty_paren_meta_list_or_name_value(
&attr.meta,
)?;
}
let meta_list = darling::util::parse_attribute_to_meta_list(attr)?;
NestedMeta::parse_meta_list(meta_list.tokens).map_err(Into::into)
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect::<Vec<_>>();
let mut config = TopLevelConfig::parse_for_fn(&meta)?;
if let Some(bon) = config.bon {
bail!(
&bon,
"`crate` parameter should be specified via `#[bon(crate = path::to::bon)]` \
when impl block syntax is used; no need to specify it in the method's \
`#[builder]` attribute"
);
}
config.bon.clone_from(&impl_params.bon);
let fn_item = SyntaxVariant {
orig: orig_fn,
norm: norm_fn,
};
let ctx = FnInputCtx::new(FnInputCtxParams {
namespace: &namespace,
fn_item,
impl_ctx: Some(impl_ctx.clone()),
config,
});
Result::<_>::Ok((ctx.adapted_fn()?, ctx.into_builder_gen_ctx()?.output()?))
})
.collect::<Result<Vec<_>>>()?;
let new_impl_items = outputs.iter().flat_map(|(adapted_fn, output)| {
let start_fn = &output.start_fn;
[syn::parse_quote!(#adapted_fn), syn::parse_quote!(#start_fn)]
});
norm_selfful_impl_block.items = other_items;
norm_selfful_impl_block.items.extend(new_impl_items);
let other_items = outputs.iter().map(|(_, output)| &output.other_items);
Ok(quote! {
#norm_selfful_impl_block
#(#other_items)*
})
}
fn conv_impl_item_fn_into_fn_item(func: syn::ImplItemFn) -> Result<syn::ItemFn> {
let syn::ImplItemFn {
attrs,
vis,
defaultness,
sig,
block,
} = func;
if let Some(defaultness) = &defaultness {
bail!(defaultness, "Default functions are not supported yet");
}
Ok(syn::ItemFn {
attrs,
vis,
sig,
block: Box::new(block),
})
}