4 releases

0.1.3 Mar 18, 2023
0.1.2 Mar 18, 2023
0.1.1 Mar 18, 2023
0.1.0 Mar 18, 2023

#1081 in Procedural macros

27 downloads per month

Apache-2.0 OR MIT

36KB
856 lines

crates-io docs-rs github

err-as-you-go

Generate enum error types inline.

If you want:

  • to easy throw errors inline like with anyhow
  • to make your error types handleable in a nice enum like thiserror

then this is the crate for you!

use err_as_you_go::err_as_you_go;

#[err_as_you_go]
fn shave_yaks(
    num_yaks: usize,
    empty_buckets: usize,
    num_razors: usize,
) -> Result<(), ShaveYaksError> {
    if num_razors == 0 {
        return Err(err!(NotEnoughRazors));
    }
    if num_yaks > empty_buckets {
        return Err(err!(NotEnoughBuckets {
            got: usize = empty_buckets,
            required: usize = num_yaks,
        }));
    }
    Ok(())
}

Under the hood, a struct like this is generated:

enum ShaveYaksError { // name and visibility are taken from function return type and visibility
    NotEnoughRazors,
    NotEnoughBuckets {
        got: usize,
        required: usize,
    }
}

Importantly, you can derive on the generated struct, and passthrough attributes, allowing you to use crates like thiserror.


#[err_as_you_go(derive(Debug, thiserror::Error))]
fn shave_yaks(
    num_yaks: usize,
    empty_buckets: usize,
    num_razors: usize,
) -> Result<(), ShaveYaksError> {
    if num_razors == 0 {
        return Err(err!(
            #[error("not enough razors!")]
            NotEnoughRazors
        ));
    }
    if num_yaks > empty_buckets {
        return Err(err!(
            #[error("not enough buckets - needed {required}")]
            NotEnoughBuckets {
                got: usize = empty_buckets,
                required: usize = num_yaks,
            }
        ));
    }
    Ok(())
}

Which generates the following:

#[derive(Debug, thiserror::Error)]
enum ShaveYaksError {
    #[error("not enough razors!")]
    NotEnoughRazors,
    #[error("not enough buckets - needed {required}")]
    NotEnoughBuckets {
        got: usize,
        required: usize,
    }
}

And err! macro invocations are replaced with struct instantiations - no matter where they are in the function body!

If you need to reuse the same variant within a function, just use the normal construction syntax:

#[err_as_you_go]
fn foo() -> Result<(), FooError> {
    fallible_op().map_err(|e| err!(IoError(io::Error = e)));
    Err(FooError::IoError(todo!()))
}

Dependencies

~3–12MB
~122K SLoC