bon_macros/builder/builder_gen/top_level_config/
mod.rs1mod on;
2
3pub(crate) use on::OnConfig;
4
5use crate::parsing::{BonCratePath, ItemSigConfig, ItemSigConfigParsing, SpannedKey};
6use crate::util::prelude::*;
7use darling::ast::NestedMeta;
8use darling::FromMeta;
9use syn::parse::Parser;
10use syn::punctuated::Punctuated;
11use syn::ItemFn;
12
13fn parse_finish_fn(meta: &syn::Meta) -> Result<ItemSigConfig> {
14 ItemSigConfigParsing {
15 meta,
16 reject_self_mentions: Some("builder struct's impl block"),
17 }
18 .parse()
19}
20
21fn parse_builder_type(meta: &syn::Meta) -> Result<ItemSigConfig> {
22 ItemSigConfigParsing {
23 meta,
24 reject_self_mentions: Some("builder struct"),
25 }
26 .parse()
27}
28
29fn parse_state_mod(meta: &syn::Meta) -> Result<ItemSigConfig> {
30 ItemSigConfigParsing {
31 meta,
32 reject_self_mentions: Some("builder's state module"),
33 }
34 .parse()
35}
36
37fn parse_start_fn(meta: &syn::Meta) -> Result<ItemSigConfig> {
38 ItemSigConfigParsing {
39 meta,
40 reject_self_mentions: None,
41 }
42 .parse()
43}
44
45#[derive(Debug, FromMeta)]
46pub(crate) struct TopLevelConfig {
47 #[darling(skip)]
53 pub(crate) const_: Option<syn::Token![const]>,
54
55 #[darling(rename = "crate", default)]
58 pub(crate) bon: BonCratePath,
59
60 #[darling(default, with = parse_start_fn)]
61 pub(crate) start_fn: ItemSigConfig,
62
63 #[darling(default, with = parse_finish_fn)]
64 pub(crate) finish_fn: ItemSigConfig,
65
66 #[darling(default, with = parse_builder_type)]
67 pub(crate) builder_type: ItemSigConfig,
68
69 #[darling(default, with = parse_state_mod)]
70 pub(crate) state_mod: ItemSigConfig,
71
72 #[darling(multiple, with = crate::parsing::parse_non_empty_paren_meta_list)]
73 pub(crate) on: Vec<OnConfig>,
74
75 #[darling(default, with = crate::parsing::parse_non_empty_paren_meta_list)]
77 pub(crate) derive: DerivesConfig,
78}
79
80impl TopLevelConfig {
81 pub(crate) fn parse_for_fn(fn_item: &ItemFn, config: Option<TokenStream>) -> Result<Self> {
82 let other_configs = fn_item
83 .attrs
84 .iter()
85 .filter(|attr| attr.path().is_ident("builder"))
86 .map(|attr| {
87 if let syn::Meta::List(_) = attr.meta {
88 crate::parsing::require_non_empty_paren_meta_list_or_name_value(&attr.meta)?;
89 }
90 let meta_list = darling::util::parse_attribute_to_meta_list(attr)?;
91 Ok(meta_list.tokens)
92 });
93
94 let configs = config
95 .map(Ok)
96 .into_iter()
97 .chain(other_configs)
98 .collect::<Result<Vec<_>>>()?;
99
100 let me = Self::parse_for_any(configs)?;
101
102 if me.start_fn.name.is_none() {
103 let ItemSigConfig { name: _, vis, docs } = &me.start_fn;
104
105 let unexpected_param = None
106 .or_else(|| vis.as_ref().map(SpannedKey::key))
107 .or_else(|| docs.as_ref().map(SpannedKey::key));
108
109 if let Some(unexpected_param) = unexpected_param {
110 bail!(
111 unexpected_param,
112 "#[builder(start_fn({unexpected_param}))] requires that you \
113 also specify #[builder(start_fn(name))] which makes the starting \
114 function not to replace the positional function under the #[builder] \
115 attribute; by default (without the explicit #[builder(start_fn(name))]) \
116 the name, visibility and documentation of the positional \
117 function are all copied to the starting function, and the positional \
118 function under the #[builder] attribute becomes private with \
119 #[doc(hidden)] and it's renamed (the name is not guaranteed \
120 to be stable) to make it inaccessible even within the current module",
121 );
122 }
123 }
124
125 Ok(me)
126 }
127
128 pub(crate) fn parse_for_struct(configs: Vec<TokenStream>) -> Result<Self> {
129 Self::parse_for_any(configs)
130 }
131
132 fn parse_for_any(mut configs: Vec<TokenStream>) -> Result<Self> {
133 fn parse_const_prefix(
134 parse: syn::parse::ParseStream<'_>,
135 ) -> syn::Result<(Option<syn::Token![const]>, TokenStream)> {
136 let const_ = parse.parse().ok();
137 if const_.is_some() && !parse.is_empty() {
138 parse.parse::<syn::Token![,]>()?;
139 }
140
141 let rest = parse.parse()?;
142 Ok((const_, rest))
143 }
144
145 let mut const_ = None;
151
152 if let Some(config) = configs.first_mut() {
153 (const_, *config) = parse_const_prefix.parse2(std::mem::take(config))?;
154 }
155
156 let configs = configs
157 .into_iter()
158 .map(NestedMeta::parse_meta_list)
159 .collect::<Result<Vec<_>, _>>()?
160 .into_iter()
161 .flatten()
162 .collect::<Vec<_>>();
163
164 let mut on_configs = configs
168 .iter()
169 .enumerate()
170 .filter_map(|(i, meta)| match meta {
171 NestedMeta::Meta(syn::Meta::List(meta)) if meta.path.is_ident("on") => {
172 Some((i, meta))
173 }
174 _ => None,
175 })
176 .peekable();
177
178 while let Some((i, _)) = on_configs.next() {
179 if let Some((j, next_on)) = on_configs.peek() {
180 if *j != i + 1 {
181 bail!(
182 next_on,
183 "this `on(...)` clause is out of order; all `on(...)` \
184 clauses must be consecutive; there shouldn't be any \
185 other parameters between them",
186 )
187 }
188 }
189 }
190
191 let me = Self {
192 const_,
193 ..Self::from_list(&configs)?
194 };
195
196 if let Some(on) = me.on.iter().skip(1).find(|on| on.required.is_present()) {
197 bail!(
198 &on.required.span(),
199 "`required` can only be specified in the first `on(...)` clause; \
200 this restriction may be lifted in the future",
201 );
202 }
203
204 if let Some(first_on) = me.on.first().filter(|on| on.required.is_present()) {
205 if !matches!(first_on.type_pattern, syn::Type::Infer(_)) {
206 bail!(
207 &first_on.type_pattern,
208 "`required` can only be used with the wildcard type pattern \
209 i.e. `on(_, required)`; this restriction may be lifted in the future",
210 );
211 }
212 }
213
214 Ok(me)
215 }
216}
217
218#[derive(Debug, Clone, Default, FromMeta)]
219pub(crate) struct DerivesConfig {
220 #[darling(rename = "Clone")]
221 pub(crate) clone: Option<DeriveConfig>,
222
223 #[darling(rename = "Debug")]
224 pub(crate) debug: Option<DeriveConfig>,
225
226 #[darling(rename = "Into")]
227 pub(crate) into: darling::util::Flag,
228
229 #[darling(rename = "IntoFuture")]
230 pub(crate) into_future: Option<IntoFutureConfig>,
231}
232
233#[derive(Debug, Clone, Default)]
234pub(crate) struct DeriveConfig {
235 pub(crate) bounds: Option<Punctuated<syn::WherePredicate, syn::Token![,]>>,
236}
237
238#[derive(Debug, Clone)]
239pub(crate) struct IntoFutureConfig {
240 pub(crate) box_ident: syn::Ident,
241 pub(crate) is_send: bool,
242}
243
244impl syn::parse::Parse for IntoFutureConfig {
245 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
246 let box_ident: syn::Ident = input.parse()?;
248 if box_ident != "Box" {
249 return Err(syn::Error::new(
250 box_ident.span(),
251 "expected `Box` as the first argument, only boxed futures are supported",
252 ));
253 }
254
255 let is_send = if input.peek(syn::Token![,]) {
257 input.parse::<syn::Token![,]>()?;
258
259 if input.peek(syn::Token![?]) {
261 input.parse::<syn::Token![?]>()?;
262 let send_ident: syn::Ident = input.parse()?;
263 if send_ident != "Send" {
264 return Err(syn::Error::new(
265 send_ident.span(),
266 "expected `Send` after ?",
267 ));
268 }
269 false
270 } else {
271 return Err(input.error("expected `?Send` as the second argument"));
272 }
273 } else {
274 true
275 };
276
277 if !input.is_empty() {
279 return Err(input.error("unexpected tokens after arguments"));
280 }
281
282 Ok(Self { box_ident, is_send })
283 }
284}
285
286impl FromMeta for IntoFutureConfig {
287 fn from_meta(meta: &syn::Meta) -> Result<Self> {
288 let meta = match meta {
289 syn::Meta::List(meta) => meta,
290 _ => bail!(meta, "expected an attribute of form `IntoFuture(Box, ...)`"),
291 };
292
293 meta.require_parens_delim()?;
294
295 let me = syn::parse2(meta.tokens.clone())?;
296
297 Ok(me)
298 }
299}
300
301impl FromMeta for DeriveConfig {
302 fn from_meta(meta: &syn::Meta) -> Result<Self> {
303 if let syn::Meta::Path(_) = meta {
304 return Ok(Self { bounds: None });
305 }
306
307 meta.require_list()?.require_parens_delim()?;
308
309 #[derive(FromMeta)]
310 struct Parsed {
311 #[darling(with = crate::parsing::parse_paren_meta_list_with_terminated)]
312 bounds: Punctuated<syn::WherePredicate, syn::Token![,]>,
313 }
314
315 let Parsed { bounds } = Parsed::from_meta(meta)?;
316
317 Ok(Self {
318 bounds: Some(bounds),
319 })
320 }
321}