Skip to content

Commit

Permalink
Support for Other variant
Browse files Browse the repository at this point in the history
  • Loading branch information
RenaudDenis committed Oct 28, 2024
1 parent 0cbeb44 commit 889fd3e
Show file tree
Hide file tree
Showing 6 changed files with 611 additions and 118 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased

- Support for #[repr(type)]
- Support for Other variant

# Version 1.3.0 (2024-07-15)

Expand Down
64 changes: 55 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# UnitEnum Crate Documentation

The `unit-enum` crate provides a procedural macro `UnitEnum` designed to enhance enums in Rust, particularly those
consisting solely of unit variants. This macro simplifies working with such enums by providing useful utility methods.
consisting of unit variants. This macro simplifies working with such enums by providing useful utility methods.

## Features

Expand All @@ -10,13 +10,14 @@ consisting solely of unit variants. This macro simplifies working with such enum
- `from_ordinal`: Convert an ordinal back to an enum variant, if possible.
- `discriminant`: Retrieve the discriminant of an enum variant.
- `from_discriminant`: Convert a discriminant back to an enum variant.
- `len`: Get the total number of variants in the enum.
- `values`: Returns an iterator over all variants of the enum, allowing for easy iteration and handling of each variant.
- `len`: Get the total number of unit variants in the enum (excluding the "other" variant if present).
- `values`: Returns an iterator over all unit variants of the enum.

## Limitations
## Supported Enum Types

- Applicable only to enums with unit variants.
- Enums with data-carrying or tuple variants are not supported and will result in a compile-time error.
The macro supports two types of enums:
1. Enums with only unit variants
2. Enums with unit variants plus one "other" variant for handling undefined discriminant values

## Installation

Expand All @@ -29,6 +30,8 @@ unit-enum = "1.4.0"

## Quick Start

### Basic Usage (Unit Variants Only)

```rust
use unit_enum::UnitEnum;

Expand Down Expand Up @@ -59,7 +62,7 @@ fn main() {
assert_eq!(Color::from_discriminant(10), Some(Color::Red));
assert_eq!(Color::from_discriminant(0), None);

// Get the total number of variants
// Get the total number of unit variants
assert_eq!(Color::len(), 3);

// Iterate over all variants
Expand All @@ -70,13 +73,47 @@ fn main() {
}
```

### Usage with "Other" Variant

```rust
use unit_enum::UnitEnum;

#[derive(Debug, Clone, Copy, PartialEq, UnitEnum)]
#[repr(u16)] // repr attribute is required when using an "other" variant
enum Status {
Active = 1,
Inactive = 2,
#[unit_enum(other)]
Unknown(u16), // type must match repr
}

fn main() {
// from_discriminant always returns a value when "other" variant is present
assert_eq!(Status::from_discriminant(1), Status::Active);
assert_eq!(Status::from_discriminant(42), Status::Unknown(42));

// ordinal treats "other" as the last variant
assert_eq!(Status::Active.ordinal(), 0);
assert_eq!(Status::Unknown(42).ordinal(), 2);

// len returns only the number of unit variants
assert_eq!(Status::len(), 2);

// values iterates only over unit variants
assert_eq!(
Status::values().collect::<Vec<_>>(),
vec![Status::Active, Status::Inactive]
);
}
```

## Discriminant Types

The crate respects the enum's `#[repr]` attribute to determine the type of discriminant values. Supported types include:
- `#[repr(i8)]`, `#[repr(i16)]`, `#[repr(i32)]`, `#[repr(i64)]`, `#[repr(i128)]`
- `#[repr(u8)]`, `#[repr(u16)]`, `#[repr(u32)]`, `#[repr(u64)]`, `#[repr(u128)]`

If no `#[repr]` attribute is specified, the discriminant type defaults to `i32`.
If no `#[repr]` attribute is specified, the discriminant type defaults to `i32`. Note that when using an "other" variant, the `#[repr]` attribute is required and must match the type of the "other" variant's field.

```rust
#[derive(UnitEnum)]
Expand All @@ -92,10 +129,19 @@ enum SmallEnum {
enum LargeEnum {
A = -1_000_000,
B = 5_000_000,
C = 1_000_000_000
#[unit_enum(other)]
Other(i64) // type matches repr
}
```

## Requirements for "Other" Variant

When using an "other" variant, the following requirements must be met:
- The enum must have a `#[repr(type)]` attribute
- Only one variant can be marked with `#[unit_enum(other)]`
- The "other" variant must have exactly one unnamed field matching the repr type
- All other variants must be unit variants

## Contributing

