#enums #conversion #protocols #macro #convert

macro nested_enum_utils

Macros to provide conversions for nested enums

1 unstable release

0.1.0 Jul 3, 2024

#1682 in Procedural macros

Download history 86/week @ 2024-06-27 915/week @ 2024-07-04 933/week @ 2024-07-11 1120/week @ 2024-07-18 959/week @ 2024-07-25 830/week @ 2024-08-01 2264/week @ 2024-08-08 1730/week @ 2024-08-15 768/week @ 2024-08-22 809/week @ 2024-08-29

5,599 downloads per month
Used in 2 crates (via iroh)

MIT/Apache

12KB
155 lines

Nested enum utils

This crate provides a single attribute macro to provide conversions from enum cases to the enum itself or to some other type.

It only works with enums where each variant has a single unnamed element, and if each variant has a distinct type.

The most basic use is to provide conversions between the enum cases and the enum type itself. You could achieve something similar with the popular derive_more crate.

#[enum_conversions()]
enum Request {
  Get(GetRequest),
  Put(PutRequest),
}

A more advanced use, and the reason for this crate to exist, is to provide conversions between enum variants and any type that itself has a conversion to the enum. This allows to use nested enums, like you would in a complex protocol that has several subsystems.

#[enum_conversions(Request)]
enum StoreRequest {
  Get(GetRequest),
  Put(PutRequest),
}

#[enum_conversions(Request)]
enum NetworkRequest {
  Ping(PingRequest),
}

#[enum_conversions()]
enum Request {
  Store(StoreRequest),
  Network(NetworkRequest),
}

Here we define conversions from GetRequest to StoreRequest, from StoreRequest to Request, and then directly from GetRequest to Request, and corresponding TryFrom conversions in the other direction.

Generated conversions

The generated From conversions are straightforward. Obviously it is always possible to convert from an enum case to the enum itself.

We also generate TryFrom conversions from the enum to each variant, as well as from a reference to the enum to a reference to the variant.

The conversions that take a value are different than the ones from derive_more: they return the unmodified input in the error case, allowing to chain conversion attempts.

let request = ...
match GetRequest::try_from(request) {
  Ok(get) => // handle get request
  Err(request) => {
    // I still got the request and can try something else
    match PutRequest::try_from(request) {
      ...
    }
  }
}

The conversions that take a reference just return a &'static str as the error type. References are Copy, so we can always retry anyway.

Dependencies

~3MB
~60K SLoC