Macro enumerable_enum

Source
macro_rules! enumerable_enum {
    (
        #[repr($t:ty)]
        $(#[$meta:meta])*
        $vis:vis enum $name:ident {
            $($(#[$variant_meta:meta])* $variant:ident $(= $discriminant:expr)?),+ $(,)?
        }
    ) => { ... };
    (
        $(#[$meta:meta])*
        $vis:vis enum $name:ident {
            $($(#[$variant_meta:meta])* $variant:ident $(= $discriminant:expr)?),+ $(,)?
        }
    ) => { ... };
    (@enum
        $(#[$meta:meta])*
        $vis:vis enum $name:ident {
            $($(#[$variant_meta:meta])* $variant:ident),+ $(,)?
        }
    ) => { ... };
    (@enum
        $(#[$meta:meta])*
        $vis:vis enum $name:ident {
            $($(#[$variant_meta:meta])* $variant:ident $(= $discriminant:expr)?),+ $(,)?
        }
    ) => { ... };
}
Expand description

Macro to generate helpers for fieldless unit-only enums.

Helpers for accessing variants:

  • COUNT: The number of variants.
  • ALL: All variants (reference to a static array).
  • iter(): Iterator over all variants.

Helpers for converting to and from the discriminant (requires the enum to have an explicit #[repr(...)] attribute before all other attributes):

  • checked_from_discriminant() & from_discriminant(): Safe and panicking conversions from the discriminant.
  • From implementation from the enum to the discriminant.
  • TryFrom implementation from the discriminant to the enum.

Helpers for using variants as array indices (requires all variants to use implicit discriminants):

See also parser::parsable_enum!, which combined this macro with building a parser.

§Examples

Variant helpers:

utils::enumerable_enum! {
    enum Direction {
        North,
        East,
        South,
        West,
    }
}

assert_eq!(Direction::COUNT, 4);
assert!(matches!(Direction::ALL, &[
    Direction::North,
    Direction::East,
    Direction::South,
    Direction::West,
]));
assert!(matches!(Direction::iter().collect::<Vec<_>>().as_slice(), &[
    Direction::North,
    Direction::East,
    Direction::South,
    Direction::West,
]));

Discriminant helpers:

utils::enumerable_enum! {
    #[repr(u8)]
    #[derive(Copy, Clone, Debug, Default, PartialEq)]
    enum Operation {
        Add,
        Sub,
        Mul,
        Div,
        #[default]
        Noop = 255,
    }
}

assert_eq!(Operation::COUNT, 5);
assert_eq!(Operation::ALL, &[
    Operation::Add,
    Operation::Sub,
    Operation::Mul,
    Operation::Div,
    Operation::Noop
]);
assert_eq!(Operation::iter().collect::<Vec<_>>(), Operation::ALL);
assert_eq!(Operation::from_discriminant(0), Operation::Add);
assert_eq!(Operation::checked_from_discriminant(255), Some(Operation::Noop));
assert_eq!(Operation::checked_from_discriminant(100), None);
assert_eq!(u8::from(Operation::Add), 0u8);
assert_eq!(Operation::try_from(2u8), Ok(Operation::Mul));
assert_eq!(Operation::try_from(4u8), Err(()));

from_discriminant panics on invalid values:

utils::enumerable_enum! {
    #[repr(u8)]
    enum Operation {
        Add,
        Sub,
        Mul,
        Div,
    }
}

Operation::from_discriminant(64);

Index helpers:

utils::enumerable_enum! {
    enum Register {
        A,
        B,
        C,
        D,
    }
}

let mut registers = [0, 1, 2, 3];
assert_eq!(registers[Register::A], 0);
assert_eq!(registers[Register::B], 1);
assert_eq!(registers[Register::C], 2);
assert_eq!(registers[Register::D], 3);
registers[Register::C] = 123;
assert_eq!(registers[Register::C], 123);