bon_macros/builder/builder_gen/member/
named.rs1use super::config::MemberConfig;
2use super::{config, MemberOrigin};
3use crate::builder::builder_gen::member::config::SettersFnsConfig;
4use crate::builder::builder_gen::top_level_config::OnConfig;
5use crate::normalization::SyntaxVariant;
6use crate::parsing::{ItemSigConfig, SpannedKey};
7use crate::util::prelude::*;
8use proc_macro2::TokenTree;
9
10#[derive(Debug)]
11pub(crate) struct MemberName {
12 pub(crate) orig: syn::Ident,
15
16 pub(crate) snake: syn::Ident,
20
21 pub(crate) snake_raw_str: String,
26
27 pub(crate) pascal: syn::Ident,
32
33 pub(crate) pascal_str: String,
41}
42
43impl MemberName {
44 pub(crate) fn new(orig: syn::Ident, config: &MemberConfig) -> Self {
45 let snake = config.name.clone().unwrap_or_else(|| {
46 let orig_str = orig.to_string();
47 let norm = orig_str
48 .strip_prefix('_')
52 .unwrap_or(&orig_str);
53
54 syn::Ident::new_maybe_raw(norm, orig.span())
57 });
58
59 let pascal = snake.snake_to_pascal_case();
60
61 Self {
62 orig,
63 snake_raw_str: snake.raw_name(),
64 snake,
65 pascal_str: pascal.to_string(),
66 pascal,
67 }
68 }
69}
70
71#[derive(Debug)]
73pub(crate) struct NamedMember {
74 pub(crate) origin: MemberOrigin,
76
77 pub(crate) index: syn::Index,
79
80 pub(crate) name: MemberName,
83
84 pub(crate) docs: Vec<syn::Attribute>,
87
88 pub(crate) ty: SyntaxVariant<Box<syn::Type>>,
91
92 pub(crate) config: MemberConfig,
94
95 pub(crate) span: Span,
97}
98
99impl NamedMember {
100 pub(super) fn validate(&self) -> Result {
101 if let Some(default) = &self.config.default {
102 if self.is_special_option_ty() {
103 bail!(
104 &default.key,
105 "`Option<_>` already implies a default of `None`, \
106 so explicit #[builder(default)] is redundant",
107 );
108 }
109 }
110
111 let member_docs_not_copied = self
112 .config
113 .setters
114 .as_ref()
115 .map(|setters| {
116 if setters.doc.content.is_some() {
117 return true;
118 }
119
120 let SettersFnsConfig { some_fn, option_fn } = &setters.fns;
121 matches!(
122 (some_fn.as_deref(), option_fn.as_deref()),
123 (
124 Some(ItemSigConfig { docs: Some(_), .. }),
125 Some(ItemSigConfig { docs: Some(_), .. })
126 )
127 )
128 })
129 .unwrap_or(false);
130
131 if !member_docs_not_copied {
132 crate::parsing::reject_self_mentions_in_docs(
133 "builder struct's impl block",
134 &self.docs,
135 )?;
136 }
137
138 self.validate_setters_config()?;
139
140 if self.config.required.is_present() && !self.ty.norm.is_option() {
141 bail!(
142 &self.config.required.span(),
143 "`#[builder(required)]` can only be applied to members of \
144 type `Option<T>` to disable their special handling",
145 );
146 }
147
148 Ok(())
149 }
150
151 fn validate_setters_config(&self) -> Result {
152 let setters = match &self.config.setters {
153 Some(setters) => setters,
154 None => return Ok(()),
155 };
156
157 if self.is_required() {
158 let SettersFnsConfig { some_fn, option_fn } = &setters.fns;
159
160 let unexpected_setter = option_fn.as_ref().or(some_fn.as_ref());
161
162 if let Some(setter) = unexpected_setter {
163 bail!(
164 &setter.key,
165 "`{}` setter function applies only to members with `#[builder(default)]` \
166 or members of `Option<T>` type (if #[builder(required)] is not set)",
167 setter.key
168 );
169 }
170 }
171
172 if let SettersFnsConfig {
173 some_fn: Some(some_fn),
174 option_fn: Some(option_fn),
175 } = &setters.fns
176 {
177 let setter_fns = &[some_fn, option_fn];
178
179 Self::validate_unused_setters_cfg(setter_fns, &setters.name, |config| &config.name)?;
180 Self::validate_unused_setters_cfg(setter_fns, &setters.vis, |config| &config.vis)?;
181 Self::validate_unused_setters_cfg(setter_fns, &setters.doc.content, |config| {
182 &config.docs
183 })?;
184 }
185
186 Ok(())
187 }
188
189 fn validate_unused_setters_cfg<T>(
190 overrides: &[&SpannedKey<ItemSigConfig>],
191 config: &Option<SpannedKey<T>>,
192 get_val: impl Fn(&ItemSigConfig) -> &Option<SpannedKey<T>>,
193 ) -> Result {
194 let config = match config {
195 Some(config) => config,
196 None => return Ok(()),
197 };
198
199 let overrides_values = overrides
200 .iter()
201 .copied()
202 .map(|over| get_val(&over.value).as_ref());
203
204 if !overrides_values.clone().all(|over| over.is_some()) {
205 return Ok(());
206 }
207
208 let setters = overrides
209 .iter()
210 .map(|over| format!("`{}`", over.key))
211 .join(", ");
212
213 bail!(
214 &config.key,
215 "this `{name}` configuration is unused because all of the \
216 {setters} setters contain a `{name}` override",
217 name = config.key,
218 );
219 }
220
221 pub(crate) fn is_special_option_ty(&self) -> bool {
224 !self.config.required.is_present() && self.ty.norm.is_option()
225 }
226
227 pub(crate) fn is_required(&self) -> bool {
230 self.config.default.is_none() && !self.is_special_option_ty()
231 }
232
233 pub(crate) fn is_stateful(&self) -> bool {
238 self.is_required() || !self.config.overwritable.is_present()
239 }
240
241 pub(crate) fn underlying_norm_ty(&self) -> &syn::Type {
244 self.underlying_ty(&self.ty.norm)
245 }
246
247 pub(crate) fn underlying_orig_ty(&self) -> &syn::Type {
250 self.underlying_ty(&self.ty.orig)
251 }
252
253 fn underlying_ty<'m>(&'m self, ty: &'m syn::Type) -> &'m syn::Type {
254 if self.config.required.is_present() || self.config.default.is_some() {
255 ty
256 } else {
257 ty.option_type_param().unwrap_or(ty)
258 }
259 }
260
261 pub(crate) fn is(&self, other: &Self) -> bool {
262 self.index == other.index
263 }
264
265 pub(crate) fn merge_on_config(&mut self, on: &[OnConfig]) -> Result {
266 if let Some(on) = on.first().filter(|on| on.required.is_present()) {
270 if self.is_special_option_ty() {
271 self.config.required = on.required;
272 }
273 }
274
275 self.merge_config_into(on)?;
276 self.merge_setters_doc_skip(on)?;
277
278 self.config.overwritable = config::EvalBlanketFlagParam {
282 on,
283 param_name: config::BlanketParamName::Overwritable,
284 member_config: &self.config,
285 scrutinee: self.underlying_norm_ty(),
286 origin: self.origin,
287 }
288 .eval()?;
289
290 Ok(())
291 }
292
293 fn merge_setters_doc_skip(&mut self, on: &[OnConfig]) -> Result {
294 let skip = config::EvalBlanketFlagParam {
295 on,
296 param_name: config::BlanketParamName::SettersDocDefaultSkip,
297 member_config: &self.config,
298 scrutinee: self.underlying_norm_ty(),
299 origin: self.origin,
300 }
301 .eval()?;
302
303 let setters = self.config.setters.get_or_insert_with(Default::default);
304 let default = setters.doc.default.get_or_insert_with(Default::default);
305
306 default.skip = skip;
307
308 Ok(())
309 }
310
311 pub(crate) fn respan(&self, tokens: TokenStream) -> impl Iterator<Item = TokenTree> {
314 let span = self.span;
315 tokens.into_iter().map(move |mut token| {
316 token.set_span(span);
317 token
318 })
319 }
320}