int-enum
A derive macro for lossless, span-aware conversion between Rust enums and their integer representations.
Repository Health
Technical Analysis
int-enum provides a #[derive(IntEnum)] procedural macro that generates From and TryFrom implementations for converting between a Rust enum and its underlying integer representation. Instead of hand-writing exhaustive match arms for every variant, you annotate a C-style enum with #[repr(u8)] (or any of the twelve supported integer types) and derive IntEnum to get safe, allocation-free conversions in both directions.
Conversion from the enum to the integer uses the infallible From trait, while conversion from an integer back to the enum uses TryFrom, returning the original integer as the error value when no matching variant exists. The macro supports explicit discriminants, implicit auto-incrementing discriminants, bit-shifted values, and #[repr(u16, align(4))]-style alignment attributes, faithfully matching Rust’s own discriminant inference rules.
What You Get
#[derive(IntEnum)]macro that implements bothFrom<Enum> for ReprandTryFrom<Repr> for Enumin one annotation- Automatic detection of the enum’s
#[repr(...)]integer type, defaulting toisizewhen none is specified - Support for all twelve Rust integer representations (i8/u8 through i128/u128, isize/usize)
- Compile-time diagnostics (via
proc-macro2-diagnostics) that reject non-unit variants with a clear error pointing at the offending variant
Common Use Cases
- Mapping wire-format byte codes (protocol opcodes, status bytes) to typed Rust enums without hand-written match statements
- Validating untrusted integer input (e.g. from a parser or FFI boundary) against a closed set of enum variants via
try_from - Serializing enum-based configuration or state values to a compact integer representation for storage
- Implementing bitflag-style or C-ABI-compatible enums that need round-trip conversion to their backing integer type
Under The Hood
Architecture
The crate is a small procedural macro structured into three modules: lib.rs (the entry point defining the #[proc_macro_derive(IntEnum)] attribute macro and a Result type alias over proc_macro2_diagnostics::Diagnostic), ast.rs (parses the enum’s #[repr(..)] attribute into a Repr struct representing the target integer type, defaulting to isize when absent), and expand.rs (the code-generation core, which validates that every variant is a unit variant, then emits a From<Enum> for Repr and TryFrom<Repr> for Enum impl pair wrapped in an anonymous const _: () block to avoid namespace pollution). Data flows linearly: tokens are parsed into a syn::DeriveInput, passed to expand::derive, which delegates repr detection to ast::Repr::from_attributes before building the two trait impls via quote!. There is no mutable state and no runtime component — the entire architecture is a single-pass AST transformation, and the load-bearing assumption (enforced by verify_unit_variants) is that all variants are unit variants; supporting non-unit or generic variants would require restructuring the pattern-generation logic in expand_enum.
Tech Stack
The crate targets Rust 2021 edition (rust-version 1.70+) and is a proc-macro crate ([lib] proc-macro = true). Dependencies are proc-macro2, quote, and syn 2.x — the standard trio for token-stream parsing and code generation in the Rust macro ecosystem — plus proc-macro2-diagnostics (default-features disabled) for emitting rustc-style span-aware compile errors without the nightly-only proc_macro::Diagnostic API. There is no build script and no runtime deployment target, since this is a compile-time-only library published to crates.io; a GitHub Actions workflow is the sole CI/automation surface.
Code Quality
Tests live in tests/test.rs as a black-box integration suite (no unit tests inside src/), covering basic conversion, signed integers, #[repr(u16, align(4))] alignment, explicit bit-shifted discriminants, implicit auto-incrementing discriminants that mirror rustc’s own discriminant rules, the default-to-isize fallback, and a regression test tied to a specific reported issue. Error handling uses a custom Result<T, E = Diagnostic> alias and returns rich span-attached diagnostics rather than panicking or emitting opaque errors, giving users compiler-level feedback pointing at the exact non-unit variant. Naming is consistent and idiomatic, a rustfmt.toml and CI workflow enforce formatting and build checks, and no unsafe code is present. Coverage is thorough at the integration level but there are no isolated unit tests of internal helpers such as Repr::from_attributes.
API Design
The public surface is a single derive macro with no configuration attributes beyond the enum’s own #[repr(...)] — there is nothing to configure or learn beyond adding #[derive(IntEnum)], and the generated From/TryFrom impls slot directly into idiomatic Rust conversion patterns (.into(), Enum::try_from(n)) that users already know. The crate deliberately restricts itself to unit-variant C-style enums rather than generalizing, which keeps both the API and its error surface small and predictable. Documentation is limited to a single doc-comment example on the macro itself and the README, with no dedicated docs beyond docs.rs-generated API reference.