checked_rs_macro_impl/item/
enum_item.rs

1use 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    // returns true if the coverage is complete
156    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}