Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions bon-macros/src/builder/builder_gen/member/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
mod blanket;
mod setter_closure;
mod setters;
mod with;

pub(crate) use blanket::*;
pub(crate) use setter_closure::*;
pub(crate) use setters::*;
pub(crate) use with::*;

use super::MemberOrigin;
use crate::parsing::SpannedKey;
Expand Down Expand Up @@ -52,11 +52,12 @@ pub(crate) struct MemberConfig {
/// this option to see if it's worth it.
pub(crate) overwritable: darling::util::Flag,

/// Customize the setter signature and body with a custom closure. The closure
/// must return the value of the type of the member, or optionally a `Result<_>`
/// type where `_` is used to mark the type of the member. In this case the
/// generated setters will be fallible (they'll propagate the `Result`).
pub(crate) with: Option<SpannedKey<SetterClosure>>,
/// Customize the setter signature and body with a custom closure or a well-known
/// function. The closure/function must return the value of the type of the member,
/// or optionally a `Result<_>` type where `_` is used to mark the type of
/// the member. In this case the generated setters will be fallible
/// (they'll propagate the `Result`).
pub(crate) with: Option<SpannedKey<WithConfig>>,

/// Disables the special handling for a member of type `Option<T>`. The
/// member no longer has the default of `None`. It also becomes a required
Expand Down
70 changes: 70 additions & 0 deletions bon-macros/src/builder/builder_gen/member/config/with/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
mod closure;

pub(crate) use closure::*;

use crate::util::prelude::*;
use darling::FromMeta;

#[derive(Debug)]
pub(crate) enum WithConfig {
/// Closure syntax e.g. `#[builder(with = |param: Type| body)]`
Closure(SetterClosure),

/// Well-known path [`Option::Some`]
Some(syn::Path),

/// Well-known path [`FromIterator::from_iter`] or `<_>::from_iter`
FromIter(syn::ExprPath),
}

impl WithConfig {
pub(crate) fn as_closure(&self) -> Option<&SetterClosure> {
match self {
Self::Closure(closure) => Some(closure),
_ => None,
}
}
}

impl FromMeta for WithConfig {
fn from_meta(meta: &syn::Meta) -> darling::Result<Self> {
let err = || {
err!(
meta,
"expected a closure e.g. `#[builder(with = |param: T| expression)]` or \
a well-known function path which could be one of:\n\
- #[builder(with = Some)]\n\
- #[builder(with = FromIterator::from_iter)]\n\
- #[builder(with = <_>::from_iter)] (same as above, but shorter)",
)
};

let name_val = match meta {
syn::Meta::NameValue(meta) => meta,
_ => return Err(err()),
};

if let syn::Expr::Closure(_) = name_val.value {
return SetterClosure::from_meta(meta).map(Self::Closure);
}

let path = match &name_val.value {
syn::Expr::Path(path) => path,
_ => return Err(err()),
};

crate::parsing::reject_syntax("attribute", &path.attrs.first())?;

if *path == syn::parse_quote!(Some) {
return Ok(Self::Some(path.path.clone()));
}

if *path == syn::parse_quote!(FromIterator::from_iter)
|| *path == syn::parse_quote!(<_>::from_iter)
{
return Ok(Self::FromIter(path.clone()));
}

Err(err())
}
}
11 changes: 6 additions & 5 deletions bon-macros/src/builder/builder_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl BuilderGenCtx {
let mut start_fn = self.start_fn();
let state_mod = state_mod::StateModGenCtx::new(&self).state_mod();
let builder_decl = self.builder_decl();
let builder_impl = self.builder_impl();
let builder_impl = self.builder_impl()?;
let builder_derives = self.builder_derives();

let default_allows = syn::parse_quote!(#[allow(
Expand Down Expand Up @@ -94,11 +94,12 @@ impl BuilderGenCtx {
})
}

fn builder_impl(&self) -> TokenStream {
fn builder_impl(&self) -> Result<TokenStream> {
let finish_fn = self.finish_fn();
let setter_methods = self
.named_members()
.map(|member| SettersCtx::new(self, member).setter_methods());
.map(|member| SettersCtx::new(self, member).setter_methods())
.collect::<Result<Vec<_>>>()?;

let generics_decl = &self.generics.decl_without_defaults;
let generic_args = &self.generics.args;
Expand All @@ -109,7 +110,7 @@ impl BuilderGenCtx {

let allows = allow_warnings_on_member_types();

quote! {
Ok(quote! {
#allows
#[automatically_derived]
impl<
Expand All @@ -125,7 +126,7 @@ impl BuilderGenCtx {
#finish_fn
#(#setter_methods)*
}
}
})
}

/// Generates code that has no meaning to the compiler, but it helps
Expand Down
Loading