macro_rules! enumerate {
    ($(#[$enum_attr:meta])* $visibility:vis $name:ident ($t:ident) $($(#[$attr:meta])* $variant:ident $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis enum $name:ident ($t:ident) $($(#[$attr:meta])* $variant:ident $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis enum $name:ident ($t:ident; $(#[$default_attr:meta])* $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis $name:ident ($t:ident; $(#[$default_attr:meta])* $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
}
Expand description

This macro helps to create enum with trait Enumeration.

You have to pass in

  • attributes (optional)
  • a visibility qualifier (optional)
  • a name
  • a type this enumeration can cast to
  • a type for associated constant values (optional)
    • a default value for associated constant values (optional)
  • at least a variant
    • attributes (before variant) (optional)
    • with associated constant value (only if the type is given) (optional only if default is given)

Note that commas after each variant isn’t a must (; works too). You can also specify enum after visibility

All patterns that start with @ is for internal macro implementation only.

Simple usage

enumerate!(pub Foo(u8)
    Bar
    Baz
);

produces

pub enum Foo {
    Bar,
    Baz,
}

with Enumeration implementation.

Syntax

You might notice that there is no comma after each variant. The macro offers multiple syntax branch that will improve readability and comply to official Rust syntax guidelines.

enumerate!(pub enum Foo(u8) // enum is optional but suggested by official Rust syntax guidelines (https://rust-lang.github.io/api-guidelines/macros.html#input-syntax-is-evocative-of-the-output-c-evocative)
    Bar, // comma is optional
    Baz; // semicolon works too!
);

Casting between index and enumeration

enumerate!(Foo(u8)
    Bar
    Baz
);

assert_eq!(Foo::variant(0)?, Foo::Bar);
assert_eq!(Foo::variant(1)?, Foo::Baz);
assert_eq!(Foo::Bar.index(), 0);
assert_eq!(Foo::Baz.index(), 1);
assert!(Foo::variant(2).is_err());

Associated constant values

enumerate!(Foo(u8; i32)
    Bar = 10
    Baz = 20
);

produces

enum Foo {
    Bar,
    Baz,
}

impl Enumeration for Foo {
    fn value(&self) -> &'static i32 {
        const Bar: i32 = 10;
        const Baz: i32 = 20;
         
        match self {
            Foo::Bar => &Bar,
            Foo::Baz => &Baz,
        }
    }
}

fn example() {
    println!("{}", Foo::Bar.value()); // prints 10
    println!("{}", Foo::Baz.value()); // prints 20
}

Default constant value

enumerate!(Foo(u8; i32 = 20)
    Bar
    Baz = 10
);

produces

enum Foo {
    Bar,
    Baz,
}

impl Enumeration for Foo {
    const DEFAULT_VARIANT_ASSOCIATED_VALUE: Option<i32> = Some(20);

    fn value(&self) -> &'static i32 {
        const Bar: Option<i32> = None;
        const Baz: Option<i32> = Some(10);
         
        match self {
            Foo::Bar => Bar.as_ref().or(Self::DEFAULT_VARIANT_ASSOCIATED_VALUE.as_ref()).unwrap(),
            Foo::Baz => Baz.as_ref().or(Self::DEFAULT_VARIANT_ASSOCIATED_VALUE.as_ref()).unwrap(),
        }
    }
}

fn example() {
    println!("{}", Foo::Bar.value()); // prints 20
    println!("{}", Foo::Baz.value()); // prints 10
}

Note that default constant value is only created once, so the reference to it will always be same.

The macro will emit error if neither associated constant value nor default constant value is provided.

enumerate!(Foo(u8; i32)
    Bar
    Baz = 10
);

Visibility

enumerate!(pub Foo(u8)
    Bar
);

enumerate!(Baz(u8)
    FooBar
);

Attributes

Attributes can be attached to enumeration itself, default constant value and each of the variants. It’s most useful for documentation.

enumerate!(#[doc="An enumeration named Foo"] pub Foo(u8; #[doc="Overwrite default constant value's documentation"] i32 = 0)
    #[doc="Bar"] Bar
    #[doc="Baz"] Baz
);

Examples

enumerate!(pub Color(u8; &'static str)
    Red = "#FF0000"
    Blue = "#0000FF"
    Yellow = "#FFFF00"
    Cyan = "#00FFFF"
);

enumerate!(State(u8)
    None
    Stationary
    Moving
);