blob: 2a55468764970d51a0f39884db94eb295a9628f0 [file] [log] [blame]
David Tolnay17e137f2020-05-08 15:55:28 -07001use proc_macro2::{Literal, Span, TokenStream};
2use quote::ToTokens;
3use std::collections::HashSet;
4use std::fmt::{self, Display};
5use std::str::FromStr;
6use syn::{Error, Expr, Lit, Result};
7
8pub struct DiscriminantSet {
9 values: HashSet<Discriminant>,
10 previous: Option<Discriminant>,
11}
12
13#[derive(Copy, Clone, Hash, Eq, PartialEq)]
14pub struct Discriminant {
15 magnitude: u32,
16}
17
18impl DiscriminantSet {
19 pub fn new() -> Self {
20 DiscriminantSet {
21 values: HashSet::new(),
22 previous: None,
23 }
24 }
25
26 pub fn insert(&mut self, expr: &Expr) -> Result<Discriminant> {
27 let discriminant = expr_to_discriminant(expr)?;
28 insert(self, discriminant)
29 }
30
31 pub fn insert_next(&mut self) -> Result<Discriminant> {
32 let discriminant = match self.previous {
33 None => Discriminant::zero(),
34 Some(mut discriminant) => {
35 if discriminant.magnitude == u32::MAX {
36 let msg = format!("discriminant overflow on value after {}", u32::MAX);
37 return Err(Error::new(Span::call_site(), msg));
38 }
39 discriminant.magnitude += 1;
40 discriminant
41 }
42 };
43 insert(self, discriminant)
44 }
45}
46
47fn expr_to_discriminant(expr: &Expr) -> Result<Discriminant> {
48 if let Expr::Lit(expr) = expr {
49 if let Lit::Int(lit) = &expr.lit {
50 return lit.base10_parse::<Discriminant>();
51 }
52 }
53 Err(Error::new_spanned(
54 expr,
55 "enums with non-integer literal discriminants are not supported yet",
56 ))
57}
58
59fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result<Discriminant> {
60 if set.values.insert(discriminant) {
61 set.previous = Some(discriminant);
62 Ok(discriminant)
63 } else {
64 let msg = format!("discriminant value `{}` already exists", discriminant);
65 Err(Error::new(Span::call_site(), msg))
66 }
67}
68
69impl Discriminant {
70 fn zero() -> Self {
71 Discriminant { magnitude: 0 }
72 }
73}
74
75impl Display for Discriminant {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 Display::fmt(&self.magnitude, f)
78 }
79}
80
81impl ToTokens for Discriminant {
82 fn to_tokens(&self, tokens: &mut TokenStream) {
83 Literal::u32_unsuffixed(self.magnitude).to_tokens(tokens);
84 }
85}
86
87impl FromStr for Discriminant {
88 type Err = Error;
89
90 fn from_str(s: &str) -> Result<Self> {
91 match s.parse::<u32>() {
92 Ok(magnitude) => Ok(Discriminant { magnitude }),
93 Err(_) => Err(Error::new(
94 Span::call_site(),
95 "discriminant value outside of supported range",
96 )),
97 }
98 }
99}