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::*;
8
9#[derive(Debug)]
10pub(crate) struct MemberName {
11 pub(crate) orig: syn::Ident,
14
15 pub(crate) snake: syn::Ident,
19
20 pub(crate) snake_raw_str: String,
25
26 pub(crate) pascal: syn::Ident,
31
32 pub(crate) pascal_str: String,
40}
41
42impl MemberName {
43 pub(crate) fn new(orig: syn::Ident, config: &MemberConfig) -> Self {
44 let snake = config.name.clone().unwrap_or_else(|| {
45 let orig_str = orig.to_string();
46 let norm = orig_str
47 .strip_prefix('_')
51 .unwrap_or(&orig_str);
52
53 syn::Ident::new_maybe_raw(norm, orig.span())
56 });
57
58 let pascal = snake.snake_to_pascal_case();
59
60 Self {
61 orig,
62 snake_raw_str: snake.raw_name(),
63 snake,
64 pascal_str: pascal.to_string(),
65 pascal,
66 }
67 }
68}
69
70#[derive(Debug)]
72pub(crate) struct NamedMember {
73 pub(crate) origin: MemberOrigin,
75
76 pub(crate) index: syn::Index,
78
79 pub(crate) name: MemberName,
82
83 pub(crate) docs: Vec<syn::Attribute>,
86
87 pub(crate) ty: SyntaxVariant<Box<syn::Type>>,
90
91 pub(crate) config: MemberConfig,
93}
94
95impl NamedMember {
96 pub(super) fn validate(&self) -> Result {
97 if let Some(default) = &self.config.default {
98 if self.is_special_option_ty() {
99 bail!(
100 &default.key,
101 "`Option<_>` already implies a default of `None`, \
102 so explicit #[builder(default)] is redundant",
103 );
104 }
105 }
106
107 let member_docs_not_copied = self
108 .config
109 .setters
110 .as_ref()
111 .map(|setters| {
112 if setters.docs.is_some() {
113 return true;
114 }
115
116 let SettersFnsConfig { some_fn, option_fn } = &setters.fns;
117 matches!(
118 (some_fn.as_deref(), option_fn.as_deref()),
119 (
120 Some(ItemSigConfig { docs: Some(_), .. }),
121 Some(ItemSigConfig { docs: Some(_), .. })
122 )
123 )
124 })
125 .unwrap_or(false);
126
127 if !member_docs_not_copied {
128 crate::parsing::reject_self_mentions_in_docs(
129 "builder struct's impl block",
130 &self.docs,
131 )?;
132 }
133
134 self.validate_setters_config()?;
135
136 if self.config.required.is_present() && !self.ty.norm.is_option() {
137 bail!(
138 &self.config.required.span(),
139 "`#[builder(required)]` can only be applied to members of \
140 type `Option<T>` to disable their special handling",
141 );
142 }
143
144 Ok(())
145 }
146
147 fn validate_setters_config(&self) -> Result {
148 let setters = match &self.config.setters {
149 Some(setters) => setters,
150 None => return Ok(()),
151 };
152
153 if self.is_required() {
154 let SettersFnsConfig { some_fn, option_fn } = &setters.fns;
155
156 let unexpected_setter = option_fn.as_ref().or(some_fn.as_ref());
157
158 if let Some(setter) = unexpected_setter {
159 bail!(
160 &setter.key,
161 "`{}` setter function applies only to members with `#[builder(default)]` \
162 or members of `Option<T>` type (if #[builder(required)] is not set)",
163 setter.key
164 );
165 }
166 }
167
168 if let SettersFnsConfig {
169 some_fn: Some(some_fn),
170 option_fn: Some(option_fn),
171 } = &setters.fns
172 {
173 let setter_fns = &[some_fn, option_fn];
174
175 Self::validate_unused_setters_cfg(setter_fns, &setters.name, |config| &config.name)?;
176 Self::validate_unused_setters_cfg(setter_fns, &setters.vis, |config| &config.vis)?;
177 Self::validate_unused_setters_cfg(setter_fns, &setters.docs, |config| &config.docs)?;
178 }
179
180 Ok(())
181 }
182
183 #[allow(unknown_lints, clippy::ref_option)]
185 fn validate_unused_setters_cfg<T>(
186 overrides: &[&SpannedKey<ItemSigConfig>],
187 config: &Option<SpannedKey<T>>,
188 get_val: impl Fn(&ItemSigConfig) -> &Option<SpannedKey<T>>,
189 ) -> Result {
190 let config = match config {
191 Some(config) => config,
192 None => return Ok(()),
193 };
194
195 let overrides_values = overrides
196 .iter()
197 .copied()
198 .map(|over| get_val(&over.value).as_ref());
199
200 if !overrides_values.clone().all(|over| over.is_some()) {
201 return Ok(());
202 }
203
204 let setters = overrides
205 .iter()
206 .map(|over| format!("`{}`", over.key))
207 .join(", ");
208
209 bail!(
210 &config.key,
211 "this `{name}` configuration is unused because all of the \
212 {setters} setters contain a `{name}` override",
213 name = config.key,
214 );
215 }
216
217 pub(crate) fn is_special_option_ty(&self) -> bool {
220 !self.config.required.is_present() && self.ty.norm.is_option()
221 }
222
223 pub(crate) fn is_required(&self) -> bool {
226 self.config.default.is_none() && !self.is_special_option_ty()
227 }
228
229 pub(crate) fn is_stateful(&self) -> bool {
234 self.is_required() || !self.config.overwritable.is_present()
235 }
236
237 pub(crate) fn underlying_norm_ty(&self) -> &syn::Type {
240 self.underlying_ty(&self.ty.norm)
241 }
242
243 pub(crate) fn underlying_orig_ty(&self) -> &syn::Type {
246 self.underlying_ty(&self.ty.orig)
247 }
248
249 fn underlying_ty<'m>(&'m self, ty: &'m syn::Type) -> &'m syn::Type {
250 if self.config.required.is_present() || self.config.default.is_some() {
251 ty
252 } else {
253 ty.option_type_param().unwrap_or(ty)
254 }
255 }
256
257 pub(crate) fn is(&self, other: &Self) -> bool {
258 self.index == other.index
259 }
260
261 pub(crate) fn merge_on_config(&mut self, on: &[OnConfig]) -> Result {
262 if let Some(on) = on.first().filter(|on| on.required.is_present()) {
266 if self.is_special_option_ty() {
267 self.config.required = on.required;
268 }
269 }
270
271 self.merge_config_into(on)?;
272
273 self.config.overwritable = config::EvalBlanketFlagParam {
277 on,
278 param_name: config::BlanketParamName::Overwritable,
279 member_config: &self.config,
280 scrutinee: self.underlying_norm_ty(),
281 origin: self.origin,
282 }
283 .eval()?;
284
285 Ok(())
286 }
287}