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
3 changes: 1 addition & 2 deletions bon-macros/src/builder/builder_gen/input_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use crate::builder::builder_gen::models::{BuilderGenCtxParams, BuilderTypeParams
use crate::normalization::{GenericsNamespace, SyntaxVariant};
use crate::parsing::{ItemSigConfig, SpannedKey};
use crate::util::prelude::*;
use darling::FromMeta;
use std::borrow::Cow;
use syn::visit::Visit;
use syn::visit_mut::VisitMut;
Expand Down Expand Up @@ -40,7 +39,7 @@ fn parse_top_level_config(item_struct: &syn::ItemStruct) -> Result<TopLevelConfi
.into_iter()
.concat();

TopLevelConfig::from_list(&meta)
TopLevelConfig::parse_for_struct(&meta)
}

pub(crate) struct StructInputCtx {
Expand Down
9 changes: 9 additions & 0 deletions bon-macros/src/builder/builder_gen/member/named.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,15 @@ impl NamedMember {
}

pub(crate) fn merge_on_config(&mut self, on: &[OnConfig]) -> Result {
// This is a temporary hack. We only allow `on(_, transparent)` as the
// first `on(...)` clause. Instead we should implement the extended design:
// https://github.com/elastio/bon/issues/152
if let Some(on) = on.first().filter(|on| on.transparent.is_present()) {
if self.is_special_option_ty() {
self.config.transparent = on.transparent;
}
}

self.merge_config_into(on)?;

// FIXME: refactor this to make it more consistent with `into`
Expand Down
59 changes: 58 additions & 1 deletion bon-macros/src/builder/builder_gen/top_level_config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub(crate) struct TopLevelConfig {

impl TopLevelConfig {
pub(crate) fn parse_for_fn(meta_list: &[darling::ast::NestedMeta]) -> Result<Self> {
let me = Self::from_list(meta_list)?;
let me = Self::parse_for_any(meta_list)?;

if me.start_fn.name.is_none() {
let ItemSigConfig { name: _, vis, docs } = &me.start_fn;
Expand All @@ -95,6 +95,63 @@ impl TopLevelConfig {

Ok(me)
}

pub(crate) fn parse_for_struct(meta_list: &[darling::ast::NestedMeta]) -> Result<Self> {
Self::parse_for_any(meta_list)
}

fn parse_for_any(meta_list: &[darling::ast::NestedMeta]) -> Result<Self> {
// This is a temporary hack. We only allow `on(_, transparent)` as the
// first `on(...)` clause. Instead we should implement an extended design:
// https://github.com/elastio/bon/issues/152
let mut on_configs = meta_list
.iter()
.enumerate()
.filter_map(|(i, meta)| match meta {
darling::ast::NestedMeta::Meta(syn::Meta::List(meta))
if meta.path.is_ident("on") =>
{
Some((i, meta))
}
_ => None,
})
.peekable();

while let Some((i, _)) = on_configs.next() {
if let Some((j, next_on)) = on_configs.peek() {
if *j != i + 1 {
bail!(
next_on,
"this `on(...)` clause is out of order; all `on(...)` \
clauses must be consecutive; there shouldn't be any \
other parameters between them",
)
}
}
}

let me = Self::from_list(meta_list)?;

if let Some(on) = me.on.iter().skip(1).find(|on| on.transparent.is_present()) {
bail!(
&on.transparent.span(),
"`transparent` can only be specified in the first `on(...)` clause; \
this restriction may be lifted in the future",
);
}

if let Some(first_on) = me.on.first().filter(|on| on.transparent.is_present()) {
if !matches!(first_on.type_pattern, syn::Type::Infer(_)) {
bail!(
&first_on.type_pattern,
"`transparent` can only be used with the wildcard type pattern \
i.e. `on(_, transparent)`; this restriction may be lifted in the future",
);
}
}

Ok(me)
}
}

#[derive(Debug, Clone, Default, FromMeta)]
Expand Down
21 changes: 18 additions & 3 deletions bon-macros/src/builder/builder_gen/top_level_config/on.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub(crate) struct OnConfig {
pub(crate) type_pattern: syn::Type,
pub(crate) into: darling::util::Flag,
pub(crate) overwritable: darling::util::Flag,
pub(crate) transparent: darling::util::Flag,
}

impl Parse for OnConfig {
Expand All @@ -22,6 +23,7 @@ impl Parse for OnConfig {
struct Parsed {
into: darling::util::Flag,
overwritable: darling::util::Flag,
transparent: darling::util::Flag,
}

let parsed = Parsed::from_meta(&syn::parse_quote!(on(#rest)))?;
Expand All @@ -31,8 +33,16 @@ impl Parse for OnConfig {
// This lives in a separate block to make sure that if a new
// field is added to `Parsed` and unused here, then a compiler
// warning is emitted.
let Parsed { into, overwritable } = &parsed;
let flags = [("into", into), ("overwritable", overwritable)];
let Parsed {
into,
overwritable,
transparent,
} = &parsed;
let flags = [
("into", into),
("overwritable", overwritable),
("transparent", transparent),
];

if flags.iter().all(|(_, flag)| !flag.is_present()) {
let flags = flags.iter().map(|(name, _)| format!("`{name}`")).join(", ");
Expand Down Expand Up @@ -76,12 +86,17 @@ impl Parse for OnConfig {
"BUG: the type pattern does not match itself: {type_pattern:#?}"
);

let Parsed { into, overwritable } = parsed;
let Parsed {
into,
overwritable,
transparent,
} = parsed;

Ok(Self {
type_pattern,
into,
overwritable,
transparent,
})
}
}
Expand Down
Loading