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