Contributions are welcome! Please feel free to submit pull requests or open issues on our [GitHub repository](https://github.com/tylium/unit-enum).
Expand Down
2 changes: 1 addition & 1 deletion examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use unit_enum::UnitEnum;
enum Color {
Red = 10,
Green,
Blue = 45654
Blue = 45654,
}

fn main() {
Expand Down
41 changes: 41 additions & 0 deletions examples/example_other.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use unit_enum::UnitEnum;

#[derive(Debug, Clone, Copy, PartialEq, UnitEnum)]
#[repr(u32)]
enum Color {
Red = 10,
Green,
Blue = 45654,

#[unit_enum(other)]
Other(u32),
}

fn main() {
// Get the name of a variant
assert_eq!(Color::Blue.name(), "Blue");

// Get the ordinal (position) of a variant
assert_eq!(Color::Green.ordinal(), 1);

// Convert from ordinal back to variant
assert_eq!(Color::from_ordinal(2), Some(Color::Blue));
assert_eq!(Color::from_ordinal(4), None);

// Get the discriminant value (respects the repr type)
assert_eq!(Color::Blue.discriminant(), 45654);
assert_eq!(Color::Green.discriminant(), 11);

// Convert from discriminant back to variant
assert_eq!(Color::from_discriminant(10), Color::Red);
assert_eq!(Color::from_discriminant(0), Color::Other(0));

// Get the total number of variants
assert_eq!(Color::len(), 3);

// Iterate over all variants
assert_eq!(
Color::values().collect::<Vec<_>>(),
vec![Color::Red, Color::Green, Color::Blue]
);
}
71 changes: 57 additions & 14 deletions src/lib.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# UnitEnum

A procedural macro for enhancing unit-only enums with useful methods.
A procedural macro for enhancing enums that consist primarily of unit variants.

This crate provides the `UnitEnum` derive macro which automatically implements
various utility methods for working with unit variant enums. It respects the
various utility methods for working with enums. It respects the
enum's `#[repr]` attribute for discriminant types and provides methods for
accessing variant names, ordinals, and discriminants.

## Usage
## Basic Usage

```rust
use unit_enum::UnitEnum;
Expand Down Expand Up @@ -44,6 +44,33 @@ assert_eq!(
);
```

## Usage with "Other" Variant

The macro also supports enums with an additional "other" variant for handling undefined discriminant values:

```rust
use unit_enum::UnitEnum;

#[derive(Debug, Clone, Copy, PartialEq, UnitEnum)]
#[repr(u16)] // required when using other variant
enum Status {
Active = 1,
Inactive = 2,
#[unit_enum(other)]
Unknown(u16) // type must match repr
}

// from_discriminant always returns a value
assert_eq!(Status::from_discriminant(1), Status::Active);
assert_eq!(Status::from_discriminant(42), Status::Unknown(42));

// values only includes unit variants
assert_eq!(
Status::values().collect::<Vec<_>>(),
vec![Status::Active, Status::Inactive]
);
```

## Features

The `UnitEnum` derive macro provides the following methods:
Expand All @@ -53,8 +80,8 @@ The `UnitEnum` derive macro provides the following methods:
- [`from_ordinal()`](#method.from_ordinal): Convert an ordinal to a variant
- [`discriminant()`](#method.discriminant): Get the variant's discriminant value
- [`from_discriminant()`](#method.from_discriminant): Convert a discriminant to a variant
- [`len()`](#method.len): Get the total number of variants
- [`values()`](#method.values): Get an iterator over all variants
- [`len()`](#method.len): Get the total number of unit variants
- [`values()`](#method.values): Get an iterator over all unit variants

## Discriminant Types

Expand All @@ -74,19 +101,29 @@ enum Small {
enum Large {
X = -1_000_000,
Y = 1_000_000,
#[unit_enum(other)]
Other(i64)
}
```

Supported types include:
- `i8`, `i16`, `i32`, `i64`, `i128`
- `u8`, `u16`, `u32`, `u64`, `u128`

If no `#[repr]` is specified, `i32` is used by default.
If no `#[repr]` is specified, `i32` is used by default. Note that when using an "other" variant,
the `#[repr]` attribute is required and must match the type of the "other" variant's field.

## Limitations
## Requirements

- Only supports enums with unit variants
- Data-carrying variants or tuple variants will cause compile errors
For basic unit-only enums:
- All variants must be unit variants (no fields)
- `#[repr]` attribute is optional, defaults to `i32`

For enums with an "other" variant:
- Must have a `#[repr]` attribute
- Only one variant can be marked with `#[unit_enum(other)]`
- The "other" variant must have exactly one unnamed field matching the repr type
- All other variants must be unit variants

## Generated Methods

Expand All @@ -98,21 +135,27 @@ impl EnumName {
pub fn name(&self) -> &str { ... }
/// Returns the zero-based ordinal (position) of the variant.
/// For enums with an "other" variant, it returns the last ordinal.
pub fn ordinal(&self) -> usize { ... }
/// Converts an ordinal to its corresponding variant, if valid.
/// Returns None for invalid ordinals or the "other" variant.
pub fn from_ordinal(ord: usize) -> Option<Self> { ... }
/// Returns the discriminant value of the variant.
/// For "other" variants, returns the contained value.
pub fn discriminant(&self) -> ReprType { ... }
/// Converts a discriminant value to its corresponding variant, if valid.
pub fn from_discriminant(discr: ReprType) -> Option<Self> { ... }
/// Converts a discriminant value to its corresponding variant.
/// For enums with an "other" variant, always returns a value.
/// For regular enums, returns None for undefined discriminants.
pub fn from_discriminant(discr: ReprType) -> Self { ... } // or -> Option<Self>
/// Returns the total number of variants in the enum.
/// Returns the total number of unit variants (excluding "other" variant).
pub fn len() -> usize { ... }
/// Returns an iterator over all variants of the enum.
/// Returns an iterator over all unit variants of the enum.
/// The "other" variant is not included in the iteration.
pub fn values() -> impl Iterator<Item = Self> { ... }
}
```
```
Loading

0 comments on commit 889fd3e

Please sign in to comment.