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