+impl EnumSetInfo {
+ fn new(input: &DeriveInput, attrs: EnumsetAttrs) -> EnumSetInfo {
+ EnumSetInfo {
+ name: input.ident.clone(),
+ crate_name: attrs.crate_name.map(|x| Ident::new(&x, Span::call_site())),
+ explicit_serde_repr: attrs.serialize_repr.map(|x| Ident::new(&x, Span::call_site())),
+ has_signed_repr: false,
+ has_large_repr: false,
+ variants: Vec::new(),
+ max_discrim: 0,
+ cur_discrim: 0,
+ used_variant_names: HashSet::new(),
+ used_discriminants: HashSet::new(),
+ no_ops: attrs.no_ops,
+ no_super_impls: attrs.no_super_impls,
+ serialize_as_list: attrs.serialize_as_list,
+ serialize_deny_unknown: attrs.serialize_deny_unknown
+ }
+ }
+
+ /// Sets an explicit repr for the enumset.
+ fn push_explicit_repr(&mut self, attr_span: Span, repr: &str) -> Result<()> {
+ // Check whether the repr is supported, and if so, set some flags for better error
+ // messages later on.
+ match repr {
+ "Rust" | "C" | "u8" | "u16" | "u32" => Ok(()),
+ "usize" | "u64" | "u128" => {
+ self.has_large_repr = true;
+ Ok(())
+ }
+ "i8" | "i16" | "i32" => {
+ self.has_signed_repr = true;
+ Ok(())
+ }
+ "isize" | "i64" | "i128" => {
+ self.has_signed_repr = true;
+ self.has_large_repr = true;
+ Ok(())
+ }
+ _ => error(attr_span, "Unsupported repr.")
+ }
+ }
+ /// Adds a variant to the enumset.
+ fn push_variant(&mut self, variant: &Variant) -> Result<()> {
+ if self.used_variant_names.contains(&variant.ident.to_string()) {
+ error(variant.span(), "Duplicated variant name.")
+ } else if let Fields::Unit = variant.fields {
+ // Parse the discriminant.
+ if let Some((_, expr)) = &variant.discriminant {
+ let discriminant_fail_message = format!(
+ "Enum set discriminants must be `u32`s.{}",
+ if self.has_signed_repr || self.has_large_repr {
+ format!(
+ " ({} discrimiants are still unsupported with reprs that allow them.)",
+ if self.has_large_repr {
+ "larger"
+ } else if self.has_signed_repr {
+ "negative"
+ } else {
+ "larger or negative"
+ }
+ )
+ } else {
+ String::new()
+ },
+ );
+ if let Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) = expr {
+ match i.base10_parse() {
+ Ok(val) => self.cur_discrim = val,
+ Err(_) => error(expr.span(), &discriminant_fail_message)?,
+ }
+ } else {
+ error(variant.span(), &discriminant_fail_message)?;
+ }
+ }
+
+ // Validate the discriminant.
+ let discriminant = self.cur_discrim;
+ if discriminant >= 128 {
+ let message = if self.variants.len() <= 127 {
+ "`#[derive(EnumSetType)]` currently only supports discriminants up to 127."
+ } else {
+ "`#[derive(EnumSetType)]` currently only supports enums up to 128 variants."
+ };
+ error(variant.span(), message)?;
+ }
+ if self.used_discriminants.contains(&discriminant) {
+ error(variant.span(), "Duplicated enum discriminant.")?;
+ }
+
+ // Add the variant to the info.
+ self.cur_discrim += 1;
+ if discriminant > self.max_discrim {
+ self.max_discrim = discriminant;
+ }
+ self.variants.push(EnumSetValue {
+ name: variant.ident.clone(),
+ variant_repr: discriminant,
+ });
+ self.used_variant_names.insert(variant.ident.to_string());
+ self.used_discriminants.insert(discriminant);
+
+ Ok(())
+ } else {
+ error(variant.span(), "`#[derive(EnumSetType)]` can only be used on fieldless enums.")
+ }
+ }
+ /// Validate the enumset type.
+ fn validate(&self) -> Result<()> {
+ // Check if all bits of the bitset can fit in the serialization representation.
+ if let Some(explicit_serde_repr) = &self.explicit_serde_repr {
+ let is_overflowed = match explicit_serde_repr.to_string().as_str() {
+ "u8" => self.max_discrim >= 8,
+ "u16" => self.max_discrim >= 16,
+ "u32" => self.max_discrim >= 32,
+ "u64" => self.max_discrim >= 64,
+ "u128" => self.max_discrim >= 128,
+ _ => error(
+ Span::call_site(),
+ "Only `u8`, `u16`, `u32`, `u64` and `u128` are supported for serde_repr."
+ )?,
+ };
+ if is_overflowed {
+ error(Span::call_site(), "serialize_repr cannot be smaller than bitset.")?;
+ }
+ }
+ Ok(())
+ }
+
+ /// Computes the underlying type used to store the enumset.
+ fn enumset_repr(&self) -> SynTokenStream {
+ if self.max_discrim <= 7 {
+ quote! { u8 }
+ } else if self.max_discrim <= 15 {
+ quote! { u16 }
+ } else if self.max_discrim <= 31 {
+ quote! { u32 }
+ } else if self.max_discrim <= 63 {
+ quote! { u64 }
+ } else if self.max_discrim <= 127 {
+ quote! { u128 }
+ } else {
+ panic!("max_variant > 127?")
+ }
+ }
+ /// Computes the underlying type used to serialize the enumset.
+ #[cfg(feature = "serde")]
+ fn serde_repr(&self) -> SynTokenStream {
+ if let Some(serde_repr) = &self.explicit_serde_repr {
+ quote! { #serde_repr }
+ } else {
+ self.enumset_repr()
+ }
+ }