Giter Site home page Giter Site logo

eyre's Introduction

eyre

Build Status Latest Version Rust Documentation Discord chat

This library provides eyre::Report, a trait object based error handling type for easy idiomatic error handling and reporting in Rust applications.

This crate is a fork of anyhow with support for customized error reports. For more details on customization checkout the docs on eyre::EyreHandler.

Custom Report Handlers

The heart of this crate is its ability to swap out the Handler type to change what information is carried alongside errors and how the end report is formatted. This crate is meant to be used alongside companion crates that customize its behavior. Below is a list of known crates that export report handlers for eyre and short summaries of what features they provide.

  • stable-eyre: Switches the backtrace type from std's to backtrace-rs's so that it can be captured on stable. The report format is identical to DefaultHandler's report format.
  • color-eyre: Captures a backtrace::Backtrace and a tracing_error::SpanTrace. Provides a Help trait for attaching warnings and suggestions to error reports. The end report is then pretty printed with the help of color-backtrace, color-spantrace, and ansi_term. Check out the README on color-eyre for details on the report format.
  • simple-eyre: A minimal EyreHandler that captures no additional information, for when you do not wish to capture Backtraces with errors.
  • jane-eyre: A report handler crate that exists purely for the pun of it. Currently just re-exports color-eyre.

Usage Recommendations and Stability Considerations

We recommend users do not re-export types from this library as part their own public API for libraries with external users. The main reason for this is that it will make your library API break if we ever bump the major version number on eyre and your users upgrade the eyre version they use in their application code before you upgrade your own eyre dep version1.

However, even beyond this API stability hazard, there are other good reasons to avoid using eyre::Report as your public error type.

  • You export an undocumented error interface that is otherwise still accessible via downcast, making it hard for users to react to specific errors while not preventing them from depending on details you didn't mean to make part of your public API.
    • This in turn makes the error types of all libraries you use a part of your public API as well, and makes changing any of those libraries into an undetectable runtime breakage.
  • If many of your errors are constructed from strings you encourage your users to use string comparision for reacting to specific errors which is brittle and turns updating error messages into a potentially undetectable runtime breakage.

Details

  • Use Result<T, eyre::Report>, or equivalently eyre::Result<T>, as the return type of any fallible function.

    Within the function, use ? to easily propagate any error that implements the std::error::Error trait.

    use eyre::Result;
    
    fn get_cluster_info() -> Result<ClusterMap> {
        let config = std::fs::read_to_string("cluster.json")?;
        let map: ClusterMap = serde_json::from_str(&config)?;
        Ok(map)
    }
  • Wrap a lower level error with a new error created from a message to help the person troubleshooting understand the chain of failures that occurred. A low-level error like "No such file or directory" can be annoying to debug without more information about what higher level step the application was in the middle of.

    use eyre::{WrapErr, Result};
    
    fn main() -> Result<()> {
        ...
        it.detach().wrap_err("Failed to detach the important thing")?;
    
        let content = std::fs::read(path)
            .wrap_err_with(|| format!("Failed to read instrs from {}", path))?;
        ...
    }
    Error: Failed to read instrs from ./path/to/instrs.json
    
    Caused by:
        No such file or directory (os error 2)
  • Downcasting is supported and can be by value, by shared reference, or by mutable reference as needed.

    // If the error was caused by redaction, then return a
    // tombstone instead of the content.
    match root_cause.downcast_ref::<DataStoreError>() {
        Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
        None => Err(error),
    }
  • If using the nightly channel, a backtrace is captured and printed with the error if the underlying error type does not already provide its own. In order to see backtraces, they must be enabled through the environment variables described in std::backtrace:

    • If you want panics and errors to both have backtraces, set RUST_BACKTRACE=1;
    • If you want only errors to have backtraces, set RUST_LIB_BACKTRACE=1;
    • If you want only panics to have backtraces, set RUST_BACKTRACE=1 and RUST_LIB_BACKTRACE=0.

    The tracking issue for this feature is rust-lang/rust#53487.

  • Eyre works with any error type that has an impl of std::error::Error, including ones defined in your crate. We do not bundle a derive(Error) macro but you can write the impls yourself or use a standalone macro like thiserror.

    use thiserror::Error;
    
    #[derive(Error, Debug)]
    pub enum FormatError {
        #[error("Invalid header (expected {expected:?}, got {found:?})")]
        InvalidHeader {
            expected: String,
            found: String,
        },
        #[error("Missing attribute: {0}")]
        MissingAttribute(String),
    }
  • One-off error messages can be constructed using the eyre! macro, which supports string interpolation and produces an eyre::Report.

    return Err(eyre!("Missing attribute: {}", missing));
  • On newer versions of the compiler (e.g. 1.58 and later) this macro also supports format args captures.

    return Err(eyre!("Missing attribute: {missing}"));

No-std support

No-std support was removed in 2020 in [commit 608a16a] due to unaddressed upstream breakages. [commit 608a16a]: https://github.com/eyre-rs/eyre/pull/29/commits/608a16aa2c2c27eca6c88001cc94c6973c18f1d5

Comparison to failure

The eyre::Report type works something like failure::Error, but unlike failure ours is built around the standard library's std::error::Error trait rather than a separate trait failure::Fail. The standard library has adopted the necessary improvements for this to be possible as part of RFC 2504.

Comparison to thiserror

Use eyre if you don't think you'll do anything with an error other than report it. This is common in application code. Use thiserror if you think you need an error type that can be handled via match or reported. This is common in library crates where you don't know how your users will handle your errors.

Compatibility with anyhow

This crate does its best to be usable as a drop in replacement of anyhow and vice-versa by re-exporting all of the renamed APIs with the names used in anyhow, though there are some differences still.

Disabling the compatibility layer

The anyhow compatibility layer is enabled by default. If you do not need anyhow compatibility, it is advisable to disable the "anyhow" feature:

eyre = { version = "0.6", default-features = false, features = ["auto-install", "track-caller"] }

Context and Option

As part of renaming Context to WrapErr we also intentionally do not implement WrapErr for Option. This decision was made because wrap_err implies that you're creating a new error that saves the old error as its source. With Option there is no source error to wrap, so wrap_err ends up being somewhat meaningless.

