#enums #handler #state #bloc

enum_handler

A macro to generate a handler trait for enums variants

1 unstable release

0.1.0 Aug 21, 2024

#534 in Rust patterns

MIT/Apache

45KB
1K SLoC

Rust Enum Handler Derive Macro

This project provides a macro for handling Rust enums in a convenient and efficient way.

The #[derive(EnumHandler)] macro simplifies the process of matching and handling different enum variants, allowing for cleaner and more readable code.

It generates a trait with a common handler method with match statements for each variant of the enum, allowing you to easily handle each case separately.

// You declare the CounterEvent enum:

use enum_handler::EnumHandler;

#[derive(EnumHandler)]
pub enum CounterEvent {
    Increment,
    Decrement,
    Reset,
    Set(i32),
}

// and the enum_handler macro will generate the following code for you behind the scenes:

pub trait CounterEventHandler {
    fn on(&self, e: CounterEvent) -> () {
        match (e) {
            CounterEvent::Increment => self.on_increment(),
            CounterEvent::Decrement => self.on_decrement(),
            CounterEvent::Reset => self.on_reset(),
            CounterEvent::Set(arg) => self.on_set(arg),
        }
    }
    fn on_increment(&self) -> ();
    fn on_decrement(&self) -> ();
    fn on_reset(&self) -> ();
    fn on_set(&self, set: i32) -> ();
}

The generated code can be highly customized to suit your specific needs.

Eg. you can specify:

  • Names for the generated trait and methods
  • async or sync methods
  • pass arguments by value (default, changes ownership) or by reference
  • generate default implementations for the methods
    • specify a return value
  • specify a common return type for each method
  • specify visibility for the generated trait and methods
  • output the generated code to a file for debug purposes
  • mock all support

Project Structure

The project is structured as follows:

Crate Description
enum_handler The library crate that exposes the #[enum_handler()] attribute macro. This is the crate that you will include in your project!
enum_handler_derive The procedural macro crate that implements the #[derive(EnumHandler)] macro.
enum_handler_core The core crate that contains almost all the logic for the macro. This crate is shared between the library and the derive crate. You can see examples is the tests directory and the tests.rs file in the enum_handler_core crate.

Configuration

With the #[enum_handler()] attribute macro, you can customize the generated code by specifying the following options:

Option Type Default Description
trait_suffix String "Handler" Specifies the suffix for the generated trait name which will be appended to the enum name.
trait_name String "" If specified, the generated trait will have this name instead of the default one.
handler_name String on Specifies the name of the common handler method. This is also used as a prefix for the generated method names (separator is _).
return_type String () Specifies the common return type for each method.
default_return_value String () Specifies the common return value for each method if the default implementations are generated.
is_async bool false Specifies whether the generated methods should be asynchronous (true) or synchronous (false).
default_implementation bool true Specifies whether default implementations should be generated for the methods (true) or not (false).
visibility String "" Specifies the visibility for the generated trait and methods. If not specified, the visibility of the enum is used.
no_async_trait_macro bool false Specifies whether to use the #[async_trait::async_trait] macro (false) or not (true). This is only relevant if is_async is true. The async_trait crate must be included in the [dependencies].
mock_name String "" If specified, a mockall trait will be generated with this name. The mockall crate must be included in the [dev-dependencies].
pass_args_by_ref bool false Specifies whether the arguments should be passed by reference (true) or by value (false).

Examples

Here are a few examples to demonstrate the usage of the #[derive(EnumHandler)] macro:

Async Methods

use enum_handler::EnumHandler;

#[derive(EnumHandler)]
#[enum_handler(is_async = true)]
pub enum CounterEvent {
    Increment,
    Decrement,
    Reset,
    Set(i32),
}

Custom Trait Name

use enum_handler::EnumHandler;

#[derive(EnumHandler)]
#[enum_handler(trait_name = "CounterHandler")]

pub enum CounterEvent {
    Increment,
    Decrement,
    Reset,
    Set(i32),
}

Custom Handler Method Name

use enum_handler::EnumHandler;

#[derive(EnumHandler)]
#[enum_handler(handler_name = "handle")]
pub enum CounterEvent {
    Increment,
    Decrement,
    Reset,
    Set(i32),
}

Custom Return Type

use enum_handler::EnumHandler;

#[derive(EnumHandler)]
#[enum_handler(return_type = "i32")]
pub enum CounterEvent {
    Increment,
    Decrement,
    Reset,
    Set(i32),
}

Mockall Support

use enum_handler::EnumHandler;

#[derive(EnumHandler)]
#[enum_handler(mock_name = "MockCounterHandler")]
pub enum CounterEvent {
    Increment,
    Decrement,
    Reset,
    Set(i32),
}

Write generated code to a file

You can set the environment variable ENUM_HANDLER_DEBUG to write the generated code to a file.

ENUM_HANDLER_DEBUG=/tmp/enum_handler.rs cargo build
  • If the file is greater than 128kB, it will be truncated.
  • This is only available for debug purposes and should not be used in production code.

If you want to format the generated code with rustfmt, you can set the environment variable ENUM_HANDLER_DEBUG_FORMAT to 1.

ENUM_HANDLER_DEBUG=/tmp/enum_handler.rs ENUM_HANDLER_DEBUG_FORMAT=1 cargo build
  • The file will be formatted with rustfmt if it is available.

You can also use cargo-expand to see the generated code:

cargo install cargo-expand
cargo expand --help

Known Issues

  • This crate is under heavy development and may have braking changes in the future.
  • The #[async_trait::async_trait] macro usage will be improved in a future release.
  • The rustfmt formatting will be customizable in a future release.
  • The names of the tuple variant parameters will be changed in a future release.
  • The minimum supported Rust version (MSRV) is currently set to 1.80.1. This will be reviewed in a future release.

Contributing

Contributions are welcome! If you have any ideas, suggestions, or bug reports, please open an issue or submit a pull request on the GitHub repository.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~0.6–1MB
~24K SLoC