#match-enums #proc-macro #match #enums

no-std matched_enums

A macro that provides the option to bin enum attribute to match-statements. This makes it easier to convert values into enums.

1 stable release

new 1.1.0 Apr 13, 2025

#79 in No standard library

Download history 89/week @ 2025-04-09

89 downloads per month

Custom license

24KB
407 lines

Matched Enum

Rust allows you to specify integer values for enumerations which make it easy to convert integers to enums. However, such luxary does not exist for complex patterns like range-based matching. This library aims to add such functionality. It will respect the option to assign integer values to enumartion allowing idiomatic derives from Ord for comparisons. While allowing for enumerations to be derived from ranges. This conversion is achieved through the From trait.

For example:

use matched_enums::Matched;

#[derive(Matched, PartialEq, Debug)]
#[matched_enum(value_type=i32)]
enum Signature {
    #[matches(1..)]
    Positive,

    #[matches(..=-1)]
    Negative,

    #[matches(0..1)]
    Zero,
}

assert_eq!(Signature::from(-42), Signature::Negative);
assert_eq!(Signature::from(0), Signature::Zero);
assert_eq!(Signature::from(451), Signature::Positive);

NOTE: Checkout the examples for more detailed and complex examples.

Features

This library offers the following features:

  • runtime_configurable: Allows the creation of runtime configurable structures.

Attributes

After deriving from Matched, the following macros are available.

Enum macros

This library uses the following macros:

#[derive(Matched, ..)]

When enabled allows the use of the other macros.

#[matched_enum(..)]

Configure the behavior of the macro using key-value pairs (configured using key=value). The following keys are available:

Name Type Default Description
allow_runtime_configurable Boolean Literal false If the runtime_configurable feature is enabled, it will generate a struct with a Matcher postfix. For example: enum Foo will have struct FooMatcher. This matcher, holds binary predicates for each enumeration. Defaulting to the normal matcher[^runtime_match_order].
use_partial Boolean Literal false When used, implements TryFrom instead of From. The implementation will use the _ matcher after all other statements to return an error if it failed. This means it can only be used if the input is only partially covered.
value_type | value_types Any type or array of types i32 Specify the type used from which the enum can be constructed.

Attribute macros

matches(..)

Indicates the criteria for matching a specific enum attribute as well as some configuration options passed as key-value pairs. Arguments should match the pattern matches(<match-arm>[[,<attribute>=value]..]).

The match-arm can be any MatchArm. Some examples:

  • Default: #[_][^match_default]
  • Direct match: #[matches(1)].
  • Joins: #[matches(0|1)]
  • Guards: #[1 | _ if { i.set(i.get() + 1); false }]

The following attributes are available:

Name Type Default Description
bind Any type None Binds the current match arm to the provided type.

Design Decisions

If you are curious about certain decisions that have shaped this package over time. Please check out the design-decisions as this contains such detailed information.

TODO

A list of tasks that still need to achieved. These items are ordered arbitrarily and have no importance in their order.

NOTE: These issues are not yet tracked in git as most have been defined since the start before it was hosted online.

Minor items

  • Allow support for additional allowing construction from templated types.
    • If value_type is set to MyType<T>, From should be implemented with the generic types: impl<T> From<MyType<T>> for Enum.
  • Allow doc support to override the From::from documentation allowing specification of, for example, pre-conditions / assumptions.
  • Add tags to the main Cargo.toml to find this easier (proc-macro, enum, match, etc.)
  • Add a precedence attribute to matches which, optionally, allows one to specify the order of match priority without relying on enum declarations (if desired).
  • Add support for a DefaultMatcher as an alias to a Matcher, making it easier to use.
  • Ensure the that unbound match attributes (i.e. without typebind) are used as the default attribute across all other types (i.e. acting as a 'default' argument).
  • Add support for more typing options in the matcher. For example a tuple, slice, etc.: https://docs.rs/syn/latest/syn/enum.Type.html
  • Add an example with a multiple matchers of generic types. (i.e. explain the need for the turbo fish, see templated_type)

Major items

  • Add support for runtime changeable ranges.
    • The ranges should span $(-\infty, \infty)$ when no allow_incomplete is provided, else it is accepted.
      • In case a value_type has been specified, it must span $[T_{MIN}, T_{MAX}]$ instead, where T represents the value_type.
      • The default range should be checked in compile time.
      • Using the runtime interface should return a Result<Self>. Returning an "IncompleteRangeError" if invallid.
  • Allow partial matching to happen on a per-type basis instead of on a general basis
    • For example a uint8 should be fully matchable, but an i32 should have partial matching.
    • The current situation, where allow_partial dictates whether all type should be matched partially or fully, ought to be repurposed as the default.
  • Allow value binding if needed. For example if an enum attribute can accept the same type as it is trying to convert from, it should be injected into the enum attribute. For example, if the value_type is an int32 and the attribute is something like Foo(i32). The value should be injected into foo.
    • The same should be done for runtime-matching.

Example of Matched enum:

[^match_default]: There is an intention to add a try_from returning an Option<Self>. It is recommended to use this once added and chaining the Option with .or_default. Allowing for more idiomatic default construction.

[^runtime_match_order]: When using the runtime configurable match options, the Matcher will go through all of the predicates linearly in a top-to-bottom order. If, a wild card predicate is used before any of the other predicates, the matcher will always go for the wild card.

Dependencies

~1–1.6MB
~28K SLoC