enum_parity 0.2.1

a macro that enforces enum discriminant bit parity
Documentation
use std::str::FromStr;

use darling::FromAttributes;
use itertools::Itertools;

#[derive(Copy, Clone, Debug)]
pub enum IntRepr {
    U8,
    U16,
    U32,
    U64,
    U128,
    Usize,
    I8,
    I16,
    I32,
    I64,
    I128,
    Isize,
}

impl IntRepr {
    const ALL_FMT: &[&str] = &[
        "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize",
    ];
}

impl FromStr for IntRepr {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "u8" => Ok(Self::U8),
            "u16" => Ok(Self::U16),
            "u32" => Ok(Self::U32),
            "u64" => Ok(Self::U64),
            "u128" => Ok(Self::U128),
            "usize" => Ok(Self::Usize),
            "i8" => Ok(Self::I8),
            "i16" => Ok(Self::I16),
            "i32" => Ok(Self::I32),
            "i64" => Ok(Self::I64),
            "i128" => Ok(Self::I128),
            "isize" => Ok(Self::Isize),
            _ => Err(()),
        }
    }
}

impl core::fmt::Display for IntRepr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let display = match self {
            Self::U8 => "u8",
            Self::U16 => "u16",
            Self::U32 => "u32",
            Self::U64 => "u64",
            Self::U128 => "u128",
            Self::Usize => "usize",
            Self::I8 => "i8",
            Self::I16 => "i16",
            Self::I32 => "i32",
            Self::I64 => "i64",
            Self::I128 => "i128",
            Self::Isize => "isize",
        };

        write!(f, "{display}")
    }
}

impl FromAttributes for IntRepr {
    fn from_attributes(attrs: &[syn::Attribute]) -> darling::Result<Self> {
        let mut int_repr = None;
        let repr_attr = attrs
            .iter()
            .find(|a| a.path().is_ident("repr"))
            .ok_or_else(|| darling::Error::custom("Enum missing `repr` attribute"))?;

        repr_attr.parse_nested_meta(|m| {
            let repr_type = m
                .path
                .get_ident()
                .ok_or_else(|| syn::Error::new_spanned(&m.path, "Missing `repr` type"))?
                .to_string();
            let ir = Self::from_str(&repr_type).map_err(|()| {
                darling::Error::custom(format!(
                    "Unsupported `repr` type. Supported types are {}",
                    Self::ALL_FMT.iter().map(|s| format!("`{s}`")).join(" ")
                ))
                .with_span(&m.path)
                .add_sibling_alts_for_unknown_field(Self::ALL_FMT)
            })?;
            int_repr = Some(ir);

            Ok(())
        })?;

        int_repr.ok_or_else(|| darling::Error::custom("unable to find a valid `repr` attribute"))
    }
}