Instead eyre offers OptionExt::ok_or_eyre to yield static errors from None, and intends for users to use the combinator functions provided by std, converting Options to Results, for dynamic errors. So where you would write this with anyhow:

use anyhow::Context;

let opt: Option<()> = None;
let result_static = opt.context("static error message");
let result_dynamic = opt.with_context(|| format!("{} error message", "dynamic"));

With eyre we want users to write:

use eyre::{eyre, OptionExt, Result};

let opt: Option<()> = None;
let result_static: Result<()> = opt.ok_or_eyre("static error message");
let result_dynamic: Result<()> = opt.ok_or_else(|| eyre!("{} error message", "dynamic"));

NOTE: However, to help with porting we do provide a ContextCompat trait which implements context for options which you can import to make existing .context calls compile.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Footnotes

  1. example and explanation of breakage https://github.com/eyre-rs/eyre/issues/30#issuecomment-647650361 โ†ฉ

eyre's People

Contributors

9999years avatar akshayknarayan avatar alexwlchan avatar alyoshavasilieva avatar asonix avatar birkenfeld avatar chris-laplante avatar d4h0 avatar dtolnay avatar hoverbear avatar iamsaquib8 avatar jaysonsantos avatar johnschug avatar kellpossible avatar killercup avatar knutwalker avatar kolloch avatar kornelski avatar kylewlacy avatar leoniephiline avatar phil-opp avatar pksunkara avatar repi avatar stupremee avatar ten3roberts avatar thebutlah avatar thenorili avatar timdiekmann avatar workingjubilee avatar yaahc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eyre's Issues

