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::punctuated::Punctuated;
10use syn::ItemFn;
11
12fn parse_finish_fn(meta: &syn::Meta) -> Result<ItemSigConfig> {
13 ItemSigConfigParsing {
14 meta,
15 reject_self_mentions: Some("builder struct's impl block"),
16 }
17 .parse()
18}
19
20fn parse_builder_type(meta: &syn::Meta) -> Result<ItemSigConfig> {
21 ItemSigConfigParsing {
22 meta,
23 reject_self_mentions: Some("builder struct"),
24 }
25 .parse()
26}
27
28fn parse_state_mod(meta: &syn::Meta) -> Result<ItemSigConfig> {
29 ItemSigConfigParsing {
30 meta,
31 reject_self_mentions: Some("builder's state module"),
32 }
33 .parse()
34}
35
36fn parse_start_fn(meta: &syn::Meta) -> Result<ItemSigConfig> {
37 ItemSigConfigParsing {
38 meta,
39 reject_self_mentions: None,
40 }
41 .parse()
42}
43
44#[derive(Debug, FromMeta)]
45pub(crate) struct TopLevelConfig {
46 #[darling(rename = "crate", default)]
49 pub(crate) bon: BonCratePath,
50
51 #[darling(default, with = parse_start_fn)]
52 pub(crate) start_fn: ItemSigConfig,
53
54 #[darling(default, with = parse_finish_fn)]
55 pub(crate) finish_fn: ItemSigConfig,
56
57 #[darling(default, with = parse_builder_type)]
58 pub(crate) builder_type: ItemSigConfig,
59
60 #[darling(default, with = parse_state_mod)]
61 pub(crate) state_mod: ItemSigConfig,
62
63 #[darling(multiple, with = crate::parsing::parse_non_empty_paren_meta_list)]
64 pub(crate) on: Vec<OnConfig>,
65
66 #[darling(default, with = crate::parsing::parse_non_empty_paren_meta_list)]
68 pub(crate) derive: DerivesConfig,
69}
70
71impl TopLevelConfig {
72 pub(crate) fn parse_for_fn(fn_item: &ItemFn, config: Option<TokenStream>) -> Result<Self> {
73 let config = config.map(NestedMeta::parse_meta_list).transpose()?;
74
75 let meta = fn_item
76 .attrs
77 .iter()
78 .filter(|attr| attr.path().is_ident("builder"))
79 .map(|attr| {
80 if let syn::Meta::List(_) = attr.meta {
81 crate::parsing::require_non_empty_paren_meta_list_or_name_value(&attr.meta)?;
82 }
83 let meta_list = darling::util::parse_attribute_to_meta_list(attr)?;
84 let meta_list = NestedMeta::parse_meta_list(meta_list.tokens)?;
85
86 Ok(meta_list)
87 })
88 .collect::<Result<Vec<_>>>()?
89 .into_iter()
90 .chain(config)
91 .flatten()
92 .collect::<Vec<_>>();
93
94 let me = Self::parse_for_any(&meta)?;
95
96 if me.start_fn.name.is_none() {
97 let ItemSigConfig { name: _, vis, docs } = &me.start_fn;
98
99 let unexpected_param = None
100 .or_else(|| vis.as_ref().map(SpannedKey::key))
101 .or_else(|| docs.as_ref().map(SpannedKey::key));
102
103 if let Some(unexpected_param) = unexpected_param {
104 bail!(
105 unexpected_param,
106 "#[builder(start_fn({unexpected_param}))] requires that you \
107 also specify #[builder(start_fn(name))] which makes the starting \
108 function not to replace the positional function under the #[builder] \
109 attribute; by default (without the explicit #[builder(start_fn(name))]) \
110 the name, visibility and documentation of the positional \
111 function are all copied to the starting function, and the positional \
112 function under the #[builder] attribute becomes private with \
113 #[doc(hidden)] and it's renamed (the name is not guaranteed \
114 to be stable) to make it inaccessible even within the current module",
115 );
116 }
117 }
118
119 Ok(me)
120 }
121
122 pub(crate) fn parse_for_struct(meta_list: &[NestedMeta]) -> Result<Self> {
123 Self::parse_for_any(meta_list)
124 }
125
126 fn parse_for_any(meta_list: &[NestedMeta]) -> Result<Self> {
127 let mut on_configs = meta_list
131 .iter()
132 .enumerate()
133 .filter_map(|(i, meta)| match meta {
134 NestedMeta::Meta(syn::Meta::List(meta)) if meta.path.is_ident("on") => {
135 Some((i, meta))
136 }
137 _ => None,
138 })
139 .peekable();
140
141 while let Some((i, _)) = on_configs.next() {
142 if let Some((j, next_on)) = on_configs.peek() {
143 if *j != i + 1 {
144 bail!(
145 next_on,
146 "this `on(...)` clause is out of order; all `on(...)` \
147 clauses must be consecutive; there shouldn't be any \
148 other parameters between them",
149 )
150 }
151 }
152 }
153
154 let me = Self::from_list(meta_list)?;
155
156 if let Some(on) = me.on.iter().skip(1).find(|on| on.required.is_present()) {
157 bail!(
158 &on.required.span(),
159 "`required` can only be specified in the first `on(...)` clause; \
160 this restriction may be lifted in the future",
161 );
162 }
163
164 if let Some(first_on) = me.on.first().filter(|on| on.required.is_present()) {
165 if !matches!(first_on.type_pattern, syn::Type::Infer(_)) {
166 bail!(
167 &first_on.type_pattern,
168 "`required` can only be used with the wildcard type pattern \
169 i.e. `on(_, required)`; this restriction may be lifted in the future",
170 );
171 }
172 }
173
174 Ok(me)
175 }
176}
177
178#[derive(Debug, Clone, Default, FromMeta)]
179pub(crate) struct DerivesConfig {
180 #[darling(rename = "Clone")]
181 pub(crate) clone: Option<DeriveConfig>,
182
183 #[darling(rename = "Debug")]
184 pub(crate) debug: Option<DeriveConfig>,
185
186 #[darling(rename = "Into")]
187 pub(crate) into: darling::util::Flag,
188}
189
190#[derive(Debug, Clone, Default)]
191pub(crate) struct DeriveConfig {
192 pub(crate) bounds: Option<Punctuated<syn::WherePredicate, syn::Token![,]>>,
193}
194
195impl FromMeta for DeriveConfig {
196 fn from_meta(meta: &syn::Meta) -> Result<Self> {
197 if let syn::Meta::Path(_) = meta {
198 return Ok(Self { bounds: None });
199 }
200
201 meta.require_list()?.require_parens_delim()?;
202
203 #[derive(FromMeta)]
204 struct Parsed {
205 #[darling(with = crate::parsing::parse_paren_meta_list_with_terminated)]
206 bounds: Punctuated<syn::WherePredicate, syn::Token![,]>,
207 }
208
209 let Parsed { bounds } = Parsed::from_meta(meta)?;
210
211 Ok(Self {
212 bounds: Some(bounds),
213 })
214 }
215}