sigma-enum-macros

Crates.iosigma-enum-macros
lib.rssigma-enum-macros
version0.1.2
created_at2025-12-30 06:26:04.883557+00
updated_at2026-01-01 02:27:06.840011+00
descriptionProcedural macros for `sigma_enum`.
homepage
repositoryhttps://github.com/milojacquet/sigma-enum
max_upload_size
id2012172
size76,709
(milojacquet)

documentation

README

sigma_enum is a procedural macro that allows a family of types to be summed into an enum and pattern matched on. It implements Σ types, also known as dependent pairs. The macro exports a set of derive macros that allow runtime values to be lifted to compile time.

Quick start

use sigma_enum::sigma_enum;

#[derive(Debug)]
struct Bytes<const N: usize>([u8; N]);

// Define sigma enum
#[sigma_enum(generic(Bytes<usize>))]
enum BytesEnum {
    __(usize), // A standalone type
    #[sigma_enum(expand(N = 0..10))]
    __(Bytes<N>), // Types indexed by a const generic
}

let n: usize = "8".parse().unwrap();
// Construct based on a runtime value
let bytes = bytes_enum_construct!(Bytes::<?n>(Bytes([0x41; n]))).unwrap();

// Match on the const generic in the type
let displayed = bytes_enum_match!(match bytes {
    usize(bytes) => format!("usize: {bytes}"),
    Bytes::<?N>(bytes) => format!("{N} bytes: {bytes:?}"),
});

Basic usage

The most basic use for sigma_enum is applying it to an enum of tuple struct variants, each of which has one value.

#[sigma_enum]
enum Numeric {
    __(i32),
    __(i64),
}

The names of the variants will be automatically generated if the provided names start with an underscore, and the provided names will be used otherwise.

Generating an enum that simulates a type that depends on a const generic value can be done by using the const generic in the type, or in shorthand with the expand attribute. Valid specifications for expand metavariables are literals, ranges, and arrays of those.

In order to use const generics in an enum, the attribute macro should be annotated with the const generic types used within using the generic attribute. If not, certain functionality will be unavailable. Non-const generics can be annotated with _. Since the const generic types are used in declarative macros, fully qualified names should be used.

struct Array<T, const N: usize>([T; N]);

#[sigma_enum(generic(Array<_, ::std::primitive::usize>))]
enum BytesEnum {
    #[sigma_enum(expand(N = 0..3))]
    __(Array<u8, N>),
}

// equivalent to
#[sigma_enum(generic(Array<_ ,usize>))]
enum BytesEnum2 {
    __(Array<u8, 0>),
    __(Array<u8, 1>),
    __(Array<u8, 2>),
}

Types used as enum variants for now must only be written with identifiers, literals, and <>.

Renaming variants

In addition to specifying the name of a variant with the name used in the enum, renaming can also be done with the rename attribute. Used on a standard variant, it can be used to select a name for the variant. Used on a variant with the expand attribute, a format string can be provided and the metavariables used will be interpolated into it.

struct Array<T, const N: usize>([T; N]);

#[sigma_enum(generic(Array<_, ::std::primitive::usize>))]
enum BytesEnum {
    #[sigma_enum(expand(N = 0..3), rename = "ByteArray{N}")]
    __(Array<u8, N>),
}

// equivalent to
#[sigma_enum(generic(Array<_, ::std::primitive::usize>))]
enum BytesEnum2 {
    ByteArray0(Array<u8, 0>),
    ByteArray1(Array<u8, 1>),
    ByteArray2(Array<u8, 2>),
}

Renaming types

The only types allowed in variant specifications are those written as unqualified identifiers, optionally with generic parameters. For qualified type names, use the alias attribute.

mod inner {
    pub struct Foo;
}

#[sigma_enum(alias(Foo = inner::Foo))]
enum Foo {
    __(Foo),
}

Generated items

Macros

sigma_enum generates several macros for each enum.

The first is the construction macro. This allows for the construction of values whose types involve const generics even when the values of the const generics only exist at runtime. Associated to a type T, the construction macro returns a value of type Option<T>. Metavariables used in the type specification must be preceded with ?.

struct Bytes<const N: usize>([u8; N]);

#[sigma_enum(generic(Bytes<usize>))]
enum BytesEnum {
    #[sigma_enum(expand(N = 0..3))]
    __(Bytes<N>),
}

let n: usize = 1;
let bytes = bytes_enum_construct!(Bytes::<?n>(Bytes([0x41; n]))).unwrap();

Dual to the construction macro is the match macro. This facilitates the use of the enum as any of its contained types. Metavariables used in the type specification must be preceded with ?.

#[derive(Debug)]
struct Bytes<const N: usize>([u8; N]);

#[sigma_enum(generic(Bytes<usize>))]
enum BytesEnum {
    #[sigma_enum(expand(N = 0..3))]
    __(Bytes<N>),
}

fn displayed(bytes: BytesEnum) -> String {
    bytes_enum_match!(match bytes {
        Bytes::<?N>(bytes) => format!("{N} bytes: {bytes:?}"),
    })
}

Traits

The sigma_enum macro also generates a conversion trait for each enum with methods for constructing values of the enum of a known variant and extracting a value of a known type from the enum. Helper methods on the enum for extraction are also generated.

struct Bytes<const N: usize>([u8; N]);

#[sigma_enum(generic(Bytes<usize>))]
enum BytesEnum {
    #[sigma_enum(expand(N = 0..3))]
    __(Bytes<N>),
}

let bytes_enum = Bytes([0x41; 2]).into_bytes_enum(); // uses IntoBytesEnum trait
let bytes: &Bytes<2> = Bytes::<2>::try_from_bytes_enum(&bytes_enum).unwrap(); // uses IntoBytesEnum trait
let bytes: Bytes<2> = bytes_enum.extract_owned().unwrap();

From, Into, TryFrom, and TryInto will also be implemented between the enum and all types it contains as variants.

Public API generation

Marking the enum pub will export the generated macros and traits as part of the public API.

For enums whose macros you intend to use in another module of the crate, you must add the path attribute that contains the absolute path to the module.

For public enums, you may not use the generated macros in the same crate they were defined in. This is a limitation of Rust's macro system (cf. issue #52234). In this case, adding the path attribute will generate two sets of macros: one that is exported at the crate root, and another usable in the definition crate whose names are suffixed by _crate.

pub mod inner {
    pub struct Foo;

    #[sigma_enum(path = crate::inner)]
    pub enum FooEnum {
        __(Foo),
    }
}

inner::foo_enum_construct_crate!(Foo(inner::Foo));
// foo_enum_construct!(Foo(inner::Foo)); // cannot refer to this macro
}

Renaming generated items

Generated items can be renamed and docstrings can be provided with the following attributes:

Item Attribute name
Construction macro macro_construct
Match macro macro_match
Enum trait into_trait
Enum trait construction method into_method
Enum trait extraction method try_from_method
Enum trait owned extraction method try_from_owned_method
Enum trait mut extraction method try_from_mut_method
Enum extraction method extract_method
Enum owned extraction method extract_owned_method
Enum mut extraction method extract_mut_method
Enum trait error try_from_error
#[sigma_enum(
    macro_construct(name = make_numeric, docs = "Make a numeric value."),
    macro_match(name = match_numeric, docs = "Match a numeric value.")
)]
enum Numeric {
    __(i32),
    __(i64),
}

Additional information

Derive macros and other item attributes will work when placed below the sigma_enum macro. Variant attributes will be copied to every instance of the variant if expanded.

Commit count: 0

cargo fmt