#enums #error #declare #macro-derive #derive #macro

simplerror

A zero-dep macro to declaratively define error enum types and their common trait implementations

1 stable release

1.0.0 Feb 1, 2025

#675 in Rust patterns

Download history 108/week @ 2025-01-28 19/week @ 2025-02-04

127 downloads per month

Unlicense

7KB
59 lines

simplerror

A zero-dependency Rust macro to declaratively define your error enums with automatic From, Display, and Error trait implementations. Declare error types which automatically wrap an inner error type while also providing detailed formatted error messages.

simplerror::declare! {
    pub enum MyError {
        SimpleMember => "something went wrong",
        SomeMember(e1: String) => "1234 {e1}",
        AnotherMember(e1: String, e2: String) => "1234 {e1} {e2}",
        BasicMember, // Display::fmt writes "MyError::BasicMember"
    }

    pub enum AnotherError {
        Variant1,
        Variant2 => "message",
        ComposedVariant(e: MyError) => "an inner error: {e}",
    }
}

assert_eq!(MyError::SimpleMember.to_string(), "something went wrong");
assert_eq!(
    MyError::SomeMember("5678".to_string()).to_string(),
    "1234 5678"
);
assert_eq!(
    AnotherError::ComposedVariant(MyError::SimpleMember).to_string(),
    "an inner error: something went wrong"
);

From implementations are handled automatically for any enum members which wrap a single value.

#[derive(Debug)]
struct Foo;

simplerror::declare! {
    pub(crate) enum CustomError {
        Failure => "We failed...",
    }

    // This enum automatically implements From<CustomError> by returning `Self::Wrapped(CustomError)`
    pub(crate) enum WrappingError {
        Wrapped(e: CustomError) => "wrapped: {e}",
    }
}

fn fail_inner() -> Result<(), CustomError> {
    Err(CustomError::Failure)
}

fn fail_outer() -> Result<(), WrappingError> {
    fail_inner()?;
    Ok(())
}

assert!(matches!(
    fail_outer(),
    Err(WrappingError::Wrapped(CustomError::Failure))
));

To derive extra traits on your error enum, or run other procedural macros, you can add them like so above each enum declaration or enum variant like so.

simplerror::declare! {
    #[derive(PartialEq)]
    #[cfg_attr(test, derive(serde::Serialize))]
    enum MyError {
        #[cfg_attr(test, serde(rename_all = "ERR_NOT_FOUND"))]
        ErrorCode404 => "didn't find it",
    }
}

impl MyError {
    fn is_404(&self) -> bool {
        self == &Self::ErrorCode404
    }
}

[!WARNING] To conditionally compile a certain enum, you can wrap the entire declare! invocation in your compilation flag.

If you conditionally compile enum members, or configure-out the enum type itself, you'll encounter compilation errors due to missing references on the automatic trait implementations which declare! generates.

No runtime deps