Github
Github Download crates.io

penum is a procedural macro that is used to make an enum follow a given pattern, which can include generics with trait bounds.

Installation

This crate is available on crates.io and can be used by adding the following to your project's Cargo.toml: toml [dependencies] penum = "0.1.9" Or run this command in your cargo project: sh $ cargo add penum

Overview

A pattern consists of one or more shapes and an optional where clause, which would auto bind all the concrete types that matches your pattern--with the trait bounds you've specified.

Supported
Unsupported

Use case

Normally, using a generic in an enum means that it gets applied to the whole enum, and not per variant. For example, if I want to specify that all variants should be a tuple(T) where T must implement Copy, I'd have to specify a generic for all variants: ``rust enum Foo where T: Copy, U: Copy, F: Copy { Bar(T), Ber(U), Bur(F) // But if I now want to addBor(D)` to this // enum, I'd have to add it manually, and then // bind that generic to impl copy.

// Also, there is nothing stopping me from 
// changing the variant shape to `Bor(D, i32)`.

} This seems kind of tedious, because all we want to do is to make the enum conform to a specific pattern, like this: rust // This forces all current and future variants to // contain one field which must implement Copy.

[penum( (T) where T: Copy )]

enum Foo { Bar(i32), Ber(u32), Bur(f32) } ``` ..which would expand to the first example above.

Examples

It's also possible to make an enum conform to multiple shapes by seperating a shape with | symbol, for example: ```rust

[penum( (T) | (T, T) | { num: T } where T: Copy )]

enum Foo { Bar(i32), Ber(u32, i32), Bur { num: f32 } } ```

Also, If an enum should break a pattern, like if a variant doesn't implement the correct Trait, an error would occur: ```rust

[penum( (T) | (T, T) | { num: T } where T: Copy )]

enum Foo { Bar(String), ^^^^^^ // ERROR: String doesn't implement Copy Ber(u32, i32), Bur { num: f32 } } ..or if a variant doesn't match the specified `shape`: rust

[penum( (T) | (T, T) | { num: T } where T: Copy )]

enum Foo { Bar(u32), Ber(u32, i32, i32), ^^^^^^^^^^^^^ // Found: Ber(u32, i32, i32) // Expected: (T) | (T, T) | { num: T } Bur { num: f32 } } ```

Sometime we don't care about specifying a where clause and just want our enum to follow a specific shape. This is done by specifing _: ```rust

[penum( () | (, _) | { num: _ } )]

enum Foo { Bar(u32), Ber(u32, i32, i32), Bur { num: f32 } } ```

Demo

```rust use penum::shape;

trait Trait {} impl Trait for f32 {} impl Trait for i32 {}

trait Advanced {} impl Advanced for usize {}

[penum( (T, T, U) | (T, U) | { name: T } where T: Trait, U: Advanced )]

enum Vector3 { Integer(i32, f32, usize), Float(f32, i32, usize), }

[penum( { name: _, age: usize } where usize: Advanced )]

enum Strategy<'a> { V1 { name: String, age: usize }, V2 { name: usize, age: usize }, V3 { name: &'a str, age: usize }, }

[penum( { name: &'a str, age: usize } )]

enum Concrete<'a> { Static { name: &'a str, age: usize }, } ```

```rust

[penum( tuple(_) )]

enum Must<'a> { Static { name: &'a str, age: usize } ^^^^^^^^^^^^^^^^^^^^^^^^^^^ // Found: Static { name : & 'a str, age : usize } // Expected: tuple(_) } // Note that this shape has a name (tuple). Right now // it doesn't do anything,but there is an idea of using // regexp to be able to validate on Variant names too.

// Also, there is thoughts about using these Idents to // specify other rules, like if penum should auto implement // a static dispatch for a certain pattern. But this could // also be done by other rules.

[penum( tuple(T) where T: Trait )]

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait boundusize: Traitis not satisfied enum Must { Static (usize) } ```