1 unstable release

0.1.0 May 19, 2019

#98 in #error-handling

MIT/Apache

9KB
68 lines

The #[powerset_enum] attribute parametrizes an enum to make it a powerset (set of all subsets), and create a macro with the same name of the enum for easy notation of the subsets.

Note: this is a nightly only crate, and to use it you need to enable the following feature flags:

#![feature(never_type, exhaustive_patterns, proc_macro_hygiene)]

Each variant of the enum decorated by #[powerset_enum] must be a tuple-struct variant with a single item, and the type of that item must be unique within that enum. Parametrization of the enum (beside the one created by #[powerset_enum]) is not supported.

To use a specific parametrization, use a macro with the same name of the enum and provide to it the list of types you require.

An upcast method is created on the enum type to convert any subset to any superset of that subsets. Usually used with Result::map_err.

fn foo(...) -> Result<..., E![A, B]> {
    ...
}
fn bar(...) -> Result<..., E![A, B, C, D]> {
    foo(...).map_err(E::upcast)
}

The [Extract] trait provides an extract method on the enum type and on [Result] with the enum as their error to extract a new [Result] where the OK value is the original value without the extracted variant and the error is the extracted variant:

fn baz(...) -> Result<..., E![A, B, D]> {
    bar(...).extract::<C>().expect("C is not allowed at all")
}
#![feature(never_type, exhaustive_patterns, proc_macro_hygiene)]

pub struct Exception1;
pub struct Exception2;
pub struct Exception3;

#[powerset_enum]
pub enum Error {
    Exception1(Exception1),
    Exception2(Exception2),
    Exception3(Exception3),
}

fn foo(x: usize) -> Result<usize, Error![Exception1, Exception2]> {
    Ok(match x {
        1 => Err(Exception1)?,
        2 => Err(Exception2)?,
        x => x,
    })
}

fn bar(x: usize) -> Result<usize, Error![Exception1, Exception3]> {
    if x == 3 {
        Err(Exception3)?;
    }
    foo(x)
        // Specifically handle `Exception2`:
        .extract::<Exception2>().unwrap_or(Ok(2))
        // Convert `Result<usize, Error![Exception1]>` to `Result<usize, Error![Exception1, Exception3]>`:
        .map_err(Error::upcast)
}

fn main() {
    let x = 1;

    match foo(x) {
        Ok(n) => println!("OK - got {}", n),
        Err(Error::Exception1(_)) => println!("Got exception 1"),
        Err(Error::Exception2(_)) => println!("Got exception 2"),
        // No Exception3 match arm needed - `foo` cannot return it
    }

    match bar(x) {
        Ok(n) => println!("OK - got {}", n),
        Err(Error::Exception1(_)) => println!("Got exception 1"),
        // No Exception2 match arm needed - `bar` cannot return it
        Err(Error::Exception3(_)) => println!("Got exception 3"),
    }
}

Dependencies

~2MB
~48K SLoC