1mod on;
23pub(crate) use on::OnConfig;
45use crate::parsing::{ItemSigConfig, ItemSigConfigParsing, SpannedKey};
6use crate::util::prelude::*;
7use darling::FromMeta;
8use syn::punctuated::Punctuated;
910fn parse_finish_fn(meta: &syn::Meta) -> Result<ItemSigConfig> {
11 ItemSigConfigParsing {
12 meta,
13 reject_self_mentions: Some("builder struct's impl block"),
14 }
15 .parse()
16}
1718fn parse_builder_type(meta: &syn::Meta) -> Result<ItemSigConfig> {
19 ItemSigConfigParsing {
20 meta,
21 reject_self_mentions: Some("builder struct"),
22 }
23 .parse()
24}
2526fn parse_state_mod(meta: &syn::Meta) -> Result<ItemSigConfig> {
27 ItemSigConfigParsing {
28 meta,
29 reject_self_mentions: Some("builder's state module"),
30 }
31 .parse()
32}
3334fn parse_start_fn(meta: &syn::Meta) -> Result<ItemSigConfig> {
35 ItemSigConfigParsing {
36 meta,
37 reject_self_mentions: None,
38 }
39 .parse()
40}
4142#[derive(Debug, FromMeta)]
43pub(crate) struct TopLevelConfig {
44/// Overrides the path to the `bon` crate. This is usedfule when the macro is
45 /// wrapped in another macro that also reexports `bon`.
46#[darling(rename = "crate", default, map = Some, with = crate::parsing::parse_bon_crate_path)]
47pub(crate) bon: Option<syn::Path>,
4849#[darling(default, with = parse_start_fn)]
50pub(crate) start_fn: ItemSigConfig,
5152#[darling(default, with = parse_finish_fn)]
53pub(crate) finish_fn: ItemSigConfig,
5455#[darling(default, with = parse_builder_type)]
56pub(crate) builder_type: ItemSigConfig,
5758#[darling(default, with = parse_state_mod)]
59pub(crate) state_mod: ItemSigConfig,
6061#[darling(multiple, with = crate::parsing::parse_non_empty_paren_meta_list)]
62pub(crate) on: Vec<OnConfig>,
6364/// Specifies the derives to apply to the builder.
65#[darling(default, with = crate::parsing::parse_non_empty_paren_meta_list)]
66pub(crate) derive: DerivesConfig,
67}
6869impl TopLevelConfig {
70pub(crate) fn parse_for_fn(meta_list: &[darling::ast::NestedMeta]) -> Result<Self> {
71let me = Self::parse_for_any(meta_list)?;
7273if me.start_fn.name.is_none() {
74let ItemSigConfig { name: _, vis, docs } = &me.start_fn;
7576let unexpected_param = None
77.or_else(|| vis.as_ref().map(SpannedKey::key))
78 .or_else(|| docs.as_ref().map(SpannedKey::key));
7980if let Some(unexpected_param) = unexpected_param {
81bail!(
82 unexpected_param,
83"#[builder(start_fn({unexpected_param}))] requires that you \
84 also specify #[builder(start_fn(name))] which makes the starting \
85 function not to replace the positional function under the #[builder] \
86 attribute; by default (without the explicit #[builder(start_fn(name))]) \
87 the name, visibility and documentation of the positional \
88 function are all copied to the starting function, and the positional \
89 function under the #[builder] attribute becomes private with \
90 #[doc(hidden)] and it's renamed (the name is not guaranteed \
91 to be stable) to make it inaccessible even within the current module",
92 );
93 }
94 }
9596Ok(me)
97 }
9899pub(crate) fn parse_for_struct(meta_list: &[darling::ast::NestedMeta]) -> Result<Self> {
100Self::parse_for_any(meta_list)
101 }
102103fn parse_for_any(meta_list: &[darling::ast::NestedMeta]) -> Result<Self> {
104// This is a temporary hack. We only allow `on(_, required)` as the
105 // first `on(...)` clause. Instead we should implement an extended design:
106 // https://github.com/elastio/bon/issues/152
107let mut on_configs = meta_list
108 .iter()
109 .enumerate()
110 .filter_map(|(i, meta)| match meta {
111 darling::ast::NestedMeta::Meta(syn::Meta::List(meta))
112if meta.path.is_ident("on") =>
113 {
114Some((i, meta))
115 }
116_ => None,
117 })
118 .peekable();
119120while let Some((i, _)) = on_configs.next() {
121if let Some((j, next_on)) = on_configs.peek() {
122if *j != i + 1 {
123bail!(
124 next_on,
125"this `on(...)` clause is out of order; all `on(...)` \
126 clauses must be consecutive; there shouldn't be any \
127 other parameters between them",
128 )
129 }
130 }
131 }
132133let me = Self::from_list(meta_list)?;
134135if let Some(on) = me.on.iter().skip(1).find(|on| on.required.is_present()) {
136bail!(
137&on.required.span(),
138"`required` can only be specified in the first `on(...)` clause; \
139 this restriction may be lifted in the future",
140 );
141 }
142143if let Some(first_on) = me.on.first().filter(|on| on.required.is_present()) {
144if !matches!(first_on.type_pattern, syn::Type::Infer(_)) {
145bail!(
146&first_on.type_pattern,
147"`required` can only be used with the wildcard type pattern \
148 i.e. `on(_, required)`; this restriction may be lifted in the future",
149 );
150 }
151 }
152153Ok(me)
154 }
155}
156157#[derive(Debug, Clone, Default, FromMeta)]
158pub(crate) struct DerivesConfig {
159#[darling(rename = "Clone")]
160pub(crate) clone: Option<DeriveConfig>,
161162#[darling(rename = "Debug")]
163pub(crate) debug: Option<DeriveConfig>,
164}
165166#[derive(Debug, Clone, Default)]
167pub(crate) struct DeriveConfig {
168pub(crate) bounds: Option<Punctuated<syn::WherePredicate, syn::Token![,]>>,
169}
170171impl FromMeta for DeriveConfig {
172fn from_meta(meta: &syn::Meta) -> Result<Self> {
173if let syn::Meta::Path(_) = meta {
174return Ok(Self { bounds: None });
175 }
176177 meta.require_list()?.require_parens_delim()?;
178179#[derive(FromMeta)]
180struct Parsed {
181#[darling(with = crate::parsing::parse_paren_meta_list_with_terminated)]
182bounds: Punctuated<syn::WherePredicate, syn::Token![,]>,
183 }
184185let Parsed { bounds } = Parsed::from_meta(meta)?;
186187Ok(Self {
188 bounds: Some(bounds),
189 })
190 }
191}