bon_macros/builder/builder_gen/
models.rsuse super::member::Member;
use super::top_level_config::{DerivesConfig, OnConfig};
use crate::normalization::GenericsNamespace;
use crate::parsing::{ItemSigConfig, SpannedKey};
use crate::util::prelude::*;
use std::borrow::Cow;
pub(super) trait FinishFnBody {
fn generate(&self, ctx: &BuilderGenCtx) -> TokenStream;
}
pub(super) struct AssocMethodReceiverCtx {
pub(super) with_self_keyword: syn::Receiver,
pub(super) without_self_keyword: Box<syn::Type>,
}
pub(super) struct AssocMethodCtx {
pub(super) self_ty: Box<syn::Type>,
pub(super) receiver: Option<AssocMethodReceiverCtx>,
}
pub(super) struct FinishFn {
pub(super) ident: syn::Ident,
pub(super) vis: syn::Visibility,
pub(super) attrs: Vec<syn::Attribute>,
pub(super) unsafety: Option<syn::Token![unsafe]>,
pub(super) asyncness: Option<syn::Token![async]>,
pub(super) must_use: Option<syn::Attribute>,
pub(super) body: Box<dyn FinishFnBody>,
pub(super) output: syn::ReturnType,
}
pub(super) struct FinishFnParams {
pub(super) ident: syn::Ident,
pub(super) vis: Option<syn::Visibility>,
pub(super) attrs: Vec<syn::Attribute>,
pub(super) unsafety: Option<syn::Token![unsafe]>,
pub(super) asyncness: Option<syn::Token![async]>,
pub(super) must_use: Option<syn::Attribute>,
pub(super) body: Box<dyn FinishFnBody>,
pub(super) output: syn::ReturnType,
}
pub(super) struct StartFn {
pub(super) ident: syn::Ident,
pub(super) vis: syn::Visibility,
pub(super) docs: Vec<syn::Attribute>,
pub(super) generics: Option<Generics>,
}
pub(super) struct StartFnParams {
pub(super) ident: syn::Ident,
pub(super) vis: Option<syn::Visibility>,
pub(super) docs: Vec<syn::Attribute>,
pub(super) generics: Option<Generics>,
}
pub(super) struct BuilderType {
pub(super) ident: syn::Ident,
pub(super) vis: syn::Visibility,
pub(super) derives: DerivesConfig,
pub(super) docs: Vec<syn::Attribute>,
}
pub(super) struct BuilderTypeParams {
pub(super) ident: syn::Ident,
pub(super) vis: Option<syn::Visibility>,
pub(super) derives: DerivesConfig,
pub(super) docs: Option<Vec<syn::Attribute>>,
}
pub(super) struct StateMod {
pub(super) ident: syn::Ident,
pub(super) vis: syn::Visibility,
pub(super) vis_child: syn::Visibility,
pub(super) vis_child_child: syn::Visibility,
pub(super) docs: Vec<syn::Attribute>,
}
pub(super) struct Generics {
pub(super) where_clause: Option<syn::WhereClause>,
pub(super) decl_with_defaults: Vec<syn::GenericParam>,
pub(super) decl_without_defaults: Vec<syn::GenericParam>,
pub(super) args: Vec<syn::GenericArgument>,
}
pub(crate) struct BuilderGenCtx {
pub(super) bon: syn::Path,
pub(super) state_var: syn::Ident,
pub(super) members: Vec<Member>,
pub(super) allow_attrs: Vec<syn::Attribute>,
pub(super) on: Vec<OnConfig>,
pub(super) generics: Generics,
pub(super) assoc_method_ctx: Option<AssocMethodCtx>,
pub(super) builder_type: BuilderType,
pub(super) state_mod: StateMod,
pub(super) start_fn: StartFn,
pub(super) finish_fn: FinishFn,
}
pub(super) struct BuilderGenCtxParams<'a> {
pub(crate) bon: Option<syn::Path>,
pub(super) namespace: Cow<'a, GenericsNamespace>,
pub(super) members: Vec<Member>,
pub(super) allow_attrs: Vec<syn::Attribute>,
pub(super) on: Vec<OnConfig>,
pub(super) orig_item_vis: syn::Visibility,
pub(super) generics: Generics,
pub(super) assoc_method_ctx: Option<AssocMethodCtx>,
pub(super) builder_type: BuilderTypeParams,
pub(super) state_mod: ItemSigConfig,
pub(super) start_fn: StartFnParams,
pub(super) finish_fn: FinishFnParams,
}
impl BuilderGenCtx {
pub(super) fn new(params: BuilderGenCtxParams<'_>) -> Result<Self> {
let BuilderGenCtxParams {
bon,
namespace,
members,
allow_attrs,
on,
generics,
orig_item_vis,
assoc_method_ctx,
builder_type,
state_mod,
start_fn,
finish_fn,
} = params;
let builder_type = BuilderType {
ident: builder_type.ident,
vis: builder_type.vis.unwrap_or(orig_item_vis),
derives: builder_type.derives,
docs: builder_type.docs.unwrap_or_else(|| {
let doc = format!(
"Use builder syntax to set the inputs and finish with [`{0}()`](Self::{0}()).",
finish_fn.ident
);
vec![syn::parse_quote! {
#[doc = #doc]
}]
}),
};
let state_mod = {
let is_ident_overridden = state_mod.name.is_some();
let ident = state_mod
.name
.map(SpannedKey::into_value)
.unwrap_or_else(|| builder_type.ident.pascal_to_snake_case());
if builder_type.ident == ident {
if is_ident_overridden {
bail!(
&ident,
"the builder module name must be different from the builder type name"
)
}
bail!(
&builder_type.ident,
"couldn't infer the builder module name that doesn't conflict with \
the builder type name; by default, the builder module name is set \
to a snake_case equivalent of the builder type name; the snake_case \
conversion doesn't produce a different name for this builder type \
name; consider using PascalCase for the builder type name or specify \
a separate name for the builder module explicitly via \
`#[builder(state_mod = {{new_name}})]`"
);
}
let vis = state_mod
.vis
.map(SpannedKey::into_value)
.unwrap_or_else(|| syn::Visibility::Inherited);
let vis_child = builder_type.vis.clone().into_equivalent_in_child_module()?;
let vis_child_child = vis_child.clone().into_equivalent_in_child_module()?;
StateMod {
vis,
vis_child,
vis_child_child,
ident,
docs: state_mod
.docs
.map(SpannedKey::into_value)
.unwrap_or_else(|| {
let docs = format!(
"Tools for manipulating the type state of [`{}`].\n\
\n\
See the [detailed guide](https://bon-rs.com/guide/typestate-api) \
that describes how all the pieces here fit together.",
builder_type.ident
);
vec![syn::parse_quote!(#[doc = #docs])]
}),
}
};
let start_fn = StartFn {
ident: start_fn.ident,
vis: start_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
docs: start_fn.docs,
generics: start_fn.generics,
};
let finish_fn = FinishFn {
ident: finish_fn.ident,
vis: finish_fn.vis.unwrap_or_else(|| builder_type.vis.clone()),
attrs: finish_fn.attrs,
unsafety: finish_fn.unsafety,
asyncness: finish_fn.asyncness,
must_use: finish_fn.must_use,
body: finish_fn.body,
output: finish_fn.output,
};
let state_var = {
let possible_names = ["S", "State", "BuilderState"];
possible_names
.iter()
.find(|&&candidate| !namespace.idents.contains(candidate))
.map(|&name| syn::Ident::new(name, Span::call_site()))
.unwrap_or_else(|| namespace.unique_ident(format!("{}_", possible_names[0])))
};
Ok(Self {
bon: bon.unwrap_or_else(|| syn::parse_quote!(::bon)),
state_var,
members,
allow_attrs,
on,
generics,
assoc_method_ctx,
builder_type,
state_mod,
start_fn,
finish_fn,
})
}
}
impl Generics {
pub(super) fn new(
decl_with_defaults: Vec<syn::GenericParam>,
where_clause: Option<syn::WhereClause>,
) -> Self {
let decl_without_defaults = decl_with_defaults
.iter()
.cloned()
.map(|mut param| {
match &mut param {
syn::GenericParam::Type(param) => {
param.default = None;
}
syn::GenericParam::Const(param) => {
param.default = None;
}
syn::GenericParam::Lifetime(_) => {}
}
param
})
.collect();
let args = decl_with_defaults
.iter()
.map(syn::GenericParam::to_generic_argument)
.collect();
Self {
where_clause,
decl_with_defaults,
decl_without_defaults,
args,
}
}
pub(super) fn where_clause_predicates(&self) -> impl Iterator<Item = &syn::WherePredicate> {
self.where_clause
.as_ref()
.into_iter()
.flat_map(|clause| &clause.predicates)
}
}