Allow use under miri (aka: fix UB / update `Error` impl to contain some of `anyhow`'s bugfixes since Jan)

I'd like to move to using eyre over anyhow more. One downside is that eyre doesn't have the changes that make anyhow pass under miri, which means if I use eyre, much of it needs to be excluded from miri.

Also, there's at least case where the old code was UB in a way that the mutable noalias addition can exploit in theory (although in practice I suspect it would be very difficult). Fixing that requires most of the work to fix the other things, and in general

See dtolnay/anyhow#134 for the original PR to anyhow (which I wrote, for essentially the same reason that I'd like this fixed in eyre), which talks about explicitly which patterns are bad, and gives some background.

In general the TL;DR is "doing dodgy things to &T, &mut T, Box<T>1 is very bad, so you need to use raw pointers". A concrete example is that stuff like ErrorImpl<()> pretty much needs to be held only behind a raw pointer. Instead of using an &ErrorImpl<()> to get at the fields, you need to eitehr use core::ptr::addr_of!/addr_of_mut!, or convert to *const ErrorImpl<E> which can be derefed. There are several other things like this, which are discussed in that PR.

The ergonomics of that PR are quite bad, but it got up cleaned it up significantly by introducing https://github.com/dtolnay/anyhow/blob/master/src/ptr.rs afterwards.

These basically just give NonNull more semantic meaning, and making deref/deref_mut unsafe (even though it probably should explain when it's safe to use it (rarely) and erm, use the correct bounds for Send/Sync ๐Ÿ˜“)

I'm willing to do the work to make this happen, but... realistically it's probably going to make the code more complex and harder to maintain2, so I figured I'd give a heads up first.

Footnotes

  1. Including ManuallyDrop<Box<T>> โ†ฉ

  2. I wish this weren't the case, but the ergonomics of raw pointers are pretty awful across the board :( โ†ฉ

Wrapping errors that do not implement Send

Hi there, first of all, thank you very much for this project. It makes working with errors in cli implementations really pleasant and fun!
I was trying to wrap an error that comes from a library I wrote that uses thiserror:

#[derive(Debug, Error)]
pub enum MambembeKeyringError {
    #[error("password not stored in the keyring yet")]
    NoPasswordFound,
    #[error("deserialization error")]
    DeserializationError(#[from] serde_json::Error),
    #[error("unknown keyring backend error")]
    UnknownBackendError(#[from] KeyringError),
}

Only this should be ok but on UnknownBackendError, that KeyringError can contain a KeyringError(OsError), and on windows, OsError is windows::Error.
I noticed the error when I tried to use ? on a function that uses eyre::Result and the following error showed up:

error[E0277]: `NonNull<c_void>` cannot be sent between threads safely
  --> cli\src\main.rs:71:45
   |
71 |             mambembe_keyring::set(&services)?;
   |                                             ^ `NonNull<c_void>` cannot be sent between threads safely
   |
   = help: within `MambembeKeyringError`, the trait `Send` is not implemented for `NonNull<c_void>`
   = note: required because it appears within the type `windows::interfaces::unknown::IUnknown`
   = note: required because it appears within the type `windows::bindings::windows::win32::winrt::IRestrictedErrorInfo`
   = note: required because it appears within the type `Option<windows::bindings::windows::win32::winrt::IRestrictedErrorInfo>`
   = note: required because it appears within the type `windows::result::error::Error`
   = note: required because it appears within the type `keyring::error::KeyringError`
   = note: required because it appears within the type `MambembeKeyringError`
   = note: required because of the requirements on the impl of `From<MambembeKeyringError>` for `ErrReport`
   = note: required by `from`

And my first thought was to wrap it with eyre using the suggestions here:

fn wrap_keyring_error(e: MambembeKeyringError) -> Report {
    // This is done because windows::Error does not allow Send
    eyre!(e)
}

so I could write:

mambembe_keyring::set(&services).map_err(wrap_keyring_error)?;

but the following error returns:

error[E0599]: no method named `eyre_kind` found for reference `&MambembeKeyringError` in the current scope
   --> cli\src\main.rs:177:5
    |
177 |     eyre!(e)
    |     ^^^^^^^^ method not found in `&MambembeKeyringError`
    | 
   ::: C:\Users\....\mambembe\keyring\src\lib.rs:17:1
    |
17  | pub enum MambembeKeyringError {
    | -----------------------------
    | |
    | doesn't satisfy `MambembeKeyringError: Into<ErrReport>`
    | doesn't satisfy `MambembeKeyringError: Send`
    | doesn't satisfy `MambembeKeyringError: Sync`
    | doesn't satisfy `_: color_eyre::eyre::private::kind::TraitKind`
    |
    = note: the method `eyre_kind` exists but the following trait bounds were not satisfied:
            `MambembeKeyringError: Into<ErrReport>`
            which is required by `MambembeKeyringError: color_eyre::eyre::private::kind::TraitKind`
            `MambembeKeyringError: Send`
            which is required by `&MambembeKeyringError: color_eyre::eyre::private::kind::AdhocKind`
            `MambembeKeyringError: Sync`
            which is required by `&MambembeKeyringError: color_eyre::eyre::private::kind::AdhocKind`
            `&MambembeKeyringError: Into<ErrReport>`
            which is required by `&MambembeKeyringError: color_eyre::eyre::private::kind::TraitKind`
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

Do you see a solution for that?
Thanks in advance!

Converting to monorepo

It would be useful to convert the eyre, color-eyre, stable-eyre et.al. It would improve the discoverability of the different subcrates, as well as provide a single place where issues can be reported.

The current separate repositories makes it difficult for users to know where to report the errors as it may not initially be obvious if the source is in Eyre or an eyre hook implementer. It is a case of the classic "where should it go"

Merging them will improve the ease of issue creation and discovery, as well as overview.

The proposed structure will also improve ease of integration testing and examples and side-by-side development between the crates.

[Feature Request] Support conversion to pyo3::PyErr

Since eyre aims to provide a developer friendly error reporting mechanism, it would be great to not have to lose those pretty errors when crossing a FFI boundary from Rust to Python. The excellent PyO3 crate provides a way to convert from Rust errors to python exceptions via the PyErr struct.

Because of the orphan rules, this trait can only be implemented by either PyO3 or by eyre or by a newtype that the user of the API makes. Because forcing people to make a newtype isn't in line with being developer friendly or "works out of the box", I think it makes the most sense to implement it here, gated under a feature flag like pyo3 or something so that the non-python people don't need the extra dependency.

I haven't yet explored exactly how the contextual backtrace/diagnostic info that eyre provides would actually be displayed in python, so I thought we could discuss.

Eyre gets flagged by Miri.

Miri reports problems related to stacked borrows and strict provenance when running it over Eyre.

Here is the error I got:
image

I also got another error previously but could not reproduce it.

Even though the stacked borrows and strict provenance memory models are experimental and what you are doing may not be treated as UB by the compiler currently, you should probably still address this, because these memory models will probably be stabilised at some point, and it clutters peoples miri output. And having Miri detect potential UB in libraries you depend on is pretty scary.

bail!() and ensure!() docs incorrectly claim it's equivalent to Err(From::from($err))

The docs on bail!() and ensure!() claim that these macros are equivalent to code returning Err(From::from($err)):

https://github.com/yaahc/eyre/blob/54933ea76d12960fb4b954c81c2d288481ff9b03/src/macros.rs#L3
https://github.com/yaahc/eyre/blob/54933ea76d12960fb4b954c81c2d288481ff9b03/src/macros.rs#L66

This appears to have been inherited from anyhow originally, although anyhow has since updated its docs.

These macros should instead claim that they're equivalent to Err(eyre!($err)) (or possibly Err(eyre!($args...)) like anyhow's current docs). Also, like current anyhow, it would be nice to use intra-doc links here.

Why does the Location section sometimes point inside eyre itself?

Sometimes when using eyre and printing out the error, the location sections points inside of eyre itself instead of the line at which the error occurred. What is the cause of this, and how do I avoid it?

Example error message:

Error: 
   0: Could not open file at "/bogus/path"
   1: No such file or directory (os error 2)

Location:
   /home/ryan/.cargo/registry/src/github.com-1ecc6299db9ec823/eyre-0.6.3/src/context.rs:25

Backtrace omitted.
Run with RUST_BACKTRACE=1 environment variable to display it.
Run with RUST_BACKTRACE=full to include source snippets.

Code snippet:

use eyre::{Result, WrapErr};

fn main() -> Result<()> {
    color_eyre::install()?;

    let args = Args::parse();

    let mut reader: Box<dyn Read + Send> = if let Some(ref path) = args.path {
        let reader = std::fs::File::open(path)
            .wrap_err(format!("Could not open file at {:?}", path))?;
        Box::new(reader)
    } else {
        // ....
    };

    //...
}

Converting from anyhow::Error

Hi,
I am porting multiple crates from anyhow to eyre, so I have code that looks something like this:

fn foo() -> Result<(), anyhow::Error> {
  Ok(())
}

fn bar() -> Result<(), eyre::Report> {
  let f = foo().map_err(|e| eyre::Report::new(e))?;
  Ok(())
}

This, as expected, gives

error[E0277]: the trait bound `anyhow::Error: std::error::Error` is not satisfied
  --> src/lib.rs:6:49
   |
6  |     let f = foo().map_err(|e| eyre::Report::new(e))?;
   |                                                 ^ the trait `std::error::Error` is not implemented for `anyhow::Error`
   |
  ::: /home/akshayn/.cargo/registry/src/github.com-1ecc6299db9ec823/eyre-0.6.0/src/error.rs:21:12
   |
21 |         E: StdError + Send + Sync + 'static,
   |            -------- required by this bound in `eyre::error::<impl eyre::Report>::new`

error: aborting due to previous error

Since anyhow::Error and eyre::Report are so similar, is there a better way to construct an eyre::Report mid-stack than this, which is sad and throws out the context:

fn foo() -> Result<(), anyhow::Error> {
  Ok(())
}

fn bar() -> Result<(), eyre::Report> {
  let f = foo().map_err(|e| eyre::eyre!("{:?}", e))?;
  Ok(())
}

Thanks for the help!

Integration with the `tracing` crate

First of all, thanks for the great crate. I've been waiting for a similar library for a long time. I highly like the idea of having a custom handler, because it allows more tight integration with an environment.

I'm writing the actor system and want, among other things, to provide a good experience with error handling:

  • Strip extra stack frames up to the current actor and leave only valuable frames.
  • Cache stack traces in LRU to reduce the overhead of capturing. (it's useful in low latency scenarios).
  • Postpone frame resolving until the logger actor actually prints events to avoid resolving in actors.
  • Provide a location and stack frames of errors to logs.

eyre allows this by providing custom handlers.

However, now I am stuck in integration with the tracing crate (elfo's logging system is based on it now).
My goal is to print something like

... something wrong    error="aa"   error.source="bb"  error.location="path/to/file:42"  error.stacktrace=[..]

for calls like

error!(error = &some_eyre_error as &dyn Error, "something wrong");

The problem is that Report doesn't implement Error.
I understand that it's impossible now, so I just want to hear an opinion on how it can be solved. Maybe I miss some easy solution.

Maybe, eyre should provide some wrapper that implements Error and have a method to expose internally stored Report? Of course, it can be provided by elfo, but it complicates using libraries that depend only on eyre and tracing.

Related:

How to use a EyreHandler without installing it

Hello! Thanks for this library, I'm really enjoying it.

I would like to use an EyreHandler in a one-off fashion to write to a string, though I couldn't work out how to do this. Oops.

Would you be able to give me any tips?

Thanks,
Louis

Document how to wrap an `eyre::Report` with a different error type

Wrapping a source error type as the cause of an eyre::Report is very straightforward with the wrap_err function. But suppose I have an eyre::Result<()> and want to convert it into a Result<(), ()> without discarding the helpful backtrace/spantrace from eyre? Is that even possible?

If it is, can it be documented?

remove generic parameter for handling and replace with a global handler hook

Based on dtolnay/anyhow#97 I'd like to transition eyre away from a generic parameter based API for error reporting customization and instead transition to using a global handler hook. This has a number of trade-offs vs the generic parameter based approach, but I think overall the global hook design the better overall set of advantages.

Advantages

  • Converting between context types is a non issue
  • Type inference becomes a non issue
  • libraries that export eyre::Report don't need to take care to expose the Handler type as a generic parameter
  • more uniform with panic customization options, allows for unified reporting customization libraries for panic handlers and error handlers
  • faster compile times due to lack of generics

Disadvantages

  • Adds an extra dynamic allocation when constructing error reports
  • multiple calls to set the global hook could race against each other
  • combinators that mutate the context need to deal with fallibility in their downcast

Overall I think the type inference / ergonomic exposure of customization context for libraries are more valuable than the better efficiency and compile time context type checking advantages of the generic based approach. I'd also like to finalize the PR on anyhow to potentially get these same changes built into our upstream library.

Maintenance Status

Hello @yaahc . What is the current status of this library?

I've found it to be fantastic to use (especially with color-eyre) for constructing rich errors as well as getting a readable panic hook.

However, I've had to go back to anyhow and stop using this library in flax and other libraries due to the Miri/memory invalidations as well as other missing features.

It would be great to have this crate up to speed.

If you do not have the time, which is completely understandable as an open-source maintainer myself, is there any other crate which achieves a similar goal of rich errors and panic messages that you would recommend?

Best regards, Tei

0.6.9 fails to build on unstable-less nightly

When testing with RUSTFLAGS=-Zallow-features= cargo +nightly test to emulate the upcoming stable compiler eyre 0.6.9 attempts to use an unstable feature anyway and fails to build

error[E0725]: the feature `rustdoc_missing_doc_code_examples` is not in the list of allowed features
Error:    --> /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/eyre-0.6.9/src/lib.rs:320:13
    |
320 |     feature(rustdoc_missing_doc_code_examples),
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Backtrace is not available due to Error API change

I'm not sure this issue is tracking or not (or even intended), current build.rs script do not add backtrace feature as it failed to compile since Error does not have backtrace() API rather https://rust-lang.github.io/rfcs/3192-dyno.html.

I found this issue while comparing anyhow and found that eyre does not emit valid backtrace like anyhow. The reason is that eyre do not set backtrace feature as I explained above.

Anyhow has changed API (dtolnay/anyhow@46d3d2c).

I modified to follow the latest API and it worked at least with minimal change. Can we as eyre support such change as well?

color-spantrace: print_source_if_avail: can't extract source if working dir is crate in workspace

Hi,

I'm debugging my PR for color_eyre at the moment. For some reason, the source code lines were not printed out.

The reason was, that I'm using a workspace and that tracing_core::metadata::Metadata contains a file path relative to the workspace, not my current working directory.

So I'm in "$workspace/color_eyre" and execute a test.

This line is then executed because filename is "color-spantrace/tests/color_schemes.rs" โ€“ which is relative to the workspace, not my current working directory:

        // `filename` can't be found, because it's relative to the workspace
        let file = match File::open(filename) {
            Ok(file) => file,
            // return without printing the source:
            Err(ref e) if e.kind() == ErrorKind::NotFound => return Ok(()),
            e @ Err(_) => e.unwrap(),
        };

I believe this is only a problem for people who work on color_spantrace, and not for end-users who install color_spantrace via cargo (but I'm not 100% sure).

I'm reporting this in case there is an easy workaround (I can't see one), or in case this could lead to problems for end-users.

Feel free, to close this if you don't think this is a problem.

Maybe a note in the README for people who want to work on color_spantrace would make sense?

Using eyre with `Result<_,&str>`

I am trying to use eyre for unwrapping Options, for which I use ok_or and &str as the Err-type. I figured this would work with eyre, since &str and String implement std::error::Error. However, the following code does not compile:

None.ok_or("test").wrap_err("oops")

with

     = note: the method `wrap_err_with` exists but the following trait bounds were not satisfied:
             `&str: eyre::context::ext::StdError<_>`
             which is required by `std::result::Result<usize, &str>: eyre::WrapErr<usize, &str, _>`

Do I need to implement Report for &str to get this working? If so, maybe it's good to add these Report implementations for &str and String in the eyre crate itself?

Handle future-incompat dependencies

pyo3 is a direct dependency and can be upgraded to 0.20.0 painlessly.

nom is 3 major versions behind and comes via ansi-parser 0.8.0. I'll reach out to that project and ask for an upgrade =]

result of cargo test:

warning: the following packages contain code that will be rejected by a future version of Rust: nom v4.2.3, pyo3 v0.13.2 note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`

`

  • Some affected dependencies have newer versions available.
    You may want to consider updating them to a newer version to see if the issue has been fixed.

nom v4.2.3 has the following newer versions available: 5.0.0-alpha1, 5.0.0-alpha2, 5.0.0-beta1, 5.0.0-beta2, 5.0.0-beta3, 5.0.0, 5.0.1, 5.1.0, 5.1.1, 5.1.2, 5.1.3, 6.0.0-alpha1, 6.0.0-alpha2, 6.0.0-alpha3, 6.0.0-beta1, 6.0.0-beta2, 6.0.0-beta3, 6.0.0-beta4, 6.0.0-beta5, 6.0.0, 6.0.1, 6.1.0, 6.1.1, 6.1.2, 6.2.0, 6.2.1, 6.2.2, 7.0.0-alpha1, 7.0.0-alpha2, 7.0.0-alpha3, 7.0.0, 7.1.0, 7.1.1, 7.1.2, 7.1.3
pyo3 v0.13.2 has the following newer versions available: 0.14.0, 0.14.1, 0.14.2, 0.14.3, 0.14.4, 0.14.5, 0.15.0, 0.15.1, 0.15.2, 0.16.0, 0.16.1, 0.16.2, 0.16.3, 0.16.4, 0.16.5, 0.16.6, 0.17.0, 0.17.1, 0.17.2, 0.17.3, 0.18.0, 0.18.1, 0.18.2, 0.18.3, 0.19.0, 0.19.1, 0.19.2, 0.20.0
`

build.rs probe fails to propagate `--target` and does not use RUSTC_WRAPPER

In tricky build situations, such as when bootstrapping rustc or for cross-compilation, it is crucial to set the right --target argument for rustc invocations. The build.rs script of eyre doesn't do that, and I think that's why I am now getting build failures in eyre inside a rustc bootstrap situation:

error[E0407]: method `backtrace` is not a member of trait `StdError`
   --> /home/r/.cargo/registry/src/github.com-1ecc6299db9ec823/eyre-0.6.8/src/context.rs:147:5
    |
147 | /     fn backtrace(&self) -> Option<&Backtrace> {
148 | |         self.error.backtrace()
149 | |     }
    | |_____^ not a member of trait `StdError`

Basically, I think eyre needs the same patch as dtolnay/anyhow#249.
(FWIW autocfg gets all this right, but it seems a lot of crates re-implement the autocfg logic -- incorrectly.)

Add feature flags for integration with foreign crates

Due to coherence users of eyre cannot implement traits defined in their dependencies for eyre::ErrReport. We should add feature flags to support known integrations so that users don't need to write cumbersome newtype wrappers.

Possible crates to support

  • Reject trait from warp
  • Serialization/Deserialization traits from serde

Add `prelude` module

It might be nice to have a prelude module that contains the macros, Report, and WrapErr. This way use eyre::prelude::*; will be equivalent to

use eyre::{bail, ensure, eyre, Report, WrapErr};

as those are the items I typically want to import everywhere I use eyre (more specifically I only import the macros I use so I don't get warnings, but that the means the other macros aren't available when I want them).

Notably this should not include Result, as taking over the bare Result identifier without explicitly importing it would be surprising.

Integrating with actix-web

Hi, great library, I'm enjoying it. I would like to somehow support the ? operator in my actix-web app. The actix-web HTTP handler's support a Result<_,_> return value so that means ? will work, but the Err variant needs to implement their ResponseError trait: https://github.com/actix/actix-web/blob/master/actix-web/src/error/response_error.rs .

I can't implement ResponseError for eyre::Result because of the orphan rule. Any tips on how I could use eyre::Result in this scenario? And food for thought, here is how I hacked some existing code to use the stdlib Result in actix-web:

use actix_web::ResponseError;
use std::fmt::{Display, Formatter};

#[derive(Debug)]
pub struct WebError {}

impl Display for WebError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "WebError{{}}")
    }
}

impl ResponseError for WebError {}

#[get("/exports/{id}/run")]
pub async fn exports_run(id: Identity, req: HttpRequest) -> Result<HttpResponse, WebError> {
    let identity = id.identity().unwrap();

    let pg_conn = pg_conn();

    let id = req.match_info().get("id").unwrap();
    let id: i32 = id.parse().map_err(|_| WebError {})?;

    let job: ExportJob = export_jobs::table
        .filter(export_jobs_dsl::identity.eq(&identity))
        .filter(export_jobs_dsl::id.eq(id))
        .first::<ExportJob>(&pg_conn)
        .map_err(|_| WebError {})?;

    let athena_query = sparkle_athena::StreamableAthenaResult {
        athena_database: job.athena_database,
        athena_view: job.athena_view,
    };

    let csv = execute_query(athena_query).await.map_err(|_| WebError {})?;

    let mut oauth2_token = oauth_tokens_dsl::oauth2_tokens
        .filter(oauth_tokens_dsl::identity.eq(&identity))
        .first::<OAuth2Token>(&pg_conn)
        .map_err(|_| WebError {})?;
    oauth2_token
        .refresh(&pg_conn)
        .await
        .map_err(|_| WebError {})?;


    Ok(HttpResponse::Ok().body("not implemented yet"))
}

`eyre!` (the macro) is kind of a weird name

Hi. I started using eyre recently, switching from anyhow, and while it's not a big deal, it kind of bothers me every time I write eyre!(...). As a non-native speaker I don't even know what it means (even after quickly googling "define eyre"), I keep forgetting how to spell it and so on. I don't mind this being the crate name - because I usually write it less often, rust-analyzer can often just auto-add the import for me, and I I understand that there's a limited number of good crate names that are not taken. But the macro, I get no rust-analyzer help, have to type it myself and it looks weird and non-informative to anyone not-familiar with that crate.

anyhow! was kind of weird already, but at least the word seemed more familiar.

Also, I'd like to point out - possibly to both authors of anyhow and eyre - that having these unique weird macro names makes anyhow = { crate ="eyre", ... } be much less likely to "just work".

Is there any reason this macro couldn't be just named err!(...) or something generic and informative like this?

Anyway, I enjoy this crate a lot, so thanks a lot for working on it!

Avoid filename collision in the monorepo structure

Error 1 below appears in both stable and nightly builds.

It's slightly more impactful than just a warning. Running cargo +nightly test, cargo test, cargo +nightly test in windows produces a linker error where color-spantrace tries to use eyre's usage.exe, see error 2 below. It's worked around pretty easily with cargo clean, but it's a problem that might get worse as the monorepo effort expands!

[1]
warning: output filename collision.
The example target usage in package eyre v0.6.8 (C:\Users\nori\dev\eyre\eyre) has the same output filename as the example target usage in package color-spantrace v0.2.0 (C:\Users\nori\dev\eyre\color-spantrace).
Colliding filename is: C:\Users\nori\dev\eyre\target\debug\examples\usage.exe
The targets should have unique names.
Consider changing their names to be unique or compiling them separately.
This may become a hard error in the future; see rust-lang/cargo#6313.

[2]
= note: LINK : fatal error LNK1104: cannot open file 'C:\Users\nori\dev\eyre\target\debug\examples\usage.exe'

error: could not compile color-spantrace (example "usage") due to previous error

Make EyreHandlers composible

Right now eyre's design essentially requires that there's a single "Handler" type that is globally used in the application. Every error Report that is created will use the globally installed constructor for this handler type to create a new handler to go with the report to store additional context and print the error report + context. This is then meant to be paired with handler().downcast::<MyExpectedHandlerType>() when attempting to insert additional context into a Report as it propagates up the stack.

The problem is, if you are a library author who wishes to leverage eyre to hook additional functionality into error reporting you end up mandating that your users use your expected error report handler type, and if they ever install a different one all your downcasts will fail.

For a while my main plan for solving this problem has been to leverage https://github.com/mystor/object-provider to allow data to flow both ways across the dyn trait boundary without needing to downcast. This at least lets additional context be stored, so long as your users install a handler that will carry and provide your context the way you want them too, but this seems onerous and easy to forget to do.

I was looking at https://github.com/kellpossible/sentry-eyre today and trying to see what problems they were trying to solve and might run into and it dawned on me that what they might want is the ability to capture and send error report to sentry whenever they're printed, regardless of where the printing happens. The act of reporting it to a log or to a user would notify the back end.

That plus another PR that was opened today, eyre-rs/color-eyre#67, which enables composition of panic handlers, made me realize that this same approach might be very useful for EyreHandler as well. If it was possible to take the Box<dyn EyreHandler> out of a report and replace it with another one we could effectively allow libraries to tap into the error handler behind a report to add additional context and functionality to reporters.

Problems

So now I need to figure out how or if this can be done. The first issue that comes to mind is losing the ability to downcast back to the original type when you wrap it, thus breaking existing integrations such as .suggestion as provided by color-eyre. It might be possible to work around this by doing some shenanigans in the downcast impls in eyre, similar to how it already supports downcasting to two alternate types for errors constructed with wrap_err.

Another alternative is to build the composition into the trait itself by adding an inner() method or something, essentially an equivalent to source() on the Error trait. Though I think I'd rather leverage the object-provider crate for this, which lets us solve more than one problem at a time.

Yet another alternative is to allow the Report type to store multiple handlers, which are called sequentially during reporting. This would then probably require some changes to the external interface of eyre so that it can downcast to the correct types but it also seems like it is probably the most intuitive approach to use. I'd likely opt to deprecate the existing handler interface which I doubt anyone uses, rather than making a breaking change, since I already know from experience that that causes immediate issues to be filed in this repo, lol. Report does not match Report is not a fun error.

add doc_cfg on all `std` api's

Right now we have nominal support for no_std thanks to anyhow, but its not really clear which APIs are available and which aren't when using std vs no_std.

To help with this we should use the new doc_cfg feature to mark APIs that are and aren't available with std enabled. The tracing-subscriber repo is a good example to use as a reference for how to setup doc_cfg.

https://docs.rs/tracing-subscriber/

Doesn't work correctly with new Rust inline formatting

Nowadays Rust supports inline formatting for variables like format!("Expected {a} to be {b}").

This syntax works correctly in format!, format_args!, anyhow! and a lot of other contexts.

However, in eyre, somewhat surprisingly, it treats the string as a literal and prints without interpolation, even though eyre itself has support for formatting when arguments are passed explicitly. It seems this stems from all macros (ensure!, bail!, eyre!) having a separate branch that catches string literal with no following arguments in a special way:

macro_rules! eyre {
    ($msg:literal $(,)?) => { ... };
    ($err:expr $(,)?) => { ... };
    ($fmt:expr, $($arg:tt)*) => { ... };
}

changes for 0.4.0

  • Add tutorial on usage
  • rename to Report
  • remove member_ref/member_mut, change backtrace to return an Option, add backtrace fn to EyreContext trait
  • Copy extra info from readme to docs and maybe rewrite a bit
  • Dig into converting between context types
  • Serialization and deserialization
  • Warp reject trait
  • Migration guide from anyhow

Fix MSRV CI

The CI is failing due to the 1.42 version subset of the test matrix.

https://github.com/eyre-rs/eyre/blob/master/.github/workflows/ci.yml#L69

The reason it is failing seems to be due to thiserror requiring the edition = 2021. We use thiserror in tests and documentation. I see a couple of potential directions we could go in resolving this issue:

  • adjust the MSRV test to only build eyre, not attempt to run tests
  • bump the MSRV to 1.56, the compiler version for the 2021 edition
  • bump the version of the MSRV test but leave the reported MSRV in the readme at the current version and only update that MSRV based on user reports of breakage.
  • disable subsets of the tests that use thiserror when testing MSRV
  • remove thiserror usage entirely

I think I lean toward the first option. I cannot anticipate any situations where we'd see breakage in runtime behavior exercised by tests across versions that we wouldn't catch due to this change. If we build in the old version and tests pass in the current version, we're almost certainly fine.

Moving a compiled binary to a clean OS would throw `cannot install provided ErrorHook`

I'm compiling binaries from this project, and it's working fine on the dev machine. However, when I move the binaries to a clean linux os, running the bins returns this error:

$ cast
Error: cannot install provided ErrorHook, a hook has already been installed

Location:
cli/src/handler.rs:70:13

and the line in question is:

eyre::set_hook(Box::new(move |_| Box::new(Handler)))?;

There's also a related issue where people have ran into the same problem on Win 10.

What is causing this?

Improve test support for `no_std`

Right now eyre does have support for no_std by virtue of the upstream support for no_std errors in anyhow. However anyhow never implemented support for testing no_std. It would be nice to update the tests to compile and pass when --no-default-features is used, however this will require adding #[cfg(feature = "std")] all over the test code, and to the doc tests.

Make adding paths to I/O errors even simpler

Currently the docs suggest doing something like

let content = fs::read(path)
    .wrap_err_with(|| format!("Failed to read instrs from {}", path.display()))?;

This is such a common case that the above code just becomes boilerplate. It would be nicer if it were just

let content = fs::read(path).wrap_err_with_path(path)?;

Remove obsolete private_in_public lint in nightly.

RFC 2145 is on nightly now, deprecating the public_in_private lint.

https://rust-lang.github.io/rfcs/2145-type-privacy.html

See PR #113

public_in_private has been superceded by three new lints. The first two are warn-by-default and the third is allow-by-default. See the excerpt below for some details.

Lint private_interfaces is reported when a type with visibility x is used in primary interface of an item with effective visibility y and x < y. This lint is warn-by-default.
Lint private_bounds is reported when a type or trait with visibility
x is used in secondary interface of an item with effective
visibility y and x < y. This lint is warn-by-default.

Lint unnameable_types is reported when effective visibility of a
type is larger than module in which it can be named, either
directly, or through reexports, or through trivial type aliases
(type X = Y;, no generics on both sides). This lint is
allow-by-default.

Compatibility lint private_in_public is never reported and removed.

Provide implementations of `From<&str>` and `From<String>` for `Report`

The standard library has the implementations From<'_ &str> for Box<dyn Error> and From<String> for Box<dyn Error>. This allows for example the following, convenient code:

fn foo() -> Result<i32, Box<dyn Error>> {
    let opt: Option<i32> = None;
    opt.ok_or("The Option was None");
}

assert!(foo().is_err());

Basically I use it a lot to change Options into Results in application code. When using eyre as a drop-in replacement for Result<..., Box<dyn Error>> I have to change all of these ok_or calls and wrap the strings with an extra macro call to eyre!. This is inconvenient. Therefore this is a request to provide these implementations.

References:

`eyre!(x)` where `x: Arc<eyre::Report>` loses trace

Hi, when you for some reason have Arc<eyre::Report> and try to wrap it into a eyre::Report using eyre!(...), it loses traces from the report (previously added with wrap_err, etc.).

Why would you have Arc<eyre::Report> you ask? For example moka returns Arc wrapped errors from some of its functions.

doctests fail to compile with thiserror 1.0.40

Running cargo test works up until thiserror 1.0.39, but starts to fail with thiserror 1.0.40 - these two doctests fail to compile:

test src/macros.rs - macros::ensure (line 85) ... FAILED
test src/macros.rs - macros::bail (line 25) ... FAILED

This can be reproduced by running:

cargo update
cargo test

Running these two commands makes tests pass again:

cargo update --package thiserror --precise 1.0.39
cargo test

The test suite of anyhow failed with similar errors in some recent versions, but the most recent version(s) apparently fixed this problem. It might be necessary to backport some anyhow changes into eyre. It seems to be related to the update to syn ^2.

Complete error message:

---- src/macros.rs - macros::ensure (line 85) stdout ----
error: unexpected token
  --> src/macros.rs:98:2
   |
15 | }, 1).1,
   |  ^

error[E0599]: the method `eyre_kind` exists for reference `&ScienceError`, but its trait bounds were not satisfied
   --> src/macros.rs:104:1
    |
9   | enum ScienceError {
    | -----------------
    | |
    | doesn't satisfy `ScienceError: Into<ErrReport>`
    | doesn't satisfy `ScienceError: eyre::kind::TraitKind`
    | doesn't satisfy `ScienceError: std::fmt::Display`
...
21  | ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `&ScienceError` due to unsatisfied trait bounds
    |
    = note: the following trait bounds were not satisfied:
            `ScienceError: Into<ErrReport>`
            which is required by `ScienceError: eyre::kind::TraitKind`
            `ScienceError: std::fmt::Display`
            which is required by `&ScienceError: eyre::kind::AdhocKind`
            `&ScienceError: Into<ErrReport>`
            which is required by `&ScienceError: eyre::kind::TraitKind`
note: the traits `Into` and `std::fmt::Display` must be implemented
   --> /home/deca/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:786:1
    |
786 | pub trait Display {
    | ^^^^^^^^^^^^^^^^^
    |
   ::: /home/deca/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:447:1
    |
447 | pub trait Into<T>: Sized {
    | ^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `$crate::eyre` which comes from the expansion of the macro `ensure` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0599`.
Couldn't compile the test.
---- src/macros.rs - macros::bail (line 25) stdout ----
error: unexpected token
  --> src/macros.rs:38:2
   |
15 | }, 1).1,
   |  ^

error[E0277]: the trait bound `ScienceError: std::error::Error` is not satisfied
  --> src/macros.rs:43:47
   |
20 |     let err: &'static dyn std::error::Error = &ScienceError::RecursionLimitExceeded;
   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `ScienceError`
   |
   = note: required for the cast from `ScienceError` to the object type `dyn std::error::Error`

error[E0599]: the method `eyre_kind` exists for reference `&ScienceError`, but its trait bounds were not satisfied
   --> src/macros.rs:46:5
    |
9   | enum ScienceError {
    | -----------------
    | |
    | doesn't satisfy `ScienceError: Into<ErrReport>`
    | doesn't satisfy `ScienceError: eyre::kind::TraitKind`
    | doesn't satisfy `ScienceError: std::fmt::Display`
...
23  |     bail!(ScienceError::RecursionLimitExceeded);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `&ScienceError` due to unsatisfied trait bounds
    |
    = note: the following trait bounds were not satisfied:
            `ScienceError: Into<ErrReport>`
            which is required by `ScienceError: eyre::kind::TraitKind`
            `ScienceError: std::fmt::Display`
            which is required by `&ScienceError: eyre::kind::AdhocKind`
            `&ScienceError: Into<ErrReport>`
            which is required by `&ScienceError: eyre::kind::TraitKind`
note: the traits `Into` and `std::fmt::Display` must be implemented
   --> /home/deca/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:786:1
    |
786 | pub trait Display {
    | ^^^^^^^^^^^^^^^^^
    |
   ::: /home/deca/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:447:1
    |
447 | pub trait Into<T>: Sized {
    | ^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `$crate::eyre` which comes from the expansion of the macro `bail` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
Couldn't compile the test.

PyO3 integration - offer to rehome

Hi,

Thanks for providing integration with PyO3!

Over in the PyO3 repo we're currently discussing accepting an anyhow optional feature. PyO3/pyo3#1822

I am aware that you've got the pyo3 optional feature; I was wondering if for symmetry you wanted us to take ownership of the eyre / pyo3 integration by adding an eyre optional feature to pyo3?

Presumably we can just copy the trait implementations you already have from this repo over to the PyO3 codebase.

Something like `ContextCompat` but for options only

I like the idea of differentiating between options from errors WrapErr intent to bring. But I still like simplicity of Option.context(..)?. So I'd prefer to use .wrap_err on errors and .context on options. Actually after reading the docs I thought ContextCompat works exactly this way, implementing .context for options only. But it happens to bring full compatibility with anyhow.Context, so the compiler won't guide me through changing .context to .wrap_err for errors. Would you consider adding something like OptionContext which would add .context for options only?

To summarize:

  • WrapErr brings .wrap_err for errors only, but no .context
  • OptionContext brings .context for options only

What do you think?

Wrapping Mutex's Error

The error returned by Mutex::lock can't be wrapped using WrapErr::wrap_err. Here's a MVE:

use eyre::{eyre, Result, WrapErr};
use std::sync::Mutex;

fn main() -> Result<()> {
    let x = Mutex::new(0);
    x.lock().wrap_err("lock")?;

    // these also fail
    // x.lock().map_err(|e| eyre!(e)).wrap_err("lock")?;
    // x.lock().map_err(|e| eyre!(Arc::new(Mutex::new(e)))).wrap_err("lock")?;

    Ok(())
}

The raised error of the above code is:

error[E0599]: the method `wrap_err` exists for enum `Result<MutexGuard<'_, {integer}>, PoisonError<MutexGuard<'_, {integer}>>>`, but its trait bounds were not satisfied
   --> src/main.rs:6:14
    |
6   |       x.lock().wrap_err("lock")?;
    |                ^^^^^^^^
    |
    = note: the following trait bounds were not satisfied:
            `PoisonError<MutexGuard<'_, {integer}>>: eyre::context::ext::StdError`
            which is required by `Result<MutexGuard<'_, {integer}>, PoisonError<MutexGuard<'_, {integer}>>>: WrapErr<MutexGuard<'_, {integer}>, PoisonError<MutexGuard<'_, {integer}>>>`
            `PoisonError<MutexGuard<'_, {integer}>>: Send`
            which is required by `Result<MutexGuard<'_, {integer}>, PoisonError<MutexGuard<'_, {integer}>>>: WrapErr<MutexGuard<'_, {integer}>, PoisonError<MutexGuard<'_, {integer}>>>`

warning: unused import: `WrapErr`
 --> src/main.rs:1:26
  |
1 | use eyre::{eyre, Result, WrapErr};
  |                          ^^^^^^^

Is there any recommended way to get around this?

eyre Result is not cmp

I tried to assert_eq! two Results but alas that doesn't seem possible? Could Result support compare if the underlying types do?

(Love color-eyre!)

eyre!(Into::<Box<dyn StdError + Send + Sync>>::into(report)) discards original spantrace, etc

It looks like the only way to convert from Box<dyn StdError + Send + Sync + 'static> into eyre::Report is via the eyre!() macro. It seems odd that there's no other way, but that aside, this means of conversion does not retain the spantrace, backtrace, or any custom color-eyre sections. It does preserve the Display impl and the source chain. I haven't tested but I assume it preserves downcast casting as well.

My guess here is that report.into() throws away that data and produces the underlying boxed error (and that Report::wrap_err() wraps the underlying error). But this is rather unfortunate.

For context, I'm trying to write library functions that take error-returning closures, need to erase the error types, and want to allow callers to use eyre::Report on both ends of the function. I was hoping that declaring the function as using Into<Box<StdError + Send + Sync + 'static>> would work for this, but this issue means callers will lose all of the custom eyre::Report info.

Here's a toy sample library function:

fn library_function<E, E2>(
    mut closure1: impl FnMut() -> Result<(), E>,
    mut closure2: impl FnMut() -> Result<(), E2>,
) -> Result<(), Box<dyn StdError + Send + Sync + 'static>>
where
    E: Into<Box<dyn StdError + Send + Sync + 'static>>,
    E2: Into<Box<dyn StdError + Send + Sync + 'static>>,
{
    closure1().map_err(Into::into)?;
    closure2().map_err(Into::into)?;
    Ok(())
}

We need to homogenize the error types, hence Box<dyn StdError + Send + Sync + 'static>. If someone calls this like eyre!(library_function(fn_that _returns report, fn_that_returns_report)) then the resulting eyre::Report declares its location to be the call to library_function() and discards color-eyre sections.

Also worth noting, the library in question doesn't know anything about eyre, it just wants to play nicely with clients that use it.

Extend `Option` with `ok_or_eyre`?

When discussing https://docs.rs/eyre/latest/eyre/index.html#context-and-option, a user remarked that a closure and macro invocation just for a static string error (as documented: opt.ok_or_else(|| eyre!("new error message"))) seemed cumbersome.

The reasoning at the documentation's "Context and Option" section does make sense - WrapErr would indicate wrapping an error, where there is (literally) None. This issue is not asking to implement WrapErr for Option.

An extension trait, offering ok_or_eyre on the Option type does seem useful to avoid closure + macro boilerplate:

use std::fmt::{Debug, Display};

pub trait OptionExt<T> {
    /// Example usage:
    ///
    /// ```rust
    /// let option: Option<()> = None;
    ///
    /// let result = option.ok_or_eyre("static str error");
    ///
    /// assert_eq!(result.unwrap_err().to_string(), "static str error");
    /// ```
    fn ok_or_eyre<M>(self, message: M) -> crate::Result<T>
    where
        M: Debug + Display + Send + Sync + 'static;
}

impl<T> OptionExt<T> for Option<T> {
    fn ok_or_eyre<M>(self, message: M) -> crate::Result<T>
    where
        M: Debug + Display + Send + Sync + 'static,
    {
        match self {
            Some(ok) => Ok(ok),
            None => Err(crate::Report::msg(message)),
        }
    }
}

Is this something you would consider accepting as contribution? Names are of course up for bikeshedding.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.