crates.io CI commits-since rust 1.51.0+ badge

fatality

A generative approach to creating fatal and non-fatal errors.

The generated source utilizes thiserror::Error derived attributes heavily, and any unknown annotations will be passed to that.

Motivation

For large scale mono-repos, with subsystems it eventually becomes very tedious to match against nested error variants defined with thiserror. Using anyhow or eyre - while it being an application - also comes with an unmanagable amount of pain for medium-large scale code bases.

fatality is a solution to this, by extending thiserror::Error with annotations to declare certain variants as fatal, or forward the fatality extraction to an inner error type.

Read on!

Usage

#[fatality] currently provides a trait Fatality with a single fn is_fatal(&self) -> bool by default.

Annotations with forward require the inner error type to also implement trait Fatality.

Annotating with #[fatality(splitable)], allows to split the type into two sub-types, a Jfyi* and a Fatal* one via fn split(self) -> Result<Self::Jfyi, Self::Fatal>. If splitable is annotated.

The derive macro implements them, and can defer calls, based on thiserror annotations, specifically #[source] and #[transparent] on enum variants and their members.

``rust /// Fatality only works withenumfor now. /// It will automatically add#[derive(Debug, thiserror::Error)]` /// annotations.

[fatality]

enum OhMy { #[error("An apple a day")] Itsgonnabefine,

/// Forwards the `is_fatal` to the `InnerError`, which has to implement `trait Fatality` as well.
#[fatal(forward)]
#[error("Dropped dead")]
ReallyReallyBad(#[source] InnerError),

/// Also works on `#[error(transparent)]
#[fatal(forward)]
#[error(transparent)]
Translucent(InnerError),


/// Will always return `is_fatal` as `true`,
/// irrespective of `#[error(transparent)]` or
/// `#[source]` annotations.
#[fatal]
#[error("So dead")]
SoDead(#[source] InnerError),

} ```

```rust

[fatality(splitable)]

enum Yikes { #[error("An apple a day")] Orange,

#[fatal]
#[error("So dead")]
Dead,

}

fn foo() -> Result<[u8;32], Yikes> { Err(Yikes::Dead) }

fn icallfoo() -> Result<(), FatalYikes> { // availble via a convenience trait Nested that is implemented // for any Result whose error type implements Split. let x: Result<[u8;32], Jfyi> = foo().into_nested()?; }

fn icallfootoo() -> Result<(), FatalYikes> { if let Err(jfyiandfatalones) = foo() { // bail if bad, otherwise just log it log::warn!("Jfyi: {:?}", jfyiandfatal_ones.split()?); } } ```

Roadmap