#enums #proc-macro #variant #associated #create #procedural #function

macro enum-from-functions

A procedural macro that will create an enum with associated functions that each have a corresponding variant

4 releases (2 breaking)

0.3.0 Aug 14, 2023
0.2.0 Jul 25, 2023
0.1.1 Jul 25, 2023
0.1.0 Jul 25, 2023
0.0.0 Jul 24, 2023

#1728 in Procedural macros

30 downloads per month

OSL-3.0 license

17KB
240 lines

enum-from-functions.rs CI/CD Crates.io

A procedural macro that will create an enum with associated functions that each have a corresponding variant.

Documentation can be found inline or on docs.rs.


lib.rs:

This crate contains a procedural macro attribute that can be placed on an impl block. It will generate an enum based on the functions defined in the impl block. The generated enum will have a variant for each function, and a new function map will be added to the impl block that will call the appropriate function based on the variant.

An example:

#[enum_from_functions]
impl Enum {
async fn foo() -> &'static str {
"Foo"
}
unsafe fn bar(baz: i32) -> &'static str {
"Bar"
}
}

expands to:

enum Enum {
Foo,
Bar {
baz: i32
},
}

impl Enum {
async fn foo() -> &'static str {
"Foo"
}
unsafe fn bar(baz: i32) -> &'static str {
"Bar"
}

async unsafe fn map(&self) -> &'static str {
match self {
Enum::Foo => Enum::foo().await,
Enum::Bar(baz) => Enum::bar(baz),
}
}
}

The signatures of functions in the impl block may be different, so long as they all have the same return type.

Note that fn f() -> T and async fn f() -> T are considered to return the same type, even though the latter technically returns a impl Future<Output = T>. See the async keyword documentation for more information.

#[enum_from_functions]
impl Enum {
fn foo(baz: i32) -> &'static str {
"Foo"
}
async fn bar(&self, baz: bool) -> &'static str {
"Bar"
}
}
// Causes a compile error because the return types don't match.
#[enum_from_functions]
impl Enum {
fn foo() -> &'static str {
"Foo"
}
fn bar() -> String {
"Bar".to_owned()
}
}

async, const and unsafe functions are supported. The presence of any of these keywords will result in the generated map function having the same keyword. For this reason, async and const functions cannot be present in the same impl block (though unsafe functions can be present with either of the other two).

#[enum_from_functions]
impl Enum {
async fn foo() -> &'static str {
"Foo"
}
const fn bar() -> &'static str {
"Bar"
}

// This would result in `async const map(...` which is not supported in Rust.
}

You can also create an empty enum by not providing any functions in the impl block (though I'm not sure why you would want to do this).

#[enum_from_functions]
impl EmptyEnum {}

If you need to export the generated enum type out of its parent module, provide the pub argument to the macro attribute.

mod internal {
#[enum_from_functions(pub)]
impl Visible {
fn example() -> bool {
true
}
}
}

// Will compile because the generated `enum` is visible outside of the `internal` module.
use internal::Visible;
mod internal {
#[enum_from_functions]
impl NotVisible {
fn example() -> bool {
false
}
}
}

// Causes a compile error because the generated `enum` is not visible outside of the `internal` module.
use internal::NotVisible;

Items in the impl block that are not functions will be ignored and passed through to the output unchanged. Similarly, any attributes applied before or after the macro attribute will be applied to the generated enum declaration.

#[enum_from_functions]
##[derive(Debug)]
impl Enum {
const FOO: &'static str = "Foo";
fn foo() -> &'static str {
Self::FOO
}

const BAR: &'static str = "Bar";
fn bar() -> &'static str {
Self::BAR
}

const BAZ: &'static str = "Baz";
fn baz() -> &'static str {
Self::BAZ
}
}

Dependencies

~0.9–1.4MB
~27K SLoC