Macro enumerate

Source
macro_rules! enumerate {
    ($(#[$enum_attr:meta])* $visibility:vis enum $name:ident ($t:ident) $($(#[$attr:meta])* $variant:ident $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis $name:ident ($t:ident) $($(#[$attr:meta])* $variant:ident $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis enum $name:ident ($t:ident; $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis one-way enum $name:ident ($t:ident; $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis enum $name:ident ($t:ident; $value_alias:ident : $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis one-way enum $name:ident ($t:ident; $value_alias:ident : $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis one-way $name:ident ($t:ident; $value_alias:ident : $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis one-way $name:ident ($t:ident; $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis $name:ident ($t:ident; $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => { ... };
    ($(#[$enum_attr:meta])* $visibility:vis $name:ident ($t:ident; $value_alias:ident : $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.

§Syntax

<code> means optional

enumerate! {
    <#[attribute] ... #[attribute]>
    <pub> <one-way> <enum> EnumName(EnumType <; <AssociatedValueAlias> AssociatedType <= DefaultValue>>)
        <#[attribute] ... #[attribute]>
        Variant1 <= Value> <,> <;>
        <#[attribute] ... #[attribute]>
        Variant2 <= Value> <,> <;>
        ...
        <#[attribute] ... #[attribute]>
        VariantN <= Value> <,> <;>
}

Arguments (in order)

  • attributes of the enum (optional)
  • a visibility qualifier (optional)
  • one-way keyword to disable implementation of FromValue (optional)
  • a name for the enum
  • an integer type as the underlying type
  • a type for associated constant values (optional)
    • an alias for the name of the association (before the type) (optional)
    • a default value for associated constant values (optional)
  • at least a variant
    • attributes (before variant) (optional)
    • with associated constant value (only if the associated type is given) (optional only if default is given)

Notes:

  • Commas after each variant is not required but accepted by the macro (semicolons are also accepted).
  • Keyword enum is optional but suggested by official Rust syntax guidelines
enumerate! {
    pub enum Foo(u8)
        Bar, // comma is optional
        Baz; // semicolon works too!
}

enumerate! {
    FooBar(u8; char)
        BarBar = 'a'
        BazBar = 'b'
}

enumerate! {
    #[doc="foobaz"]
    pub FooBaz(u8; &'static str = "default string")
        #[doc="barbaz"]
        BarBaz = "hello world"
        BazBaz // value not provided will be default value
}

§Expanded macro

enumerate! {
    pub Foo(u8)
        Bar
        Baz
}

produces

pub enum Foo {
    Bar,
    Baz,
}

with implementation of Enumeration and some other useful traits.

§Casting between index and enumeration

enumerate! {
    Foo(u8)
        Bar
        Baz
}

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

See also Enumeration::variant, Enumeration::variant_unchecked, Enumeration::to_index

§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: &'static i32 = &10;
        const Baz: &'static i32 = &20;
         
        match self {
            Foo::Bar => Bar,
            Foo::Baz => Baz,
        }
    }
}

assert_eq!(Foo::Bar.value(), &10);
assert_eq!(Foo::Baz.value(), &20);

See also Enumeration::value

§Default constant value

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

produces

enum Foo {
    Bar,
    Baz,
}

impl DefaultAssociatedValue for Foo {
    const DEFAULT_ASSOCIATED_VALUE: i32 = 20;
}

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

assert_eq!(Foo::Bar.value(), &20);
assert_eq!(Foo::Baz.value(), &10);

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

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

Note that any constant expression can be used as an associated constant. See also DefaultAssociatedValue::DEFAULT_ASSOCIATED_VALUE

§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 mostly useful for documentation.

enumerate! {
    #[doc="An enumeration named Foo"]
    pub Foo(u8)
        #[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
}

§Two-way conversion

This macro will implement FromValue that allows converting Enumeration::AssociatedValueType back into Enumeration.

enumerate! {
    Enum(u8; char)
        Foo = 'a'
        Bar = 'b'
        Baz = 'c'
}

// will also generate

impl FromValue for Enum {
    fn from_value<'a>(value: &'a Self::AssociatedValueType) -> Result<Self, UnknownAssociatedValueError<'a, Self>> {
        match value {
            &'a' => Ok(Enum::Foo),
            &'b' => Ok(Enum::Bar),
            &'c' => Ok(Enum::Baz),
            _ => Err(UnknownAssociatedValueError(value)),
        }
    }
    fn from_value_unchecked(value: &Self::AssociatedValueType) -> Self {
        match value {
            &'a' => Enum::Foo,
            &'b' => Enum::Bar,
            &'c' => Enum::Baz,
            _ => unreachable!(),
        }
    }
}

§Opting out

You may not desire to have a two-way conversions in some cases like function pointers

fn f() {}

enumerate! {
    Foo(u8; fn())
        Bar = (f as fn())
}

You can opt out of the FromValue implementation by adding the one-way keyword

fn f() {}

enumerate! {
    one-way Foo(u8; fn())
        Bar = (f as fn())
}

More info at Enumeration.

§Alias

You can give the associated value an alias

enumerate! {
    Foo(u8; some_alias: char)
        Bar = 'a'
        Baz = 'b'
}
 
// the left code and the right code is completely same
assert_eq!(Foo::Bar.value(), Foo::Bar.some_alias());
assert_eq!(Foo::Baz.value(), Foo::Baz.some_alias());