checked_rs_macro_impl/item/
enum_item.rs1use std::collections::HashSet;
2
3use proc_macro2::Span;
4use syn::{parse::Parse, parse_quote};
5
6use crate::{
7 params::{
8 kw, BehaviorArg, DerivedTraits, NumberArg, NumberArgRange, NumberKind, NumberValue,
9 NumberValueRange, Params, SemiOrComma,
10 },
11 range_seq::RangeSeq,
12};
13
14pub mod field;
15pub mod variant;
16
17pub use field::*;
18pub use variant::*;
19
20pub struct ClampedEnumItem {
21 pub pound: syn::Token![#],
22 pub bracket: syn::token::Bracket,
23 pub integer: NumberKind,
24 pub integer_semi: Option<SemiOrComma>,
25 pub derived_traits: Option<DerivedTraits>,
26 pub derived_semi: Option<SemiOrComma>,
27 pub default_kw: Option<kw::default>,
28 pub default_eq: Option<syn::Token![=]>,
29 pub default_val: Option<NumberArg>,
30 pub default_semi: Option<SemiOrComma>,
31 pub behavior_kw: kw::behavior,
32 pub behavior_eq: syn::Token![=],
33 pub behavior: BehaviorArg,
34 pub behavior_semi: Option<SemiOrComma>,
35 pub vis: Option<syn::Visibility>,
36 pub enum_token: syn::Token![enum],
37 pub ident: syn::Ident,
38 pub range_bracket: Option<syn::token::Bracket>,
39 pub value_range: Option<NumberArgRange>,
40 pub brace: syn::token::Brace,
41 pub variants: syn::punctuated::Punctuated<ClampedEnumVariant, syn::Token![,]>,
42}
43
44impl Parse for ClampedEnumItem {
45 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
46 let pound = input.parse()?;
47
48 let content;
49 let bracket = syn::bracketed!(content in input);
50 let integer = content.parse()?;
51 let mut integer_semi = None;
52 let mut derived_traits = None;
53 let mut derived_semi = None;
54 let mut default_kw = None;
55 let mut default_eq = None;
56 let mut default_val = None;
57 let mut default_semi = None;
58 let mut behavior_kw = None;
59 let mut behavior_eq = None;
60 let mut behavior = None;
61 let mut behavior_semi = None;
62 let mut vis = None;
63
64 if !content.is_empty() {
65 integer_semi = Some(content.parse()?);
66
67 while !content.is_empty() {
68 if content.peek(kw::derive) {
69 derived_traits = Some(content.parse()?);
70 derived_semi = if content.peek(syn::Token![;]) {
71 Some(content.parse()?)
72 } else {
73 None
74 };
75 }
76
77 if content.peek(kw::default) {
78 default_kw = Some(content.parse()?);
79 default_eq = Some(content.parse()?);
80 default_val = Some(content.parse()?);
81 default_semi = if content.peek(syn::Token![;]) {
82 Some(content.parse()?)
83 } else {
84 None
85 };
86 }
87
88 if content.peek(kw::behavior) {
89 behavior_kw = Some(content.parse()?);
90 behavior_eq = Some(content.parse()?);
91 behavior = Some(content.parse()?);
92 behavior_semi = if content.peek(syn::Token![;]) {
93 Some(content.parse()?)
94 } else {
95 None
96 };
97 }
98 }
99 }
100
101 if input.peek(syn::Token![pub]) {
102 vis = Some(input.parse()?);
103 }
104
105 let enum_token = input.parse()?;
106 let ident = input.parse()?;
107 let mut range_bracket = None;
108 let mut value_range = None;
109
110 if input.peek(syn::token::Bracket) {
111 let content;
112 range_bracket = Some(syn::bracketed!(content in input));
113 value_range = Some(content.parse()?);
114 }
115
116 let content;
117 let brace = syn::braced!(content in input);
118 let variants = content.parse_terminated(ClampedEnumVariant::parse, syn::Token![,])?;
119
120 Ok(Self {
121 pound,
122 bracket,
123 integer,
124 integer_semi,
125 derived_traits,
126 derived_semi,
127 default_kw,
128 default_eq,
129 default_val,
130 default_semi,
131 behavior_kw: behavior_kw.unwrap_or_else(|| parse_quote!(behavior)),
132 behavior_eq: behavior_eq.unwrap_or_else(|| parse_quote!(=)),
133 behavior: behavior.unwrap_or_else(|| parse_quote!(Panic)),
134 behavior_semi,
135 vis,
136 enum_token,
137 ident,
138 range_bracket,
139 value_range,
140 brace,
141 variants,
142 })
143 }
144}
145
146impl ClampedEnumItem {
147 pub fn has_enum_token(input: syn::parse::ParseBuffer) -> syn::Result<bool> {
148 let _ = input.parse::<syn::Token![#]>();
149 let _content;
150 syn::bracketed!(_content in input);
151
152 Ok(input.peek(syn::Token![enum]))
153 }
154
155 pub fn check_coverage<'a, 'b: 'a>(
157 parent_exacts: Option<&'a mut HashSet<NumberValue>>,
158 parent_range_seq: Option<&'a mut RangeSeq>,
159 parent_lower_limit: Option<NumberValue>,
160 parent_upper_limit: Option<NumberValue>,
161 kind: NumberKind,
162 variants: impl Iterator<Item = &'b ClampedEnumVariant>,
163 ) -> syn::Result<bool> {
164 let mut exacts = HashSet::new();
165 let mut outer_range_seq = RangeSeq::new(kind);
166
167 for variant in variants {
168 match &variant.field {
169 ClampedEnumVariantField::Values { values, .. } => {
170 for val in values.iter() {
171 let val = val.into_value(kind);
172
173 if let Some(lower_limit) = parent_lower_limit {
174 if val < lower_limit {
175 return Err(syn::Error::new(
176 Span::call_site(),
177 format!("Value below lower limit in clamped enum {}", val),
178 ));
179 }
180 }
181
182 if let Some(upper_limit) = parent_upper_limit {
183 if val > upper_limit {
184 return Err(syn::Error::new(
185 Span::call_site(),
186 format!("Value above upper limit in clamped enum {}", val),
187 ));
188 }
189 }
190
191 if !exacts.insert(val) {
192 return Err(syn::Error::new(
193 Span::call_site(),
194 format!("Duplicate value in clamped enum {}", val),
195 ));
196 }
197 }
198 }
199 ClampedEnumVariantField::Ranges { values, .. } => {
200 for range in values.iter() {
201 outer_range_seq.insert(range.to_value_range(kind)?)?;
202 }
203 }
204 ClampedEnumVariantField::ClampedEnum {
205 value_range,
206 variants,
207 ..
208 } => {
209 let mut lower_limit = None;
210 let mut upper_limit = None;
211 let mut inner_exacts = HashSet::new();
212 let mut inner_range_seq = RangeSeq::new(kind);
213
214 if let Some(range) = value_range {
215 lower_limit = Some(range.first_val(kind));
216 upper_limit = Some(range.last_val(kind));
217 }
218
219 let full_coverage = Self::check_coverage(
220 Some(&mut inner_exacts),
221 Some(&mut inner_range_seq),
222 lower_limit,
223 upper_limit,
224 kind,
225 variants.iter(),
226 )?;
227
228 if let Some(val) = exacts.intersection(&inner_exacts).next() {
229 return Err(syn::Error::new(
230 Span::call_site(),
231 format!("Nested[1]: Duplicate value in clamped enum {}", val),
232 ));
233 } else {
234 exacts.extend(inner_exacts);
235 }
236
237 if full_coverage {
238 outer_range_seq.insert(NumberValueRange::new_inclusive(
239 lower_limit,
240 upper_limit,
241 kind,
242 )?)?;
243 } else {
244 for range in inner_range_seq.all_ranges() {
245 outer_range_seq.insert(range)?;
246 }
247 }
248 }
249 }
250 }
251
252 if let Some(parent_exacts) = parent_exacts {
253 if let Some(val) = parent_exacts.intersection(&exacts).next() {
254 return Err(syn::Error::new(
255 Span::call_site(),
256 format!("Outer: Duplicate value in clamped enum {}", val),
257 ));
258 } else {
259 parent_exacts.extend(exacts);
260 }
261 }
262
263 let full_start = parent_lower_limit
264 .unwrap_or_else(|| NumberArg::new_min_constant(kind).into_value(kind));
265
266 let full_end = parent_upper_limit
267 .unwrap_or_else(|| NumberArg::new_max_constant(kind).into_value(kind));
268
269 if outer_range_seq.has_full_range() {
270 if let Some(parent_range_seq) = parent_range_seq {
271 let full_range =
272 NumberValueRange::new_inclusive(Some(full_start), Some(full_end), kind)?;
273
274 parent_range_seq.insert(full_range)?;
275 }
276
277 return Ok(true);
278 } else if let Some(parent_range_seq) = parent_range_seq {
279 for range in outer_range_seq.all_ranges() {
280 parent_range_seq.insert(range)?;
281 }
282 }
283
284 return Ok(outer_range_seq.has_gaps());
285 }
286
287 pub fn limits(&self) -> syn::Result<NumberArgRange> {
288 let kind = self.integer;
289 let hard_lower_limit = self.value_range.as_ref().map(|range| range.start_arg(kind));
290 let hard_upper_limit = self.value_range.as_ref().map(|range| range.end_arg(kind));
291
292 let (mut lower_limit, mut upper_limit) = NumberArg::LIMITS_INIT.clone();
293
294 for variant in self.variants.iter() {
295 let variant_limits =
296 variant
297 .field
298 .limits(kind, hard_lower_limit.clone(), hard_upper_limit.clone())?;
299
300 let start = variant_limits.start_arg(kind);
301 let end = variant_limits.end_arg(kind);
302
303 lower_limit = lower_limit.map_or_else(
304 || Some(start.clone()),
305 |lower_limit| Some(lower_limit.min(&start, kind)),
306 );
307
308 upper_limit = upper_limit.map_or_else(
309 || Some(end.clone()),
310 |upper_limit| Some(upper_limit.max(&end, kind)),
311 );
312 }
313
314 if lower_limit.is_none() || upper_limit.is_none() {
315 return Err(syn::Error::new(
316 Span::call_site(),
317 "Item::Limits: No values in enum variant field",
318 ));
319 }
320
321 let lower_limit = lower_limit.unwrap();
322 let upper_limit = upper_limit.unwrap();
323
324 if let Some(hard_lower_limit) = hard_lower_limit.map(|arg| arg.into_value(kind)) {
325 if lower_limit.into_value(kind) < hard_lower_limit {
326 return Err(syn::Error::new(
327 Span::call_site(),
328 "Enum variant lower limit is below hard limit",
329 ));
330 }
331 }
332
333 if let Some(hard_upper_limit) = hard_upper_limit.map(|arg| arg.into_value(kind)) {
334 if upper_limit.into_value(kind) > hard_upper_limit {
335 return Err(syn::Error::new(
336 Span::call_site(),
337 "Enum variant upper limit is above hard limit",
338 ));
339 }
340 }
341
342 Ok(NumberArgRange::new_inclusive(lower_limit, upper_limit))
343 }
344
345 pub fn params(&self) -> syn::Result<Params> {
346 let kind = self.integer;
347 let limits = self.limits()?;
348
349 let total_lower_limit = limits.first_val(kind);
350 let total_upper_limit = limits.last_val(kind);
351
352 let mut parent_exacts = HashSet::new();
353 let mut parent_range_seq = RangeSeq::new(kind);
354
355 let this = Params {
356 integer: kind,
357 derived_traits: self.derived_traits.clone(),
358 vis: self.vis.clone().unwrap_or(syn::Visibility::Inherited),
359 ident: self.ident.clone(),
360 as_soft_or_hard: None,
361 default_val: self.default_val.as_ref().map(|arg| arg.into_value(kind)),
362 behavior: self.behavior.clone(),
363 lower_limit_val: total_lower_limit,
364 upper_limit_val: total_upper_limit,
365 full_coverage: Self::check_coverage(
366 Some(&mut parent_exacts),
367 Some(&mut parent_range_seq),
368 Some(total_lower_limit),
369 Some(total_upper_limit),
370 kind,
371 self.variants.iter(),
372 )?,
373 exact_values: if parent_exacts.is_empty() {
374 None
375 } else {
376 let mut exact_values = parent_exacts.into_iter().collect::<Vec<_>>();
377 exact_values.sort_unstable();
378 exact_values.dedup();
379 Some(exact_values)
380 },
381 valid_ranges: if parent_range_seq.is_empty() {
382 None
383 } else {
384 Some(parent_range_seq.uniq_ranges())
385 },
386 };
387
388 Ok(this)
389 }
390}