From 86da223cde6ae4f899298f6130604e192e7f1212 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 27 Feb 2020 13:04:19 -0800 Subject: [PATCH 01/10] Rename core types --- Cargo.toml | 8 +-- README.md | 28 +++++------ build.rs | 4 +- src/context.rs | 28 +++++------ src/error.rs | 108 ++++++++++++++++++++-------------------- src/kind.rs | 46 ++++++++--------- src/lib.rs | 98 ++++++++++++++++++------------------ src/macros.rs | 28 +++++------ tests/common/mod.rs | 2 +- tests/test_autotrait.rs | 6 +-- tests/test_backtrace.rs | 4 +- tests/test_boxed.rs | 12 ++--- tests/test_chain.rs | 6 +-- tests/test_context.rs | 8 +-- tests/test_convert.rs | 4 +- tests/test_downcast.rs | 6 +-- tests/test_fmt.rs | 2 +- tests/test_macros.rs | 2 +- tests/test_repr.rs | 10 ++-- tests/test_source.rs | 18 +++---- tests/ui/no-impl.rs | 4 +- tests/ui/no-impl.stderr | 22 ++++---- 22 files changed, 227 insertions(+), 227 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f0c1f8f..7c0099e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,17 @@ [package] -name = "anyhow" +name = "eyre" version = "1.0.26" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" description = "Flexible concrete Error type built on std::error::Error" -repository = "https://github.com/dtolnay/anyhow" -documentation = "https://docs.rs/anyhow" +repository = "https://github.com/dtolnay/eyre" +documentation = "https://docs.rs/eyre" readme = "README.md" categories = ["rust-patterns"] [badges] -travis-ci = { repository = "dtolnay/anyhow" } +travis-ci = { repository = "dtolnay/eyre" } [features] default = ["std"] diff --git a/README.md b/README.md index 16c0b42..4f9bd59 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ Anyhow ¯\\\_(ツ)\_/¯ ========================= -[![Build Status](https://api.travis-ci.com/dtolnay/anyhow.svg?branch=master)](https://travis-ci.com/dtolnay/anyhow) -[![Latest Version](https://img.shields.io/crates/v/anyhow.svg)](https://crates.io/crates/anyhow) -[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/anyhow) +[![Build Status](https://api.travis-ci.com/dtolnay/eyre.svg?branch=master)](https://travis-ci.com/dtolnay/eyre) +[![Latest Version](https://img.shields.io/crates/v/eyre.svg)](https://crates.io/crates/eyre) +[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/eyre) -This library provides [`anyhow::Error`][Error], a trait object based error type +This library provides [`eyre::Error`][Error], a trait object based error type for easy idiomatic error handling in Rust applications. -[Error]: https://docs.rs/anyhow/1.0/anyhow/struct.Error.html +[Error]: https://docs.rs/eyre/1.0/eyre/struct.Error.html ```toml [dependencies] -anyhow = "1.0" +eyre = "1.0" ``` *Compiler support: requires rustc 1.34+* @@ -21,14 +21,14 @@ anyhow = "1.0" ## Details -- Use `Result`, or equivalently `anyhow::Result`, as the +- Use `Result`, or equivalently `eyre::Result`, as the return type of any fallible function. Within the function, use `?` to easily propagate any error that implements the `std::error::Error` trait. ```rust - use anyhow::Result; + use eyre::Result; fn get_cluster_info() -> Result { let config = std::fs::read_to_string("cluster.json")?; @@ -43,7 +43,7 @@ anyhow = "1.0" application was in the middle of. ```rust - use anyhow::{Context, Result}; + use eyre::{Context, Result}; fn main() -> Result<()> { ... @@ -98,11 +98,11 @@ anyhow = "1.0" } ``` -- One-off error messages can be constructed using the `anyhow!` macro, which - supports string interpolation and produces an `anyhow::Error`. +- One-off error messages can be constructed using the `eyre!` macro, which + supports string interpolation and produces an `eyre::Error`. ```rust - return Err(anyhow!("Missing attribute: {}", missing)); + return Err(eyre!("Missing attribute: {}", missing)); ```
@@ -115,7 +115,7 @@ Cargo.toml. A global allocator is required. ```toml [dependencies] -anyhow = { version = "1.0", default-features = false } +eyre = { version = "1.0", default-features = false } ``` Since the `?`-based error conversions would normally rely on the @@ -127,7 +127,7 @@ type inside a function that returns Anyhow's error type. ## Comparison to failure -The `anyhow::Error` type works something like `failure::Error`, but unlike +The `eyre::Error` 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]. diff --git a/build.rs b/build.rs index 056164d..f704a7b 100644 --- a/build.rs +++ b/build.rs @@ -5,7 +5,7 @@ use std::process::{Command, ExitStatus}; // This code exercises the surface area that we expect of the std Backtrace // type. If the current toolchain is able to compile it, we go ahead and use -// backtrace in anyhow. +// backtrace in eyre. const PROBE: &str = r#" #![feature(backtrace)] #![allow(dead_code)] @@ -51,7 +51,7 @@ fn compile_probe() -> Option { fs::write(&probefile, PROBE).ok()?; Command::new(rustc) .arg("--edition=2018") - .arg("--crate-name=anyhow_build") + .arg("--crate-name=eyre_build") .arg("--crate-type=lib") .arg("--emit=metadata") .arg("--out-dir") diff --git a/src/context.rs b/src/context.rs index 25d3411..0e4345e 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,5 +1,5 @@ use crate::error::ContextError; -use crate::{Context, Error, StdError}; +use crate::{Context, ErrReport, StdError}; use core::convert::Infallible; use core::fmt::{self, Debug, Display, Write}; @@ -10,7 +10,7 @@ mod ext { use super::*; pub trait StdError { - fn ext_context(self, context: C) -> Error + fn ext_context(self, context: C) -> ErrReport where C: Display + Send + Sync + 'static; } @@ -20,17 +20,17 @@ mod ext { where E: std::error::Error + Send + Sync + 'static, { - fn ext_context(self, context: C) -> Error + fn ext_context(self, context: C) -> ErrReport where C: Display + Send + Sync + 'static, { let backtrace = backtrace_if_absent!(self); - Error::from_context(context, self, backtrace) + ErrReport::from_context(context, self, backtrace) } } - impl StdError for Error { - fn ext_context(self, context: C) -> Error + impl StdError for ErrReport { + fn ext_context(self, context: C) -> ErrReport where C: Display + Send + Sync + 'static, { @@ -43,14 +43,14 @@ impl Context for Result where E: ext::StdError + Send + Sync + 'static, { - fn context(self, context: C) -> Result + fn context(self, context: C) -> Result where C: Display + Send + Sync + 'static, { self.map_err(|error| error.ext_context(context)) } - fn with_context(self, context: F) -> Result + fn with_context(self, context: F) -> Result where C: Display + Send + Sync + 'static, F: FnOnce() -> C, @@ -62,7 +62,7 @@ where /// ``` /// # type T = (); /// # -/// use anyhow::{Context, Result}; +/// use eyre::{Context, Result}; /// /// fn maybe_get() -> Option { /// # const IGNORE: &str = stringify! { @@ -80,19 +80,19 @@ where /// } /// ``` impl Context for Option { - fn context(self, context: C) -> Result + fn context(self, context: C) -> Result where C: Display + Send + Sync + 'static, { - self.ok_or_else(|| Error::from_display(context, backtrace!())) + self.ok_or_else(|| ErrReport::from_display(context, backtrace!())) } - fn with_context(self, context: F) -> Result + fn with_context(self, context: F) -> Result where C: Display + Send + Sync + 'static, F: FnOnce() -> C, { - self.ok_or_else(|| Error::from_display(context(), backtrace!())) + self.ok_or_else(|| ErrReport::from_display(context(), backtrace!())) } } @@ -133,7 +133,7 @@ where } } -impl StdError for ContextError +impl StdError for ContextError where C: Display, { diff --git a/src/error.rs b/src/error.rs index 88526df..23c374e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,7 @@ use crate::alloc::Box; use crate::backtrace::Backtrace; use crate::chain::Chain; -use crate::{Error, StdError}; +use crate::{ErrReport, StdError}; use core::any::TypeId; use core::fmt::{self, Debug, Display}; use core::mem::{self, ManuallyDrop}; @@ -10,10 +10,10 @@ use core::ptr::{self, NonNull}; #[cfg(feature = "std")] use core::ops::{Deref, DerefMut}; -impl Error { +impl ErrReport { /// Create a new error object from any error type. /// - /// The error type must be threadsafe and `'static`, so that the `Error` + /// The error type must be threadsafe and `'static`, so that the `ErrReport` /// will be as well. /// /// If the error type does not provide a backtrace, a backtrace will be @@ -24,18 +24,18 @@ impl Error { E: StdError + Send + Sync + 'static, { let backtrace = backtrace_if_absent!(error); - Error::from_std(error, backtrace) + ErrReport::from_std(error, backtrace) } /// Create a new error object from a printable error message. /// - /// If the argument implements std::error::Error, prefer `Error::new` + /// If the argument implements std::error::Error, prefer `ErrReport::new` /// instead which preserves the underlying error's cause chain and /// backtrace. If the argument may or may not implement std::error::Error - /// now or in the future, use `anyhow!(err)` which handles either way + /// now or in the future, use `eyre!(err)` which handles either way /// correctly. /// - /// `Error::msg("...")` is equivalent to `anyhow!("...")` but occasionally + /// `ErrReport::msg("...")` is equivalent to `eyre!("...")` but occasionally /// convenient in places where a function is preferable over a macro, such /// as iterator or stream combinators: /// @@ -50,7 +50,7 @@ impl Error { /// # /// # use ffi::{Input, Output}; /// # - /// use anyhow::{Error, Result}; + /// use eyre::{ErrReport, Result}; /// use futures::stream::{Stream, StreamExt, TryStreamExt}; /// /// async fn demo(stream: S) -> Result> @@ -59,7 +59,7 @@ impl Error { /// { /// stream /// .then(ffi::do_some_work) // returns Result - /// .map_err(Error::msg) + /// .map_err(ErrReport::msg) /// .try_collect() /// .await /// } @@ -68,7 +68,7 @@ impl Error { where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + ErrReport::from_adhoc(message, backtrace!()) } #[cfg(feature = "std")] @@ -87,7 +87,7 @@ impl Error { }; // Safety: passing vtable that operates on the right type E. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable, backtrace) } } pub(crate) fn from_adhoc(message: M, backtrace: Option) -> Self @@ -108,7 +108,7 @@ impl Error { // Safety: MessageError is repr(transparent) so it is okay for the // vtable to allow casting the MessageError to M. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable, backtrace) } } pub(crate) fn from_display(message: M, backtrace: Option) -> Self @@ -129,7 +129,7 @@ impl Error { // Safety: DisplayError is repr(transparent) so it is okay for the // vtable to allow casting the DisplayError to M. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable, backtrace) } } #[cfg(feature = "std")] @@ -151,7 +151,7 @@ impl Error { }; // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable, backtrace) } } #[cfg(feature = "std")] @@ -173,7 +173,7 @@ impl Error { // Safety: BoxedError is repr(transparent) so it is okay for the vtable // to allow casting to Box. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable, backtrace) } } // Takes backtrace as argument rather than capturing it here so that the @@ -202,7 +202,7 @@ impl Error { // caller rather than a builtin fat pointer vtable. let erased = mem::transmute::>, Box>>(inner); let inner = ManuallyDrop::new(erased); - Error { inner } + ErrReport { inner } } /// Wrap the error value with additional context. @@ -232,7 +232,7 @@ impl Error { /// # } /// # } /// # - /// use anyhow::Result; + /// use eyre::Result; /// use std::fs::File; /// use std::path::Path; /// @@ -255,7 +255,7 @@ impl Error { /// "only the first {} lines of {} are valid", /// error.line, path.as_ref().display(), /// ); - /// anyhow::Error::new(error).context(context) + /// eyre::ErrReport::new(error).context(context) /// }) /// } /// ``` @@ -263,29 +263,29 @@ impl Error { where C: Display + Send + Sync + 'static, { - let error: ContextError = ContextError { + let error: ContextError = ContextError { context, error: self, }; let vtable = &ErrorVTable { - object_drop: object_drop::>, - object_ref: object_ref::>, + object_drop: object_drop::>, + object_ref: object_ref::>, #[cfg(feature = "std")] - object_mut: object_mut::>, - object_boxed: object_boxed::>, + object_mut: object_mut::>, + object_boxed: object_boxed::>, object_downcast: context_chain_downcast::, object_drop_rest: context_chain_drop_rest::, }; - // As the cause is anyhow::Error, we already have a backtrace for it. + // As the cause is eyre::ErrReport, we already have a backtrace for it. let backtrace = None; // Safety: passing vtable that operates on the right type. - unsafe { Error::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable, backtrace) } } - /// Get the backtrace for this Error. + /// Get the backtrace for this ErrReport. /// /// Backtraces are only available on the nightly channel. Tracking issue: /// [rust-lang/rust#53487][tracking]. @@ -301,7 +301,7 @@ impl Error { self.inner.backtrace() } - /// An iterator of the chain of source errors contained by this Error. + /// An iterator of the chain of source errors contained by this ErrReport. /// /// This iterator will visit every error in the cause chain of this error /// object, beginning with the error that this error object was created @@ -310,10 +310,10 @@ impl Error { /// # Example /// /// ``` - /// use anyhow::Error; + /// use eyre::ErrReport; /// use std::io; /// - /// pub fn underlying_io_error_kind(error: &Error) -> Option { + /// pub fn underlying_io_error_kind(error: &ErrReport) -> Option { /// for cause in error.chain() { /// if let Some(io_error) = cause.downcast_ref::() { /// return Some(io_error.kind()); @@ -331,7 +331,7 @@ impl Error { /// cause's cause etc. /// /// The root cause is the last error in the iterator produced by - /// [`chain()`][Error::chain]. + /// [`chain()`][ErrReport::chain]. #[cfg(feature = "std")] pub fn root_cause(&self) -> &(dyn StdError + 'static) { let mut chain = self.chain(); @@ -379,7 +379,7 @@ impl Error { let error = ptr::read(addr.cast::().as_ptr()); // Read Box> from self. Can't move it out because - // Error has a Drop impl which we want to not run. + // ErrReport has a Drop impl which we want to not run. let inner = ptr::read(&outer.inner); let erased = ManuallyDrop::into_inner(inner); @@ -395,7 +395,7 @@ impl Error { /// # Example /// /// ``` - /// # use anyhow::anyhow; + /// # use eyre::eyre; /// # use std::fmt::{self, Display}; /// # use std::task::Poll; /// # @@ -414,7 +414,7 @@ impl Error { /// # /// # const REDACTED_CONTENT: () = (); /// # - /// # let error = anyhow!("..."); + /// # let error = eyre!("..."); /// # let root_cause = &error; /// # /// # let ret = @@ -455,18 +455,18 @@ impl Error { } #[cfg(feature = "std")] -impl From for Error +impl From for ErrReport where E: StdError + Send + Sync + 'static, { fn from(error: E) -> Self { let backtrace = backtrace_if_absent!(error); - Error::from_std(error, backtrace) + ErrReport::from_std(error, backtrace) } } #[cfg(feature = "std")] -impl Deref for Error { +impl Deref for ErrReport { type Target = dyn StdError + Send + Sync + 'static; fn deref(&self) -> &Self::Target { @@ -475,25 +475,25 @@ impl Deref for Error { } #[cfg(feature = "std")] -impl DerefMut for Error { +impl DerefMut for ErrReport { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.error_mut() } } -impl Display for Error { +impl Display for ErrReport { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.inner.display(formatter) } } -impl Debug for Error { +impl Debug for ErrReport { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.inner.debug(formatter) } } -impl Drop for Error { +impl Drop for ErrReport { fn drop(&mut self) { unsafe { // Read Box> from self. @@ -622,24 +622,24 @@ where } } -// Safety: requires layout of *e to match ErrorImpl>. +// Safety: requires layout of *e to match ErrorImpl>. unsafe fn context_chain_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> where C: 'static, { if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; let addr = &(*unerased)._object.context as *const C as *mut (); Some(NonNull::new_unchecked(addr)) } else { // Recurse down the context chain per the inner error's vtable. - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; let source = &(*unerased)._object.error; (source.inner.vtable.object_downcast)(&source.inner, target) } } -// Safety: requires layout of *e to match ErrorImpl>. +// Safety: requires layout of *e to match ErrorImpl>. unsafe fn context_chain_drop_rest(e: Box>, target: TypeId) where C: 'static, @@ -649,14 +649,14 @@ where if TypeId::of::() == target { let unerased = mem::transmute::< Box>, - Box, Error>>>, + Box, ErrReport>>>, >(e); - // Drop the entire rest of the data structure rooted in the next Error. + // Drop the entire rest of the data structure rooted in the next ErrReport. drop(unerased); } else { let unerased = mem::transmute::< Box>, - Box>>>, + Box>>>, >(e); // Read out a ManuallyDrop>> from the next error. let inner = ptr::read(&unerased._object.error.inner); @@ -756,12 +756,12 @@ where } } -impl From for Box { - fn from(error: Error) -> Self { +impl From for Box { + fn from(error: ErrReport) -> Self { let outer = ManuallyDrop::new(error); unsafe { // Read Box> from error. Can't move it out because - // Error has a Drop impl which we want to not run. + // ErrReport has a Drop impl which we want to not run. let inner = ptr::read(&outer.inner); let erased = ManuallyDrop::into_inner(inner); @@ -772,21 +772,21 @@ impl From for Box { } } -impl From for Box { - fn from(error: Error) -> Self { +impl From for Box { + fn from(error: ErrReport) -> Self { Box::::from(error) } } #[cfg(feature = "std")] -impl AsRef for Error { +impl AsRef for ErrReport { fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) { &**self } } #[cfg(feature = "std")] -impl AsRef for Error { +impl AsRef for ErrReport { fn as_ref(&self) -> &(dyn StdError + 'static) { &**self } diff --git a/src/kind.rs b/src/kind.rs index fdeb060..8235c25 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -1,50 +1,50 @@ -// Tagged dispatch mechanism for resolving the behavior of `anyhow!($expr)`. +// Tagged dispatch mechanism for resolving the behavior of `eyre!($expr)`. // -// When anyhow! is given a single expr argument to turn into anyhow::Error, we -// want the resulting Error to pick up the input's implementation of source() +// When eyre! is given a single expr argument to turn into eyre::ErrReport, we +// want the resulting ErrReport to pick up the input's implementation of source() // and backtrace() if it has a std::error::Error impl, otherwise require nothing // more than Display and Debug. // // Expressed in terms of specialization, we want something like: // -// trait AnyhowNew { -// fn new(self) -> Error; +// trait EyreNew { +// fn new(self) -> ErrReport; // } // -// impl AnyhowNew for T +// impl EyreNew for T // where // T: Display + Debug + Send + Sync + 'static, // { -// default fn new(self) -> Error { +// default fn new(self) -> ErrReport { // /* no std error impl */ // } // } // -// impl AnyhowNew for T +// impl EyreNew for T // where // T: std::error::Error + Send + Sync + 'static, // { -// fn new(self) -> Error { +// fn new(self) -> ErrReport { // /* use std error's source() and backtrace() */ // } // } // // Since specialization is not stable yet, instead we rely on autoref behavior // of method resolution to perform tagged dispatch. Here we have two traits -// AdhocKind and TraitKind that both have an anyhow_kind() method. AdhocKind is +// AdhocKind and TraitKind that both have an eyre_kind() method. AdhocKind is // implemented whether or not the caller's type has a std error impl, while // TraitKind is implemented only when a std error impl does exist. The ambiguity // is resolved by AdhocKind requiring an extra autoref so that it has lower // precedence. // -// The anyhow! macro will set up the call in this form: +// The eyre! macro will set up the call in this form: // // #[allow(unused_imports)] // use $crate::private::{AdhocKind, TraitKind}; // let error = $msg; -// (&error).anyhow_kind().new(error) +// (&error).eyre_kind().new(error) -use crate::Error; +use crate::ErrReport; use core::fmt::{Debug, Display}; #[cfg(feature = "std")] @@ -57,7 +57,7 @@ pub struct Adhoc; pub trait AdhocKind: Sized { #[inline] - fn anyhow_kind(&self) -> Adhoc { + fn eyre_kind(&self) -> Adhoc { Adhoc } } @@ -65,11 +65,11 @@ pub trait AdhocKind: Sized { impl AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} impl Adhoc { - pub fn new(self, message: M) -> Error + pub fn new(self, message: M) -> ErrReport where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + ErrReport::from_adhoc(message, backtrace!()) } } @@ -77,17 +77,17 @@ pub struct Trait; pub trait TraitKind: Sized { #[inline] - fn anyhow_kind(&self) -> Trait { + fn eyre_kind(&self) -> Trait { Trait } } -impl TraitKind for E where E: Into {} +impl TraitKind for E where E: Into {} impl Trait { - pub fn new(self, error: E) -> Error + pub fn new(self, error: E) -> ErrReport where - E: Into, + E: Into, { error.into() } @@ -99,7 +99,7 @@ pub struct Boxed; #[cfg(feature = "std")] pub trait BoxedKind: Sized { #[inline] - fn anyhow_kind(&self) -> Boxed { + fn eyre_kind(&self) -> Boxed { Boxed } } @@ -109,8 +109,8 @@ impl BoxedKind for Box {} #[cfg(feature = "std")] impl Boxed { - pub fn new(self, error: Box) -> Error { + pub fn new(self, error: Box) -> ErrReport { let backtrace = backtrace_if_absent!(error); - Error::from_boxed(error, backtrace) + ErrReport::from_boxed(error, backtrace) } } diff --git a/src/lib.rs b/src/lib.rs index 39bd6a0..8621113 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,15 @@ -//! This library provides [`anyhow::Error`][Error], a trait object based error +//! This library provides [`eyre::ErrReport`][ErrReport], a trait object based error //! type for easy idiomatic error handling in Rust applications. //! //!
//! //! # Details //! -//! - Use `Result`, or equivalently `anyhow::Result`, as +//! - Use `Result`, or equivalently `eyre::Result`, as //! the return type of any fallible function. //! //! Within the function, use `?` to easily propagate any error that implements -//! the `std::error::Error` trait. +//! the `std::error::ErrReport` trait. //! //! ``` //! # pub trait Deserialize {} @@ -27,7 +27,7 @@ //! # //! # impl Deserialize for ClusterMap {} //! # -//! use anyhow::Result; +//! use eyre::Result; //! //! fn get_cluster_info() -> Result { //! let config = std::fs::read_to_string("cluster.json")?; @@ -52,7 +52,7 @@ //! # } //! # } //! # -//! use anyhow::{Context, Result}; +//! use eyre::{Context, Result}; //! //! fn main() -> Result<()> { //! # return Ok(()); @@ -88,7 +88,7 @@ //! mutable reference as needed. //! //! ``` -//! # use anyhow::anyhow; +//! # use eyre::eyre; //! # use std::fmt::{self, Display}; //! # use std::task::Poll; //! # @@ -107,7 +107,7 @@ //! # //! # const REDACTED_CONTENT: () = (); //! # -//! # let error = anyhow!("..."); +//! # let error = eyre!("..."); //! # let root_cause = &error; //! # //! # let ret = @@ -146,15 +146,15 @@ //! } //! ``` //! -//! - One-off error messages can be constructed using the `anyhow!` macro, which -//! supports string interpolation and produces an `anyhow::Error`. +//! - One-off error messages can be constructed using the `eyre!` macro, which +//! supports string interpolation and produces an `eyre::ErrReport`. //! //! ``` -//! # use anyhow::{anyhow, Result}; +//! # use eyre::{eyre, Result}; //! # //! # fn demo() -> Result<()> { //! # let missing = "..."; -//! return Err(anyhow!("Missing attribute: {}", missing)); +//! return Err(eyre!("Missing attribute: {}", missing)); //! # Ok(()) //! # } //! ``` @@ -169,15 +169,15 @@ //! //! ```toml //! [dependencies] -//! anyhow = { version = "1.0", default-features = false } +//! eyre = { version = "1.0", default-features = false } //! ``` //! //! Since the `?`-based error conversions would normally rely on the -//! `std::error::Error` trait which is only available through std, no_std mode -//! will require an explicit `.map_err(Error::msg)` when working with a +//! `std::error::ErrReport` trait which is only available through std, no_std mode +//! will require an explicit `.map_err(ErrReport::msg)` when working with a //! non-Anyhow error type inside a function that returns Anyhow's error type. -#![doc(html_root_url = "https://docs.rs/anyhow/1.0.26")] +#![doc(html_root_url = "https://docs.rs/eyre/1.0.26")] #![cfg_attr(backtrace, feature(backtrace))] #![cfg_attr(not(feature = "std"), no_std)] #![allow( @@ -225,17 +225,17 @@ trait StdError: Debug + Display { } } -pub use anyhow as format_err; +pub use eyre as format_err; -/// The `Error` type, a wrapper around a dynamic error type. +/// The `ErrReport` type, a wrapper around a dynamic error type. /// -/// `Error` works a lot like `Box`, but with these +/// `ErrReport` works a lot like `Box`, but with these /// differences: /// -/// - `Error` requires that the error is `Send`, `Sync`, and `'static`. -/// - `Error` guarantees that a backtrace is available, even if the underlying +/// - `ErrReport` requires that the error is `Send`, `Sync`, and `'static`. +/// - `ErrReport` guarantees that a backtrace is available, even if the underlying /// error type does not provide one. -/// - `Error` is represented as a narrow pointer — exactly one word in +/// - `ErrReport` is represented as a narrow pointer — exactly one word in /// size instead of two. /// ///
@@ -245,13 +245,13 @@ pub use anyhow as format_err; /// When you print an error object using "{}" or to_string(), only the outermost /// underlying error or context is printed, not any of the lower level causes. /// This is exactly as if you had called the Display impl of the error from -/// which you constructed your anyhow::Error. +/// which you constructed your eyre::ErrReport. /// /// ```console /// Failed to read instrs from ./path/to/instrs.json /// ``` /// -/// To print causes as well using anyhow's default formatting of causes, use the +/// To print causes as well using eyre's default formatting of causes, use the /// alternate selector "{:#}". /// /// ```console @@ -269,12 +269,12 @@ pub use anyhow as format_err; /// No such file or directory (os error 2) /// /// Stack backtrace: -/// 0: ::ext_context -/// at /git/anyhow/src/backtrace.rs:26 +/// 0: ::ext_context +/// at /git/eyre/src/backtrace.rs:26 /// 1: core::result::Result::map_err /// at /git/rustc/src/libcore/result.rs:596 -/// 2: anyhow::context:: for core::result::Result>::with_context -/// at /git/anyhow/src/context.rs:58 +/// 2: eyre::context:: for core::result::Result>::with_context +/// at /git/eyre/src/context.rs:58 /// 3: testing::main /// at src/main.rs:5 /// 4: std::rt::lang_start @@ -302,7 +302,7 @@ pub use anyhow as format_err; /// like this: /// /// ``` -/// use anyhow::{Context, Result}; +/// use eyre::{Context, Result}; /// /// fn main() { /// if let Err(err) = try_main() { @@ -319,21 +319,21 @@ pub use anyhow as format_err; /// # Ok(()) /// } /// ``` -pub struct Error { +pub struct ErrReport { inner: ManuallyDrop>>, } /// Iterator of a chain of source errors. /// -/// This type is the iterator returned by [`Error::chain`]. +/// This type is the iterator returned by [`ErrReport::chain`]. /// /// # Example /// /// ``` -/// use anyhow::Error; +/// use eyre::ErrReport; /// use std::io; /// -/// pub fn underlying_io_error_kind(error: &Error) -> Option { +/// pub fn underlying_io_error_kind(error: &ErrReport) -> Option { /// for cause in error.chain() { /// if let Some(io_error) = cause.downcast_ref::() { /// return Some(io_error.kind()); @@ -354,14 +354,14 @@ pub struct Chain<'a> { /// for `fn main`; if you do, failures will be printed along with any /// [context][Context] and a backtrace if one was captured. /// -/// `anyhow::Result` may be used with one *or* two type parameters. +/// `eyre::Result` may be used with one *or* two type parameters. /// /// ```rust -/// use anyhow::Result; +/// use eyre::Result; /// /// # const IGNORE: &str = stringify! { /// fn demo1() -> Result {...} -/// // ^ equivalent to std::result::Result +/// // ^ equivalent to std::result::Result /// /// fn demo2() -> Result {...} /// // ^ equivalent to std::result::Result @@ -387,7 +387,7 @@ pub struct Chain<'a> { /// # /// # impl Deserialize for ClusterMap {} /// # -/// use anyhow::Result; +/// use eyre::Result; /// /// fn main() -> Result<()> { /// # return Ok(()); @@ -397,19 +397,19 @@ pub struct Chain<'a> { /// Ok(()) /// } /// ``` -pub type Result = core::result::Result; +pub type Result = core::result::Result; /// Provides the `context` method for `Result`. /// /// This trait is sealed and cannot be implemented for types outside of -/// `anyhow`. +/// `eyre`. /// ///
/// /// # Example /// /// ``` -/// use anyhow::{Context, Result}; +/// use eyre::{Context, Result}; /// use std::fs; /// use std::path::PathBuf; /// @@ -452,7 +452,7 @@ pub type Result = core::result::Result; /// # Effect on downcasting /// /// After attaching context of type `C` onto an error of type `E`, the resulting -/// `anyhow::Error` may be downcast to `C` **or** to `E`. +/// `eyre::Error` may be downcast to `C` **or** to `E`. /// /// That is, in codebases that rely on downcasting, Anyhow's context supports /// both of the following use cases: @@ -468,7 +468,7 @@ pub type Result = core::result::Result; /// be helpful. /// /// ``` -/// # use anyhow::bail; +/// # use eyre::bail; /// # use thiserror::Error; /// # /// # #[derive(Error, Debug)] @@ -479,7 +479,7 @@ pub type Result = core::result::Result; /// # bail!(SuspiciousError); /// # } /// # -/// use anyhow::{Context, Result}; +/// use eyre::{Context, Result}; /// /// fn do_it() -> Result<()> { /// helper().context("Failed to complete the work")?; @@ -508,7 +508,7 @@ pub type Result = core::result::Result; /// the application. /// /// ``` -/// # use anyhow::bail; +/// # use eyre::bail; /// # use thiserror::Error; /// # /// # #[derive(Error, Debug)] @@ -519,7 +519,7 @@ pub type Result = core::result::Result; /// # bail!("no such file or directory"); /// # } /// # -/// use anyhow::{Context, Result}; +/// use eyre::{Context, Result}; /// /// fn do_it() -> Result<()> { /// helper().context(HelperFailed)?; @@ -542,13 +542,13 @@ pub type Result = core::result::Result; /// ``` pub trait Context: context::private::Sealed { /// Wrap the error value with additional context. - fn context(self, context: C) -> Result + fn context(self, context: C) -> Result where C: Display + Send + Sync + 'static; /// Wrap the error value with additional context that is evaluated lazily /// only once an error does occur. - fn with_context(self, f: F) -> Result + fn with_context(self, f: F) -> Result where C: Display + Send + Sync + 'static, F: FnOnce() -> C; @@ -557,7 +557,7 @@ pub trait Context: context::private::Sealed { // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { - use crate::Error; + use crate::ErrReport; use core::fmt::{Debug, Display}; #[cfg(backtrace)] @@ -573,10 +573,10 @@ pub mod private { pub use crate::kind::BoxedKind; } - pub fn new_adhoc(message: M) -> Error + pub fn new_adhoc(message: M) -> ErrReport where M: Display + Debug + Send + Sync + 'static, { - Error::from_adhoc(message, backtrace!()) + ErrReport::from_adhoc(message, backtrace!()) } } diff --git a/src/macros.rs b/src/macros.rs index 15a9208..8418d55 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,7 +5,7 @@ /// # Example /// /// ``` -/// # use anyhow::{bail, Result}; +/// # use eyre::{bail, Result}; /// # /// # fn has_permission(user: usize, resource: usize) -> bool { /// # true @@ -23,7 +23,7 @@ /// ``` /// /// ``` -/// # use anyhow::{bail, Result}; +/// # use eyre::{bail, Result}; /// # use thiserror::Error; /// # /// # const MAX_DEPTH: usize = 1; @@ -50,13 +50,13 @@ #[macro_export] macro_rules! bail { ($msg:literal $(,)?) => { - return $crate::private::Err($crate::anyhow!($msg)); + return $crate::private::Err($crate::eyre!($msg)); }; ($err:expr $(,)?) => { - return $crate::private::Err($crate::anyhow!($err)); + return $crate::private::Err($crate::eyre!($err)); }; ($fmt:expr, $($arg:tt)*) => { - return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)); + return $crate::private::Err($crate::eyre!($fmt, $($arg)*)); }; } @@ -71,7 +71,7 @@ macro_rules! bail { /// # Example /// /// ``` -/// # use anyhow::{ensure, Result}; +/// # use eyre::{ensure, Result}; /// # /// # fn main() -> Result<()> { /// # let user = 0; @@ -82,7 +82,7 @@ macro_rules! bail { /// ``` /// /// ``` -/// # use anyhow::{ensure, Result}; +/// # use eyre::{ensure, Result}; /// # use thiserror::Error; /// # /// # const MAX_DEPTH: usize = 1; @@ -108,17 +108,17 @@ macro_rules! bail { macro_rules! ensure { ($cond:expr, $msg:literal $(,)?) => { if !$cond { - return $crate::private::Err($crate::anyhow!($msg)); + return $crate::private::Err($crate::eyre!($msg)); } }; ($cond:expr, $err:expr $(,)?) => { if !$cond { - return $crate::private::Err($crate::anyhow!($err)); + return $crate::private::Err($crate::eyre!($err)); } }; ($cond:expr, $fmt:expr, $($arg:tt)*) => { if !$cond { - return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)); + return $crate::private::Err($crate::eyre!($fmt, $($arg)*)); } }; } @@ -134,11 +134,11 @@ macro_rules! ensure { /// ``` /// # type V = (); /// # -/// use anyhow::{anyhow, Result}; +/// use eyre::{eyre, Result}; /// /// fn lookup(key: &str) -> Result { /// if key.len() != 16 { -/// return Err(anyhow!("key length must be 16 characters, got {:?}", key)); +/// return Err(eyre!("key length must be 16 characters, got {:?}", key)); /// } /// /// // ... @@ -146,7 +146,7 @@ macro_rules! ensure { /// } /// ``` #[macro_export] -macro_rules! anyhow { +macro_rules! eyre { ($msg:literal $(,)?) => { // Handle $:literal as a special case to make cargo-expanded code more // concise in the common case. @@ -155,7 +155,7 @@ macro_rules! anyhow { ($err:expr $(,)?) => ({ use $crate::private::kind::*; let error = $err; - (&error).anyhow_kind().new(error) + (&error).eyre_kind().new(error) }); ($fmt:expr, $($arg:tt)*) => { $crate::private::new_adhoc(format!($fmt, $($arg)*)) diff --git a/tests/common/mod.rs b/tests/common/mod.rs index fc165a5..81ae0bc 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Result}; +use eyre::{bail, Result}; use std::io; pub fn bail_literal() -> Result<()> { diff --git a/tests/test_autotrait.rs b/tests/test_autotrait.rs index 0c9326d..de78967 100644 --- a/tests/test_autotrait.rs +++ b/tests/test_autotrait.rs @@ -1,13 +1,13 @@ -use anyhow::Error; +use eyre::ErrReport; #[test] fn test_send() { fn assert_send() {} - assert_send::(); + assert_send::(); } #[test] fn test_sync() { fn assert_sync() {} - assert_sync::(); + assert_sync::(); } diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index ce385f5..134567a 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -6,8 +6,8 @@ fn test_backtrace() {} #[rustversion::nightly] #[test] fn test_backtrace() { - use anyhow::anyhow; + use eyre::eyre; - let error = anyhow!("oh no!"); + let error = eyre!("oh no!"); let _ = error.backtrace(); } diff --git a/tests/test_boxed.rs b/tests/test_boxed.rs index 38a568f..d4284bb 100644 --- a/tests/test_boxed.rs +++ b/tests/test_boxed.rs @@ -1,4 +1,4 @@ -use anyhow::anyhow; +use eyre::eyre; use std::error::Error as StdError; use std::io; use thiserror::Error; @@ -12,7 +12,7 @@ struct MyError { #[test] fn test_boxed_str() { let error = Box::::from("oh no!"); - let error = anyhow!(error); + let error = eyre!(error); assert_eq!("oh no!", error.to_string()); assert_eq!( "oh no!", @@ -28,13 +28,13 @@ fn test_boxed_thiserror() { let error = MyError { source: io::Error::new(io::ErrorKind::Other, "oh no!"), }; - let error = anyhow!(error); + let error = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } #[test] -fn test_boxed_anyhow() { - let error = anyhow!("oh no!").context("it failed"); - let error = anyhow!(error); +fn test_boxed_eyre() { + let error = eyre!("oh no!").context("it failed"); + let error = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } diff --git a/tests/test_chain.rs b/tests/test_chain.rs index b1c5a3d..7e07fef 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -1,7 +1,7 @@ -use anyhow::{anyhow, Error}; +use eyre::{eyre, ErrReport}; -fn error() -> Error { - anyhow!(0).context(1).context(2).context(3) +fn error() -> ErrReport { + eyre!(0).context(1).context(2).context(3) } #[test] diff --git a/tests/test_context.rs b/tests/test_context.rs index 44c1c70..de67d8e 100644 --- a/tests/test_context.rs +++ b/tests/test_context.rs @@ -1,11 +1,11 @@ mod drop; use crate::drop::{DetectDrop, Flag}; -use anyhow::{Context, Error, Result}; +use eyre::{Context, ErrReport, Result}; use std::fmt::{self, Display}; use thiserror::Error; -// https://github.com/dtolnay/anyhow/issues/18 +// https://github.com/dtolnay/eyre/issues/18 #[test] fn test_inference() -> Result<()> { let x = "1"; @@ -56,7 +56,7 @@ impl Dropped { } } -fn make_chain() -> (Error, Dropped) { +fn make_chain() -> (ErrReport, Dropped) { let dropped = Dropped { low: Flag::new(), mid: Flag::new(), @@ -77,7 +77,7 @@ fn make_chain() -> (Error, Dropped) { .unwrap_err(); // impl Context for Result - let high = Err::<(), Error>(mid) + let high = Err::<(), ErrReport>(mid) .context(HighLevel { message: "failed to start server", drop: DetectDrop::new(&dropped.high), diff --git a/tests/test_convert.rs b/tests/test_convert.rs index 72da020..2a6adce 100644 --- a/tests/test_convert.rs +++ b/tests/test_convert.rs @@ -1,13 +1,13 @@ mod drop; use self::drop::{DetectDrop, Flag}; -use anyhow::{Error, Result}; +use eyre::{ErrReport, Result}; use std::error::Error as StdError; #[test] fn test_convert() { let has_dropped = Flag::new(); - let error = Error::new(DetectDrop::new(&has_dropped)); + let error = ErrReport::new(DetectDrop::new(&has_dropped)); let box_dyn = Box::::from(error); assert_eq!("oh no!", box_dyn.to_string()); drop(box_dyn); diff --git a/tests/test_downcast.rs b/tests/test_downcast.rs index c2c3e12..b1b4a04 100644 --- a/tests/test_downcast.rs +++ b/tests/test_downcast.rs @@ -3,7 +3,7 @@ mod drop; use self::common::*; use self::drop::{DetectDrop, Flag}; -use anyhow::Error; +use eyre::ErrReport; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io; @@ -71,7 +71,7 @@ fn test_downcast_mut() { #[test] fn test_drop() { let has_dropped = Flag::new(); - let error = Error::new(DetectDrop::new(&has_dropped)); + let error = ErrReport::new(DetectDrop::new(&has_dropped)); drop(error.downcast::().unwrap()); assert!(has_dropped.get()); } @@ -90,7 +90,7 @@ fn test_large_alignment() { impl StdError for LargeAlignedError {} - let error = Error::new(LargeAlignedError("oh no!")); + let error = ErrReport::new(LargeAlignedError("oh no!")); assert_eq!( "oh no!", error.downcast_ref::().unwrap().0 diff --git a/tests/test_fmt.rs b/tests/test_fmt.rs index cc49291..4005af3 100644 --- a/tests/test_fmt.rs +++ b/tests/test_fmt.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use eyre::{bail, Context, Result}; use std::io; fn f() -> Result<()> { diff --git a/tests/test_macros.rs b/tests/test_macros.rs index c6888b6..8e2d5bf 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -1,7 +1,7 @@ mod common; use self::common::*; -use anyhow::ensure; +use eyre::ensure; #[test] fn test_messages() { diff --git a/tests/test_repr.rs b/tests/test_repr.rs index 72f5002..f71737f 100644 --- a/tests/test_repr.rs +++ b/tests/test_repr.rs @@ -1,29 +1,29 @@ mod drop; use self::drop::{DetectDrop, Flag}; -use anyhow::Error; +use eyre::ErrReport; use std::marker::Unpin; use std::mem; #[test] fn test_error_size() { - assert_eq!(mem::size_of::(), mem::size_of::()); + assert_eq!(mem::size_of::(), mem::size_of::()); } #[test] fn test_null_pointer_optimization() { - assert_eq!(mem::size_of::>(), mem::size_of::()); + assert_eq!(mem::size_of::>(), mem::size_of::()); } #[test] fn test_autotraits() { fn assert() {} - assert::(); + assert::(); } #[test] fn test_drop() { let has_dropped = Flag::new(); - drop(Error::new(DetectDrop::new(&has_dropped))); + drop(ErrReport::new(DetectDrop::new(&has_dropped))); assert!(has_dropped.get()); } diff --git a/tests/test_source.rs b/tests/test_source.rs index 018267d..c4b7d38 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,4 +1,4 @@ -use anyhow::anyhow; +use eyre::eyre; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io; @@ -26,37 +26,37 @@ impl StdError for TestError { #[test] fn test_literal_source() { - let error = anyhow!("oh no!"); + let error = eyre!("oh no!"); assert!(error.source().is_none()); } #[test] fn test_variable_source() { let msg = "oh no!"; - let error = anyhow!(msg); + let error = eyre!(msg); assert!(error.source().is_none()); let msg = msg.to_owned(); - let error = anyhow!(msg); + let error = eyre!(msg); assert!(error.source().is_none()); } #[test] fn test_fmt_source() { - let error = anyhow!("{} {}!", "oh", "no"); + let error = eyre!("{} {}!", "oh", "no"); assert!(error.source().is_none()); } #[test] fn test_io_source() { let io = io::Error::new(io::ErrorKind::Other, "oh no!"); - let error = anyhow!(TestError::Io(io)); + let error = eyre!(TestError::Io(io)); assert_eq!("oh no!", error.source().unwrap().to_string()); } #[test] -fn test_anyhow_from_anyhow() { - let error = anyhow!("oh no!").context("context"); - let error = anyhow!(error); +fn test_eyre_from_eyre() { + let error = eyre!("oh no!").context("context"); + let error = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } diff --git a/tests/ui/no-impl.rs b/tests/ui/no-impl.rs index d2e89af..176ba65 100644 --- a/tests/ui/no-impl.rs +++ b/tests/ui/no-impl.rs @@ -1,8 +1,8 @@ -use anyhow::anyhow; +use eyre::eyre; #[derive(Debug)] struct Error; fn main() { - let _ = anyhow!(Error); + let _ = eyre!(Error); } diff --git a/tests/ui/no-impl.stderr b/tests/ui/no-impl.stderr index ab1d72c..f9cbc3d 100644 --- a/tests/ui/no-impl.stderr +++ b/tests/ui/no-impl.stderr @@ -1,16 +1,16 @@ -error[E0599]: no method named `anyhow_kind` found for reference `&Error` in the current scope +error[E0599]: no method named `eyre_kind` found for reference `&Error` in the current scope --> $DIR/no-impl.rs:7:13 | -7 | let _ = anyhow!(Error); - | ^^^^^^^^^^^^^^ method not found in `&Error` +7 | let _ = eyre!(Error); + | ^^^^^^^^^^^^ method not found in `&Error` | - = note: the method `anyhow_kind` exists but the following trait bounds were not satisfied: - `&Error : anyhow::kind::AdhocKind` - `&Error : anyhow::kind::TraitKind` - `Error : anyhow::kind::TraitKind` + = note: the method `eyre_kind` exists but the following trait bounds were not satisfied: + `&Error : eyre::kind::AdhocKind` + `&Error : eyre::kind::TraitKind` + `Error : eyre::kind::TraitKind` = help: items from traits can only be used if the trait is implemented and in scope - = note: the following traits define an item `anyhow_kind`, perhaps you need to implement one of them: - candidate #1: `anyhow::kind::AdhocKind` - candidate #2: `anyhow::kind::TraitKind` - candidate #3: `anyhow::kind::BoxedKind` + = note: the following traits define an item `eyre_kind`, perhaps you need to implement one of them: + candidate #1: `eyre::kind::AdhocKind` + candidate #2: `eyre::kind::TraitKind` + candidate #3: `eyre::kind::BoxedKind` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) From c476a98f77f3306655f09a3f50a34cd1a7ca77fd Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 27 Feb 2020 13:18:06 -0800 Subject: [PATCH 02/10] Rename Context trait --- README.md | 2 +- src/context.rs | 8 ++++---- src/error.rs | 6 +++--- src/lib.rs | 16 ++++++++-------- tests/test_context.rs | 6 +++--- tests/test_fmt.rs | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 4f9bd59..fdb6971 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ eyre = "1.0" application was in the middle of. ```rust - use eyre::{Context, Result}; + use eyre::{Report, Result}; fn main() -> Result<()> { ... diff --git a/src/context.rs b/src/context.rs index 0e4345e..7dc631f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,5 +1,5 @@ use crate::error::ContextError; -use crate::{Context, ErrReport, StdError}; +use crate::{Report, ErrReport, StdError}; use core::convert::Infallible; use core::fmt::{self, Debug, Display, Write}; @@ -39,7 +39,7 @@ mod ext { } } -impl Context for Result +impl Report for Result where E: ext::StdError + Send + Sync + 'static, { @@ -62,7 +62,7 @@ where /// ``` /// # type T = (); /// # -/// use eyre::{Context, Result}; +/// use eyre::{Report, Result}; /// /// fn maybe_get() -> Option { /// # const IGNORE: &str = stringify! { @@ -79,7 +79,7 @@ where /// # unimplemented!() /// } /// ``` -impl Context for Option { +impl Report for Option { fn context(self, context: C) -> Result where C: Display + Send + Sync + 'static, diff --git a/src/error.rs b/src/error.rs index 23c374e..8ef970b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -208,11 +208,11 @@ impl ErrReport { /// Wrap the error value with additional context. /// /// For attaching context to a `Result` as it is propagated, the - /// [`Context`][crate::Context] extension trait may be more convenient than + /// [`Report`][crate::Report] extension trait may be more convenient than /// this function. /// /// The primary reason to use `error.context(...)` instead of - /// `result.context(...)` via the `Context` trait would be if the context + /// `result.context(...)` via the `Report` trait would be if the context /// needs to depend on some data held by the underlying error: /// /// ``` @@ -349,7 +349,7 @@ impl ErrReport { /// context has been attached. For details about the interaction between /// context and downcasting, [see here]. /// - /// [see here]: trait.Context.html#effect-on-downcasting + /// [see here]: trait.Report.html#effect-on-downcasting pub fn is(&self) -> bool where E: Display + Debug + Send + Sync + 'static, diff --git a/src/lib.rs b/src/lib.rs index 8621113..a41f39f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ //! # } //! # } //! # -//! use eyre::{Context, Result}; +//! use eyre::{Report, Result}; //! //! fn main() -> Result<()> { //! # return Ok(()); @@ -273,7 +273,7 @@ pub use eyre as format_err; /// at /git/eyre/src/backtrace.rs:26 /// 1: core::result::Result::map_err /// at /git/rustc/src/libcore/result.rs:596 -/// 2: eyre::context:: for core::result::Result>::with_context +/// 2: eyre::context:: for core::result::Result>::with_context /// at /git/eyre/src/context.rs:58 /// 3: testing::main /// at src/main.rs:5 @@ -302,7 +302,7 @@ pub use eyre as format_err; /// like this: /// /// ``` -/// use eyre::{Context, Result}; +/// use eyre::{Report, Result}; /// /// fn main() { /// if let Err(err) = try_main() { @@ -352,7 +352,7 @@ pub struct Chain<'a> { /// /// This is a reasonable return type to use throughout your application but also /// for `fn main`; if you do, failures will be printed along with any -/// [context][Context] and a backtrace if one was captured. +/// [context][Report] and a backtrace if one was captured. /// /// `eyre::Result` may be used with one *or* two type parameters. /// @@ -409,7 +409,7 @@ pub type Result = core::result::Result; /// # Example /// /// ``` -/// use eyre::{Context, Result}; +/// use eyre::{Report, Result}; /// use std::fs; /// use std::path::PathBuf; /// @@ -479,7 +479,7 @@ pub type Result = core::result::Result; /// # bail!(SuspiciousError); /// # } /// # -/// use eyre::{Context, Result}; +/// use eyre::{Report, Result}; /// /// fn do_it() -> Result<()> { /// helper().context("Failed to complete the work")?; @@ -519,7 +519,7 @@ pub type Result = core::result::Result; /// # bail!("no such file or directory"); /// # } /// # -/// use eyre::{Context, Result}; +/// use eyre::{Report, Result}; /// /// fn do_it() -> Result<()> { /// helper().context(HelperFailed)?; @@ -540,7 +540,7 @@ pub type Result = core::result::Result; /// # panic!("expected downcast to succeed"); /// } /// ``` -pub trait Context: context::private::Sealed { +pub trait Report: context::private::Sealed { /// Wrap the error value with additional context. fn context(self, context: C) -> Result where diff --git a/tests/test_context.rs b/tests/test_context.rs index de67d8e..f7849bf 100644 --- a/tests/test_context.rs +++ b/tests/test_context.rs @@ -1,7 +1,7 @@ mod drop; use crate::drop::{DetectDrop, Flag}; -use eyre::{Context, ErrReport, Result}; +use eyre::{Report, ErrReport, Result}; use std::fmt::{self, Display}; use thiserror::Error; @@ -68,7 +68,7 @@ fn make_chain() -> (ErrReport, Dropped) { drop: DetectDrop::new(&dropped.low), }; - // impl Context for Result + // impl Report for Result let mid = Err::<(), LowLevel>(low) .context(MidLevel { message: "failed to load config", @@ -76,7 +76,7 @@ fn make_chain() -> (ErrReport, Dropped) { }) .unwrap_err(); - // impl Context for Result + // impl Report for Result let high = Err::<(), ErrReport>(mid) .context(HighLevel { message: "failed to start server", diff --git a/tests/test_fmt.rs b/tests/test_fmt.rs index 4005af3..f2af55d 100644 --- a/tests/test_fmt.rs +++ b/tests/test_fmt.rs @@ -1,4 +1,4 @@ -use eyre::{bail, Context, Result}; +use eyre::{bail, Report, Result}; use std::io; fn f() -> Result<()> { From 74938f4ad897812185009b72781061a60540c30e Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 27 Feb 2020 15:50:57 -0800 Subject: [PATCH 03/10] Allow customization of error context --- src/backtrace.rs | 12 +- src/context.rs | 70 ++++----- src/error.rs | 333 ++++++++++++++++++++++------------------ src/fmt.rs | 6 +- src/kind.rs | 15 +- src/lib.rs | 58 +++++-- tests/test_backtrace.rs | 4 +- tests/test_boxed.rs | 4 +- tests/test_convert.rs | 2 +- tests/test_downcast.rs | 6 +- tests/test_fmt.rs | 6 +- tests/test_macros.rs | 8 +- tests/test_repr.rs | 3 +- tests/test_source.rs | 14 +- 14 files changed, 308 insertions(+), 233 deletions(-) diff --git a/src/backtrace.rs b/src/backtrace.rs index 01e33cb..462f5ca 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -4,12 +4,12 @@ pub(crate) use std::backtrace::Backtrace; #[cfg(not(backtrace))] pub(crate) enum Backtrace {} -#[cfg(backtrace)] -macro_rules! backtrace { - () => { - Some(Backtrace::capture()) - }; -} +// #[cfg(backtrace)] +// macro_rules! backtrace { +// () => { +// Some(Backtrace::capture()) +// }; +// } #[cfg(not(backtrace))] macro_rules! backtrace { diff --git a/src/context.rs b/src/context.rs index 7dc631f..9fe64ac 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,5 +1,5 @@ use crate::error::ContextError; -use crate::{Report, ErrReport, StdError}; +use crate::{Report, DefaultContext, ErrReport, StdError, EyreContext}; use core::convert::Infallible; use core::fmt::{self, Debug, Display, Write}; @@ -10,9 +10,9 @@ mod ext { use super::*; pub trait StdError { - fn ext_context(self, context: C) -> ErrReport + fn ext_context(self, context: D) -> ErrReport where - C: Display + Send + Sync + 'static; + D: Display + Send + Sync + 'static; } #[cfg(feature = "std")] @@ -20,19 +20,18 @@ mod ext { where E: std::error::Error + Send + Sync + 'static, { - fn ext_context(self, context: C) -> ErrReport + fn ext_context(self, context: D) -> ErrReport where - C: Display + Send + Sync + 'static, + D: Display + Send + Sync + 'static, { - let backtrace = backtrace_if_absent!(self); - ErrReport::from_context(context, self, backtrace) + ErrReport::from_context(context, self) } } - impl StdError for ErrReport { - fn ext_context(self, context: C) -> ErrReport + impl StdError for ErrReport { + fn ext_context(self, context: D) -> ErrReport where - C: Display + Send + Sync + 'static, + D: Display + Send + Sync + 'static, { self.context(context) } @@ -43,17 +42,17 @@ impl Report for Result where E: ext::StdError + Send + Sync + 'static, { - fn context(self, context: C) -> Result + fn context(self, context: D) -> Result> where - C: Display + Send + Sync + 'static, + D: Display + Send + Sync + 'static, { self.map_err(|error| error.ext_context(context)) } - fn with_context(self, context: F) -> Result + fn with_context(self, context: F) -> Result> where - C: Display + Send + Sync + 'static, - F: FnOnce() -> C, + D: Display + Send + Sync + 'static, + F: FnOnce() -> D, { self.map_err(|error| error.ext_context(context())) } @@ -80,47 +79,47 @@ where /// } /// ``` impl Report for Option { - fn context(self, context: C) -> Result + fn context(self, context: D) -> Result> where - C: Display + Send + Sync + 'static, + D: Display + Send + Sync + 'static, { - self.ok_or_else(|| ErrReport::from_display(context, backtrace!())) + self.ok_or_else(|| ErrReport::from_display(context)) } - fn with_context(self, context: F) -> Result + fn with_context(self, context: F) -> Result> where - C: Display + Send + Sync + 'static, - F: FnOnce() -> C, + D: Display + Send + Sync + 'static, + F: FnOnce() -> D, { - self.ok_or_else(|| ErrReport::from_display(context(), backtrace!())) + self.ok_or_else(|| ErrReport::from_display(context())) } } -impl Debug for ContextError +impl Debug for ContextError where - C: Display, + D: Display, E: Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Error") - .field("context", &Quoted(&self.context)) + .field("msg", &Quoted(&self.msg)) .field("source", &self.error) .finish() } } -impl Display for ContextError +impl Display for ContextError where - C: Display, + D: Display, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&self.context, f) + Display::fmt(&self.msg, f) } } -impl StdError for ContextError +impl StdError for ContextError where - C: Display, + D: Display, E: StdError + 'static, { #[cfg(backtrace)] @@ -133,9 +132,10 @@ where } } -impl StdError for ContextError +impl StdError for ContextError> where - C: Display, + C: EyreContext, + D: Display, { #[cfg(backtrace)] fn backtrace(&self) -> Option<&Backtrace> { @@ -147,11 +147,11 @@ where } } -struct Quoted(C); +struct Quoted(D); -impl Debug for Quoted +impl Debug for Quoted where - C: Display, + D: Display, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_char('"')?; diff --git a/src/error.rs b/src/error.rs index 8ef970b..1704359 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,5 @@ use crate::alloc::Box; +use std::any::Any; use crate::backtrace::Backtrace; use crate::chain::Chain; use crate::{ErrReport, StdError}; @@ -6,11 +7,15 @@ use core::any::TypeId; use core::fmt::{self, Debug, Display}; use core::mem::{self, ManuallyDrop}; use core::ptr::{self, NonNull}; +use crate::EyreContext; #[cfg(feature = "std")] use core::ops::{Deref, DerefMut}; -impl ErrReport { +impl ErrReport +where + C: EyreContext, +{ /// Create a new error object from any error type. /// /// The error type must be threadsafe and `'static`, so that the `ErrReport` @@ -23,8 +28,7 @@ impl ErrReport { where E: StdError + Send + Sync + 'static, { - let backtrace = backtrace_if_absent!(error); - ErrReport::from_std(error, backtrace) + ErrReport::from_std(error) } /// Create a new error object from a printable error message. @@ -68,112 +72,111 @@ impl ErrReport { where M: Display + Debug + Send + Sync + 'static, { - ErrReport::from_adhoc(message, backtrace!()) + ErrReport::from_adhoc(message) } #[cfg(feature = "std")] - pub(crate) fn from_std(error: E, backtrace: Option) -> Self + pub(crate) fn from_std(error: E) -> Self where E: StdError + Send + Sync + 'static, { let vtable = &ErrorVTable { - object_drop: object_drop::, - object_ref: object_ref::, + object_drop: object_drop::, + object_ref: object_ref::, #[cfg(feature = "std")] - object_mut: object_mut::, - object_boxed: object_boxed::, - object_downcast: object_downcast::, - object_drop_rest: object_drop_front::, + object_mut: object_mut::, + object_boxed: object_boxed::, + object_downcast: object_downcast::, + object_drop_rest: object_drop_front::, }; // Safety: passing vtable that operates on the right type E. - unsafe { ErrReport::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable) } } - pub(crate) fn from_adhoc(message: M, backtrace: Option) -> Self + pub(crate) fn from_adhoc(message: M) -> Self where M: Display + Debug + Send + Sync + 'static, { use crate::wrapper::MessageError; let error: MessageError = MessageError(message); let vtable = &ErrorVTable { - object_drop: object_drop::>, - object_ref: object_ref::>, + object_drop: object_drop::, C>, + object_ref: object_ref::, C>, #[cfg(feature = "std")] - object_mut: object_mut::>, - object_boxed: object_boxed::>, - object_downcast: object_downcast::, - object_drop_rest: object_drop_front::, + object_mut: object_mut::, C>, + object_boxed: object_boxed::, C>, + object_downcast: object_downcast::, + object_drop_rest: object_drop_front::, }; // Safety: MessageError is repr(transparent) so it is okay for the // vtable to allow casting the MessageError to M. - unsafe { ErrReport::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable) } } - pub(crate) fn from_display(message: M, backtrace: Option) -> Self + pub(crate) fn from_display(message: M) -> Self where M: Display + Send + Sync + 'static, { use crate::wrapper::DisplayError; let error: DisplayError = DisplayError(message); let vtable = &ErrorVTable { - object_drop: object_drop::>, - object_ref: object_ref::>, + object_drop: object_drop::, C>, + object_ref: object_ref::, C>, #[cfg(feature = "std")] - object_mut: object_mut::>, - object_boxed: object_boxed::>, - object_downcast: object_downcast::, - object_drop_rest: object_drop_front::, + object_mut: object_mut::, C>, + object_boxed: object_boxed::, C>, + object_downcast: object_downcast::, + object_drop_rest: object_drop_front::, }; // Safety: DisplayError is repr(transparent) so it is okay for the // vtable to allow casting the DisplayError to M. - unsafe { ErrReport::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable) } } #[cfg(feature = "std")] - pub(crate) fn from_context(context: C, error: E, backtrace: Option) -> Self + pub(crate) fn from_context(msg: D, error: E) -> Self where - C: Display + Send + Sync + 'static, + D: Display + Send + Sync + 'static, E: StdError + Send + Sync + 'static, { - let error: ContextError = ContextError { context, error }; + let error: ContextError = ContextError { msg, error }; let vtable = &ErrorVTable { - object_drop: object_drop::>, - object_ref: object_ref::>, + object_drop: object_drop::, C>, + object_ref: object_ref::, C>, #[cfg(feature = "std")] - object_mut: object_mut::>, - object_boxed: object_boxed::>, - object_downcast: context_downcast::, - object_drop_rest: context_drop_rest::, + object_mut: object_mut::, C>, + object_boxed: object_boxed::, C>, + object_downcast: context_downcast::, + object_drop_rest: context_drop_rest::, }; // Safety: passing vtable that operates on the right type. - unsafe { ErrReport::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable) } } #[cfg(feature = "std")] pub(crate) fn from_boxed( error: Box, - backtrace: Option, ) -> Self { use crate::wrapper::BoxedError; let error = BoxedError(error); let vtable = &ErrorVTable { - object_drop: object_drop::, - object_ref: object_ref::, + object_drop: object_drop::, + object_ref: object_ref::, #[cfg(feature = "std")] - object_mut: object_mut::, - object_boxed: object_boxed::, - object_downcast: object_downcast::>, - object_drop_rest: object_drop_front::>, + object_mut: object_mut::, + object_boxed: object_boxed::, + object_downcast: object_downcast::, C>, + object_drop_rest: object_drop_front::, C>, }; // Safety: BoxedError is repr(transparent) so it is okay for the vtable // to allow casting to Box. - unsafe { ErrReport::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable) } } // Takes backtrace as argument rather than capturing it here so that the @@ -183,15 +186,15 @@ impl ErrReport { // value of type E. unsafe fn construct( error: E, - vtable: &'static ErrorVTable, - backtrace: Option, + vtable: &'static ErrorVTable, ) -> Self where E: StdError + Send + Sync + 'static, { + let context = C::default(&error); let inner = Box::new(ErrorImpl { vtable, - backtrace, + context, _object: error, }); // Erase the concrete type of E from the compile-time type system. This @@ -200,7 +203,7 @@ impl ErrReport { // result is a thin pointer. The necessary behavior for manipulating the // underlying ErrorImpl is preserved in the vtable provided by the // caller rather than a builtin fat pointer vtable. - let erased = mem::transmute::>, Box>>(inner); + let erased = mem::transmute::>, Box>>(inner); let inner = ManuallyDrop::new(erased); ErrReport { inner } } @@ -259,30 +262,27 @@ impl ErrReport { /// }) /// } /// ``` - pub fn context(self, context: C) -> Self + pub fn context(self, msg: D) -> Self where - C: Display + Send + Sync + 'static, + D: Display + Send + Sync + 'static, { - let error: ContextError = ContextError { - context, + let error: ContextError> = ContextError { + msg, error: self, }; let vtable = &ErrorVTable { - object_drop: object_drop::>, - object_ref: object_ref::>, + object_drop: object_drop::>, C>, + object_ref: object_ref::>, C>, #[cfg(feature = "std")] - object_mut: object_mut::>, - object_boxed: object_boxed::>, - object_downcast: context_chain_downcast::, - object_drop_rest: context_chain_drop_rest::, + object_mut: object_mut::>, C>, + object_boxed: object_boxed::>, C>, + object_downcast: context_chain_downcast::, + object_drop_rest: context_chain_drop_rest::, }; - // As the cause is eyre::ErrReport, we already have a backtrace for it. - let backtrace = None; - // Safety: passing vtable that operates on the right type. - unsafe { ErrReport::construct(error, vtable, backtrace) } + unsafe { ErrReport::construct(error, vtable) } } /// Get the backtrace for this ErrReport. @@ -345,7 +345,7 @@ impl ErrReport { /// Returns true if `E` is the type held by this error object. /// /// For errors with context, this method returns true if `E` matches the - /// type of the context `C` **or** the type of the error on which the + /// type of the context `D` **or** the type of the error on which the /// context has been attached. For details about the interaction between /// context and downcasting, [see here]. /// @@ -395,7 +395,7 @@ impl ErrReport { /// # Example /// /// ``` - /// # use eyre::eyre; + /// # use eyre::{ErrReport, eyre}; /// # use std::fmt::{self, Display}; /// # use std::task::Poll; /// # @@ -414,7 +414,7 @@ impl ErrReport { /// # /// # const REDACTED_CONTENT: () = (); /// # - /// # let error = eyre!("..."); + /// # let error: ErrReport = eyre!("..."); /// # let root_cause = &error; /// # /// # let ret = @@ -455,18 +455,18 @@ impl ErrReport { } #[cfg(feature = "std")] -impl From for ErrReport +impl From for ErrReport where + C: EyreContext, E: StdError + Send + Sync + 'static, { fn from(error: E) -> Self { - let backtrace = backtrace_if_absent!(error); - ErrReport::from_std(error, backtrace) + ErrReport::from_std(error) } } #[cfg(feature = "std")] -impl Deref for ErrReport { +impl Deref for ErrReport { type Target = dyn StdError + Send + Sync + 'static; fn deref(&self) -> &Self::Target { @@ -475,25 +475,28 @@ impl Deref for ErrReport { } #[cfg(feature = "std")] -impl DerefMut for ErrReport { +impl DerefMut for ErrReport { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.error_mut() } } -impl Display for ErrReport { +impl Display for ErrReport { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.inner.display(formatter) } } -impl Debug for ErrReport { +impl Debug for ErrReport { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { self.inner.debug(formatter) } } -impl Drop for ErrReport { +impl Drop for ErrReport +where + C: EyreContext, +{ fn drop(&mut self) { unsafe { // Read Box> from self. @@ -506,71 +509,85 @@ impl Drop for ErrReport { } } -struct ErrorVTable { - object_drop: unsafe fn(Box>), - object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static), +struct ErrorVTable +where + C: EyreContext, +{ + object_drop: unsafe fn(Box>), + object_ref: unsafe fn(&ErrorImpl<(), C>) -> &(dyn StdError + Send + Sync + 'static), #[cfg(feature = "std")] - object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static), - object_boxed: unsafe fn(Box>) -> Box, - object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option>, - object_drop_rest: unsafe fn(Box>, TypeId), + object_mut: unsafe fn(&mut ErrorImpl<(), C>) -> &mut (dyn StdError + Send + Sync + 'static), + object_boxed: unsafe fn(Box>) -> Box, + object_downcast: unsafe fn(&ErrorImpl<(), C>, TypeId) -> Option>, + object_drop_rest: unsafe fn(Box>, TypeId), } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_drop(e: Box>) { +unsafe fn object_drop(e: Box>) +where + C: EyreContext, +{ // Cast back to ErrorImpl so that the allocator receives the correct // Layout to deallocate the Box's memory. - let unerased = mem::transmute::>, Box>>(e); + let unerased = mem::transmute::>, Box>>(e); drop(unerased); } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_drop_front(e: Box>, target: TypeId) { +unsafe fn object_drop_front(e: Box>, target: TypeId) +where + C: EyreContext, +{ // Drop the fields of ErrorImpl other than E as well as the Box allocation, // without dropping E itself. This is used by downcast after doing a // ptr::read to take ownership of the E. let _ = target; - let unerased = mem::transmute::>, Box>>>(e); + let unerased = mem::transmute::>, Box, C>>>(e); drop(unerased); } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_ref(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static) +unsafe fn object_ref(e: &ErrorImpl<(), C>) -> &(dyn StdError + Send + Sync + 'static) where + C: EyreContext, E: StdError + Send + Sync + 'static, { // Attach E's native StdError vtable onto a pointer to self._object. - &(*(e as *const ErrorImpl<()> as *const ErrorImpl))._object + &(*(e as *const ErrorImpl<(), C> as *const ErrorImpl))._object } // Safety: requires layout of *e to match ErrorImpl. #[cfg(feature = "std")] -unsafe fn object_mut(e: &mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static) +unsafe fn object_mut(e: &mut ErrorImpl<(), C>) -> &mut (dyn StdError + Send + Sync + 'static) where + C: EyreContext, E: StdError + Send + Sync + 'static, { // Attach E's native StdError vtable onto a pointer to self._object. - &mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl))._object + &mut (*(e as *mut ErrorImpl<(), C> as *mut ErrorImpl))._object } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_boxed(e: Box>) -> Box +unsafe fn object_boxed(e: Box>) -> Box where + C: EyreContext, E: StdError + Send + Sync + 'static, + C: Send + Sync + 'static, { // Attach ErrorImpl's native StdError vtable. The StdError impl is below. - mem::transmute::>, Box>>(e) + mem::transmute::>, Box>>(e) } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> +unsafe fn object_downcast(e: &ErrorImpl<(), C>, target: TypeId) -> Option> where + C: EyreContext, E: 'static, { if TypeId::of::() == target { // Caller is looking for an E pointer and e is ErrorImpl, take a // pointer to its E field. - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl; + let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl; let addr = &(*unerased)._object as *const E as *mut (); Some(NonNull::new_unchecked(addr)) } else { @@ -578,19 +595,20 @@ where } } -// Safety: requires layout of *e to match ErrorImpl>. +// Safety: requires layout of *e to match ErrorImpl>. #[cfg(feature = "std")] -unsafe fn context_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> +unsafe fn context_downcast(e: &ErrorImpl<(), C>, target: TypeId) -> Option> where - C: 'static, + C: EyreContext, + D: 'static, E: 'static, { - if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; - let addr = &(*unerased)._object.context as *const C as *mut (); + if TypeId::of::() == target { + let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl, C>; + let addr = &(*unerased)._object.msg as *const D as *mut (); Some(NonNull::new_unchecked(addr)) } else if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; + let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl, C>; let addr = &(*unerased)._object.error as *const E as *mut (); Some(NonNull::new_unchecked(addr)) } else { @@ -598,65 +616,68 @@ where } } -// Safety: requires layout of *e to match ErrorImpl>. +// Safety: requires layout of *e to match ErrorImpl>. #[cfg(feature = "std")] -unsafe fn context_drop_rest(e: Box>, target: TypeId) +unsafe fn context_drop_rest(e: Box>, target: TypeId) where - C: 'static, + C: EyreContext, + D: 'static, E: 'static, { - // Called after downcasting by value to either the C or the E and doing a + // Called after downcasting by value to either the D or the E and doing a // ptr::read to take ownership of that value. - if TypeId::of::() == target { + if TypeId::of::() == target { let unerased = mem::transmute::< - Box>, - Box, E>>>, + Box>, + Box, E>, C>>, >(e); drop(unerased); } else { let unerased = mem::transmute::< - Box>, - Box>>>, + Box>, + Box>, C>>, >(e); drop(unerased); } } -// Safety: requires layout of *e to match ErrorImpl>. -unsafe fn context_chain_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> +// Safety: requires layout of *e to match ErrorImpl>. +unsafe fn context_chain_downcast(e: &ErrorImpl<(), C>, target: TypeId) -> Option> where - C: 'static, + C: EyreContext, + D: 'static, { - if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; - let addr = &(*unerased)._object.context as *const C as *mut (); + if TypeId::of::() == target { + let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl>, C>; + let addr = &(*unerased)._object.msg as *const D as *mut (); Some(NonNull::new_unchecked(addr)) } else { // Recurse down the context chain per the inner error's vtable. - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; + let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl>, C>; let source = &(*unerased)._object.error; (source.inner.vtable.object_downcast)(&source.inner, target) } } -// Safety: requires layout of *e to match ErrorImpl>. -unsafe fn context_chain_drop_rest(e: Box>, target: TypeId) +// Safety: requires layout of *e to match ErrorImpl>. +unsafe fn context_chain_drop_rest(e: Box>, target: TypeId) where - C: 'static, + C: EyreContext, + D: 'static, { - // Called after downcasting by value to either the C or one of the causes + // Called after downcasting by value to either the D or one of the causes // and doing a ptr::read to take ownership of that value. - if TypeId::of::() == target { + if TypeId::of::() == target { let unerased = mem::transmute::< - Box>, - Box, ErrReport>>>, + Box>, + Box, ErrReport>, C>>, >(e); // Drop the entire rest of the data structure rooted in the next ErrReport. drop(unerased); } else { let unerased = mem::transmute::< - Box>, - Box>>>, + Box>, + Box>>, C>>, >(e); // Read out a ManuallyDrop>> from the next error. let inner = ptr::read(&unerased._object.error.inner); @@ -669,32 +690,41 @@ where // repr C to ensure that E remains in the final position. #[repr(C)] -pub(crate) struct ErrorImpl { - vtable: &'static ErrorVTable, - backtrace: Option, +pub(crate) struct ErrorImpl +where + C: EyreContext, +{ + vtable: &'static ErrorVTable, + context: C, // NOTE: Don't use directly. Use only through vtable. Erased type may have // different alignment. _object: E, } -// repr C to ensure that ContextError has the same layout as -// ContextError, E> and ContextError>. +// repr C to ensure that ContextError has the same layout as +// ContextError, E> and ContextError>. #[repr(C)] -pub(crate) struct ContextError { - pub context: C, +pub(crate) struct ContextError { + pub msg: D, pub error: E, } -impl ErrorImpl { - fn erase(&self) -> &ErrorImpl<()> { +impl ErrorImpl +where + C: EyreContext, +{ + fn erase(&self) -> &ErrorImpl<(), C> { // Erase the concrete type of E but preserve the vtable in self.vtable // for manipulating the resulting thin pointer. This is analogous to an // unsize coersion. - unsafe { &*(self as *const ErrorImpl as *const ErrorImpl<()>) } + unsafe { &*(self as *const ErrorImpl as *const ErrorImpl<(), C>) } } } -impl ErrorImpl<()> { +impl ErrorImpl<(), C> +where + C: EyreContext, +{ pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) { // Use vtable to attach E's native StdError vtable for the right // original type E. @@ -708,13 +738,17 @@ impl ErrorImpl<()> { unsafe { &mut *(self.vtable.object_mut)(self) } } + + pub fn context(&self) -> Option<&T> { + self.context.context_raw(TypeId::of::())?.downcast_ref::() + } + #[cfg(backtrace)] pub(crate) fn backtrace(&self) -> &Backtrace { // This unwrap can only panic if the underlying error's backtrace method // is nondeterministic, which would only happen in maliciously // constructed code. - self.backtrace - .as_ref() + self.context() .or_else(|| self.error().backtrace()) .expect("backtrace capture failed") } @@ -724,8 +758,9 @@ impl ErrorImpl<()> { } } -impl StdError for ErrorImpl +impl StdError for ErrorImpl where + C: EyreContext, E: StdError, { #[cfg(backtrace)] @@ -738,8 +773,9 @@ where } } -impl Debug for ErrorImpl +impl Debug for ErrorImpl where + C: EyreContext, E: Debug, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -747,8 +783,9 @@ where } } -impl Display for ErrorImpl +impl Display for ErrorImpl where + C: EyreContext, E: Display, { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -756,8 +793,8 @@ where } } -impl From for Box { - fn from(error: ErrReport) -> Self { +impl From> for Box { + fn from(error: ErrReport) -> Self { let outer = ManuallyDrop::new(error); unsafe { // Read Box> from error. Can't move it out because @@ -772,21 +809,21 @@ impl From for Box { } } -impl From for Box { - fn from(error: ErrReport) -> Self { +impl From> for Box { + fn from(error: ErrReport) -> Self { Box::::from(error) } } #[cfg(feature = "std")] -impl AsRef for ErrReport { +impl AsRef for ErrReport { fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) { &**self } } #[cfg(feature = "std")] -impl AsRef for ErrReport { +impl AsRef for ErrReport { fn as_ref(&self) -> &(dyn StdError + 'static) { &**self } diff --git a/src/fmt.rs b/src/fmt.rs index 16a82ba..54b4e0a 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,8 +1,12 @@ use crate::chain::Chain; use crate::error::ErrorImpl; use core::fmt::{self, Debug, Write}; +use crate::EyreContext; -impl ErrorImpl<()> { +impl ErrorImpl<(), C> +where + C: EyreContext, +{ pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.error())?; diff --git a/src/kind.rs b/src/kind.rs index 8235c25..eebf8ae 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -44,14 +44,14 @@ // let error = $msg; // (&error).eyre_kind().new(error) -use crate::ErrReport; +use crate::{EyreContext, ErrReport}; use core::fmt::{Debug, Display}; #[cfg(feature = "std")] use crate::StdError; -#[cfg(backtrace)] -use std::backtrace::Backtrace; +// #[cfg(backtrace)] +// use std::backtrace::Backtrace; pub struct Adhoc; @@ -65,11 +65,11 @@ pub trait AdhocKind: Sized { impl AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} impl Adhoc { - pub fn new(self, message: M) -> ErrReport + pub fn new(self, message: M) -> ErrReport where M: Display + Debug + Send + Sync + 'static, { - ErrReport::from_adhoc(message, backtrace!()) + ErrReport::from_adhoc(message) } } @@ -109,8 +109,7 @@ impl BoxedKind for Box {} #[cfg(feature = "std")] impl Boxed { - pub fn new(self, error: Box) -> ErrReport { - let backtrace = backtrace_if_absent!(error); - ErrReport::from_boxed(error, backtrace) + pub fn new(self, error: Box) -> ErrReport { + ErrReport::from_boxed(error) } } diff --git a/src/lib.rs b/src/lib.rs index a41f39f..a451e03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,7 @@ //! mutable reference as needed. //! //! ``` -//! # use eyre::eyre; +//! # use eyre::{ErrReport, eyre}; //! # use std::fmt::{self, Display}; //! # use std::task::Poll; //! # @@ -107,7 +107,7 @@ //! # //! # const REDACTED_CONTENT: () = (); //! # -//! # let error = eyre!("..."); +//! # let error: ErrReport = eyre!("..."); //! # let root_cause = &error; //! # //! # let ret = @@ -211,6 +211,9 @@ use crate::alloc::Box; use crate::error::ErrorImpl; use core::fmt::Display; use core::mem::ManuallyDrop; +use std::backtrace::Backtrace; +use std::any::{Any, TypeId}; + #[cfg(not(feature = "std"))] use core::fmt::Debug; @@ -319,8 +322,38 @@ pub use eyre as format_err; /// # Ok(()) /// } /// ``` -pub struct ErrReport { - inner: ManuallyDrop>>, +pub struct ErrReport +where + C: EyreContext, +{ + inner: ManuallyDrop>>, +} + +pub trait EyreContext: Sized + Send + Sync + 'static { + fn default(err: &(dyn std::error::Error + 'static)) -> Self; + + fn context_raw(&self, typeid: TypeId) -> Option<&dyn Any>; +} + +pub struct DefaultContext { + backtrace: Option, +} + +impl EyreContext for DefaultContext { + fn default(error: &(dyn std::error::Error + 'static)) -> Self { + let backtrace = backtrace_if_absent!(error); + + Self { + backtrace + } + } + + fn context_raw(&self, typid: TypeId) -> Option<&dyn Any> { + match typid { + t if t == TypeId::of::() => self.backtrace.as_ref().map(|b| b as &dyn Any), + _ => None, + } + } } /// Iterator of a chain of source errors. @@ -397,7 +430,7 @@ pub struct Chain<'a> { /// Ok(()) /// } /// ``` -pub type Result = core::result::Result; +pub type Result> = core::result::Result; /// Provides the `context` method for `Result`. /// @@ -542,13 +575,13 @@ pub type Result = core::result::Result; /// ``` pub trait Report: context::private::Sealed { /// Wrap the error value with additional context. - fn context(self, context: C) -> Result + fn context(self, context: C) -> Result> where C: Display + Send + Sync + 'static; /// Wrap the error value with additional context that is evaluated lazily /// only once an error does occur. - fn with_context(self, f: F) -> Result + fn with_context(self, f: F) -> Result> where C: Display + Send + Sync + 'static, F: FnOnce() -> C; @@ -557,11 +590,11 @@ pub trait Report: context::private::Sealed { // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { - use crate::ErrReport; + use crate::{EyreContext, ErrReport}; use core::fmt::{Debug, Display}; - #[cfg(backtrace)] - use std::backtrace::Backtrace; +// #[cfg(backtrace)] +// use std::backtrace::Backtrace; pub use core::result::Result::Err; @@ -573,10 +606,11 @@ pub mod private { pub use crate::kind::BoxedKind; } - pub fn new_adhoc(message: M) -> ErrReport + pub fn new_adhoc(message: M) -> ErrReport where + C: EyreContext, M: Display + Debug + Send + Sync + 'static, { - ErrReport::from_adhoc(message, backtrace!()) + ErrReport::from_adhoc(message) } } diff --git a/tests/test_backtrace.rs b/tests/test_backtrace.rs index 134567a..eedb6a8 100644 --- a/tests/test_backtrace.rs +++ b/tests/test_backtrace.rs @@ -6,8 +6,8 @@ fn test_backtrace() {} #[rustversion::nightly] #[test] fn test_backtrace() { - use eyre::eyre; + use eyre::{eyre, ErrReport}; - let error = eyre!("oh no!"); + let error: ErrReport = eyre!("oh no!"); let _ = error.backtrace(); } diff --git a/tests/test_boxed.rs b/tests/test_boxed.rs index d4284bb..59b99f4 100644 --- a/tests/test_boxed.rs +++ b/tests/test_boxed.rs @@ -1,4 +1,4 @@ -use eyre::eyre; +use eyre::{ErrReport, eyre}; use std::error::Error as StdError; use std::io; use thiserror::Error; @@ -12,7 +12,7 @@ struct MyError { #[test] fn test_boxed_str() { let error = Box::::from("oh no!"); - let error = eyre!(error); + let error: ErrReport = eyre!(error); assert_eq!("oh no!", error.to_string()); assert_eq!( "oh no!", diff --git a/tests/test_convert.rs b/tests/test_convert.rs index 2a6adce..117780c 100644 --- a/tests/test_convert.rs +++ b/tests/test_convert.rs @@ -7,7 +7,7 @@ use std::error::Error as StdError; #[test] fn test_convert() { let has_dropped = Flag::new(); - let error = ErrReport::new(DetectDrop::new(&has_dropped)); + let error: ErrReport = ErrReport::new(DetectDrop::new(&has_dropped)); let box_dyn = Box::::from(error); assert_eq!("oh no!", box_dyn.to_string()); drop(box_dyn); diff --git a/tests/test_downcast.rs b/tests/test_downcast.rs index b1b4a04..51b437c 100644 --- a/tests/test_downcast.rs +++ b/tests/test_downcast.rs @@ -3,7 +3,7 @@ mod drop; use self::common::*; use self::drop::{DetectDrop, Flag}; -use eyre::ErrReport; +use eyre::{DefaultContext, ErrReport}; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io; @@ -71,7 +71,7 @@ fn test_downcast_mut() { #[test] fn test_drop() { let has_dropped = Flag::new(); - let error = ErrReport::new(DetectDrop::new(&has_dropped)); + let error: ErrReport = ErrReport::new(DetectDrop::new(&has_dropped)); drop(error.downcast::().unwrap()); assert!(has_dropped.get()); } @@ -90,7 +90,7 @@ fn test_large_alignment() { impl StdError for LargeAlignedError {} - let error = ErrReport::new(LargeAlignedError("oh no!")); + let error: ErrReport = ErrReport::new(LargeAlignedError("oh no!")); assert_eq!( "oh no!", error.downcast_ref::().unwrap().0 diff --git a/tests/test_fmt.rs b/tests/test_fmt.rs index f2af55d..ee03f0d 100644 --- a/tests/test_fmt.rs +++ b/tests/test_fmt.rs @@ -45,7 +45,7 @@ Custom { const EXPECTED_ALTDEBUG_G: &str = "\ Error { - context: \"f failed\", + msg: \"f failed\", source: Custom { kind: PermissionDenied, error: \"oh no!\", @@ -55,9 +55,9 @@ Error { const EXPECTED_ALTDEBUG_H: &str = "\ Error { - context: \"g failed\", + msg: \"g failed\", source: Error { - context: \"f failed\", + msg: \"f failed\", source: Custom { kind: PermissionDenied, error: \"oh no!\", diff --git a/tests/test_macros.rs b/tests/test_macros.rs index 8e2d5bf..01c60a0 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -1,7 +1,7 @@ mod common; use self::common::*; -use eyre::ensure; +use eyre::{Result, ensure}; #[test] fn test_messages() { @@ -12,20 +12,20 @@ fn test_messages() { #[test] fn test_ensure() { - let f = || { + let f = || -> Result<()> { ensure!(1 + 1 == 2, "This is correct"); Ok(()) }; assert!(f().is_ok()); let v = 1; - let f = || { + let f = || -> Result<()>{ ensure!(v + v == 2, "This is correct, v: {}", v); Ok(()) }; assert!(f().is_ok()); - let f = || { + let f = || -> Result<()>{ ensure!(v + v == 1, "This is not correct, v: {}", v); Ok(()) }; diff --git a/tests/test_repr.rs b/tests/test_repr.rs index f71737f..377ce56 100644 --- a/tests/test_repr.rs +++ b/tests/test_repr.rs @@ -2,6 +2,7 @@ mod drop; use self::drop::{DetectDrop, Flag}; use eyre::ErrReport; +use eyre::DefaultContext; use std::marker::Unpin; use std::mem; @@ -24,6 +25,6 @@ fn test_autotraits() { #[test] fn test_drop() { let has_dropped = Flag::new(); - drop(ErrReport::new(DetectDrop::new(&has_dropped))); + drop(ErrReport::::new(DetectDrop::new(&has_dropped))); assert!(has_dropped.get()); } diff --git a/tests/test_source.rs b/tests/test_source.rs index c4b7d38..a17d697 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,4 +1,4 @@ -use eyre::eyre; +use eyre::{ErrReport, eyre}; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io; @@ -26,37 +26,37 @@ impl StdError for TestError { #[test] fn test_literal_source() { - let error = eyre!("oh no!"); + let error: ErrReport = eyre!("oh no!"); assert!(error.source().is_none()); } #[test] fn test_variable_source() { let msg = "oh no!"; - let error = eyre!(msg); + let error: ErrReport = eyre!(msg); assert!(error.source().is_none()); let msg = msg.to_owned(); - let error = eyre!(msg); + let error: ErrReport = eyre!(msg); assert!(error.source().is_none()); } #[test] fn test_fmt_source() { - let error = eyre!("{} {}!", "oh", "no"); + let error: ErrReport = eyre!("{} {}!", "oh", "no"); assert!(error.source().is_none()); } #[test] fn test_io_source() { let io = io::Error::new(io::ErrorKind::Other, "oh no!"); - let error = eyre!(TestError::Io(io)); + let error: ErrReport = eyre!(TestError::Io(io)); assert_eq!("oh no!", error.source().unwrap().to_string()); } #[test] fn test_eyre_from_eyre() { - let error = eyre!("oh no!").context("context"); + let error: ErrReport = eyre!("oh no!").context("context"); let error = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } From 04f6ed13d25579bde6faedc118feea0357bfdf1c Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 27 Feb 2020 17:15:17 -0800 Subject: [PATCH 04/10] Rename context apis --- README.md | 12 ++++++------ src/context.rs | 28 +++++++++++++-------------- src/error.rs | 6 +++--- src/lib.rs | 44 +++++++++++++++++++++---------------------- tests/test_boxed.rs | 2 +- tests/test_chain.rs | 2 +- tests/test_context.rs | 6 +++--- tests/test_fmt.rs | 4 ++-- tests/test_source.rs | 2 +- 9 files changed, 53 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index fdb6971..3c15b68 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Anyhow ¯\\\_(ツ)\_/¯ +Eyre ¯\\\_(ツ)\_/¯ ========================= [![Build Status](https://api.travis-ci.com/dtolnay/eyre.svg?branch=master)](https://travis-ci.com/dtolnay/eyre) @@ -78,7 +78,7 @@ eyre = "1.0" type does not already provide its own. In order to see backtraces, the `RUST_LIB_BACKTRACE=1` environment variable must be defined. -- Anyhow works with any error type that has an impl of `std::error::Error`, +- 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]. @@ -110,7 +110,7 @@ eyre = "1.0" ## No-std support In no_std mode, the same API is almost all available and works the same way. To -depend on Anyhow in no_std mode, disable our default enabled "std" feature in +depend on Eyre in no_std mode, disable our default enabled "std" feature in Cargo.toml. A global allocator is required. ```toml @@ -120,8 +120,8 @@ eyre = { version = "1.0", default-features = false } Since the `?`-based error conversions would normally rely on the `std::error::Error` trait which is only available through std, no_std mode will -require an explicit `.map_err(Error::msg)` when working with a non-Anyhow error -type inside a function that returns Anyhow's error type. +require an explicit `.map_err(Error::msg)` when working with a non-Eyre error +type inside a function that returns Eyre's error type.
@@ -138,7 +138,7 @@ the necessary improvements for this to be possible as part of [RFC 2504]. ## Comparison to thiserror -Use Anyhow if you don't care what error type your functions return, you just +Use Eyre if you don't care what error type your functions return, you just want it to be easy. This is common in application code. Use [thiserror] if you are a library that wants to design your own dedicated error type(s) so that on failures the caller gets exactly the information that you choose. diff --git a/src/context.rs b/src/context.rs index 9fe64ac..c760e0d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -10,7 +10,7 @@ mod ext { use super::*; pub trait StdError { - fn ext_context(self, context: D) -> ErrReport + fn ext_report(self, msg: D) -> ErrReport where D: Display + Send + Sync + 'static; } @@ -20,20 +20,20 @@ mod ext { where E: std::error::Error + Send + Sync + 'static, { - fn ext_context(self, context: D) -> ErrReport + fn ext_report(self, msg: D) -> ErrReport where D: Display + Send + Sync + 'static, { - ErrReport::from_context(context, self) + ErrReport::from_msg(msg, self) } } impl StdError for ErrReport { - fn ext_context(self, context: D) -> ErrReport + fn ext_report(self, msg: D) -> ErrReport where D: Display + Send + Sync + 'static, { - self.context(context) + self.wrap_err(msg) } } } @@ -42,19 +42,19 @@ impl Report for Result where E: ext::StdError + Send + Sync + 'static, { - fn context(self, context: D) -> Result> + fn wrap_err(self, msg: D) -> Result> where D: Display + Send + Sync + 'static, { - self.map_err(|error| error.ext_context(context)) + self.map_err(|error| error.ext_report(msg)) } - fn with_context(self, context: F) -> Result> + fn wrap_err_with(self, msg: F) -> Result> where D: Display + Send + Sync + 'static, F: FnOnce() -> D, { - self.map_err(|error| error.ext_context(context())) + self.map_err(|error| error.ext_report(msg())) } } @@ -71,7 +71,7 @@ where /// } /// /// fn demo() -> Result<()> { -/// let t = maybe_get().context("there is no T")?; +/// let t = maybe_get().wrap_err("there is no T")?; /// # const IGNORE: &str = stringify! { /// ... /// # }; @@ -79,19 +79,19 @@ where /// } /// ``` impl Report for Option { - fn context(self, context: D) -> Result> + fn wrap_err(self, msg: D) -> Result> where D: Display + Send + Sync + 'static, { - self.ok_or_else(|| ErrReport::from_display(context)) + self.ok_or_else(|| ErrReport::from_display(msg)) } - fn with_context(self, context: F) -> Result> + fn wrap_err_with(self, msg: F) -> Result> where D: Display + Send + Sync + 'static, F: FnOnce() -> D, { - self.ok_or_else(|| ErrReport::from_display(context())) + self.ok_or_else(|| ErrReport::from_display(msg())) } } diff --git a/src/error.rs b/src/error.rs index 1704359..fec9c63 100644 --- a/src/error.rs +++ b/src/error.rs @@ -137,7 +137,7 @@ where } #[cfg(feature = "std")] - pub(crate) fn from_context(msg: D, error: E) -> Self + pub(crate) fn from_msg(msg: D, error: E) -> Self where D: Display + Send + Sync + 'static, E: StdError + Send + Sync + 'static, @@ -258,11 +258,11 @@ where /// "only the first {} lines of {} are valid", /// error.line, path.as_ref().display(), /// ); - /// eyre::ErrReport::new(error).context(context) + /// eyre::ErrReport::new(error).wrap_err(context) /// }) /// } /// ``` - pub fn context(self, msg: D) -> Self + pub fn wrap_err(self, msg: D) -> Self where D: Display + Send + Sync + 'static, { diff --git a/src/lib.rs b/src/lib.rs index a451e03..95c960f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,10 +64,10 @@ //! # let it = It; //! # let path = "./path/to/instrs.json"; //! # -//! it.detach().context("Failed to detach the important thing")?; +//! it.detach().wrap_err("Failed to detach the important thing")?; //! //! let content = std::fs::read(path) -//! .with_context(|| format!("Failed to read instrs from {}", path))?; +//! .wrap_err_with(|| format!("Failed to read instrs from {}", path))?; //! # //! # const _: &str = stringify! { //! ... @@ -124,7 +124,7 @@ //! type does not already provide its own. In order to see backtraces, the //! `RUST_LIB_BACKTRACE=1` environment variable must be defined. //! -//! - Anyhow works with any error type that has an impl of `std::error::Error`, +//! - 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]. @@ -164,7 +164,7 @@ //! # No-std support //! //! In no_std mode, the same API is almost all available and works the same way. -//! To depend on Anyhow in no_std mode, disable our default enabled "std" +//! To depend on Eyre in no_std mode, disable our default enabled "std" //! feature in Cargo.toml. A global allocator is required. //! //! ```toml @@ -175,7 +175,7 @@ //! Since the `?`-based error conversions would normally rely on the //! `std::error::ErrReport` trait which is only available through std, no_std mode //! will require an explicit `.map_err(ErrReport::msg)` when working with a -//! non-Anyhow error type inside a function that returns Anyhow's error type. +//! non-Eyre error type inside a function that returns Eyre's error type. #![doc(html_root_url = "https://docs.rs/eyre/1.0.26")] #![cfg_attr(backtrace, feature(backtrace))] @@ -272,11 +272,11 @@ pub use eyre as format_err; /// No such file or directory (os error 2) /// /// Stack backtrace: -/// 0: ::ext_context +/// 0: ::ext_report /// at /git/eyre/src/backtrace.rs:26 /// 1: core::result::Result::map_err /// at /git/rustc/src/libcore/result.rs:596 -/// 2: eyre::context:: for core::result::Result>::with_context +/// 2: eyre::context:: for core::result::Result>::wrap_err_with /// at /git/eyre/src/context.rs:58 /// 3: testing::main /// at src/main.rs:5 @@ -291,7 +291,7 @@ pub use eyre as format_err; /// /// ```console /// Error { -/// context: "Failed to read instrs from ./path/to/instrs.json", +/// msg: "Failed to read instrs from ./path/to/instrs.json", /// source: Os { /// code: 2, /// kind: NotFound, @@ -432,7 +432,7 @@ pub struct Chain<'a> { /// ``` pub type Result> = core::result::Result; -/// Provides the `context` method for `Result`. +/// Provides the `wrap_err` method for `Result`. /// /// This trait is sealed and cannot be implemented for types outside of /// `eyre`. @@ -460,11 +460,11 @@ pub type Result> = core::result::Result; /// } /// /// pub fn do_it(mut it: ImportantThing) -> Result> { -/// it.detach().context("Failed to detach the important thing")?; +/// it.detach().wrap_err("Failed to detach the important thing")?; /// /// let path = &it.path; /// let content = fs::read(path) -/// .with_context(|| format!("Failed to read instrs from {}", path.display()))?; +/// .wrap_err_with(|| format!("Failed to read instrs from {}", path.display()))?; /// /// Ok(content) /// } @@ -487,7 +487,7 @@ pub type Result> = core::result::Result; /// After attaching context of type `C` onto an error of type `E`, the resulting /// `eyre::Error` may be downcast to `C` **or** to `E`. /// -/// That is, in codebases that rely on downcasting, Anyhow's context supports +/// That is, in codebases that rely on downcasting, Eyre's context supports /// both of the following use cases: /// /// - **Attaching context whose type is insignificant onto errors whose type @@ -495,7 +495,7 @@ pub type Result> = core::result::Result; /// /// In other error libraries whose context is not designed this way, it can /// be risky to introduce context to existing code because new context might -/// break existing working downcasts. In Anyhow, any downcast that worked +/// break existing working downcasts. In Eyre, any downcast that worked /// before adding context will continue to work after you add a context, so /// you should freely add human-readable context to errors wherever it would /// be helpful. @@ -515,7 +515,7 @@ pub type Result> = core::result::Result; /// use eyre::{Report, Result}; /// /// fn do_it() -> Result<()> { -/// helper().context("Failed to complete the work")?; +/// helper().wrap_err("Failed to complete the work")?; /// # const IGNORE: &str = stringify! { /// ... /// # }; @@ -555,7 +555,7 @@ pub type Result> = core::result::Result; /// use eyre::{Report, Result}; /// /// fn do_it() -> Result<()> { -/// helper().context(HelperFailed)?; +/// helper().wrap_err(HelperFailed)?; /// # const IGNORE: &str = stringify! { /// ... /// # }; @@ -574,17 +574,17 @@ pub type Result> = core::result::Result; /// } /// ``` pub trait Report: context::private::Sealed { - /// Wrap the error value with additional context. - fn context(self, context: C) -> Result> + /// Wrap the error value with a new adhoc error + fn wrap_err(self, msg: D) -> Result> where - C: Display + Send + Sync + 'static; + D: Display + Send + Sync + 'static; - /// Wrap the error value with additional context that is evaluated lazily + /// Wrap the error value with a new adhoc error that is evaluated lazily /// only once an error does occur. - fn with_context(self, f: F) -> Result> + fn wrap_err_with(self, f: F) -> Result> where - C: Display + Send + Sync + 'static, - F: FnOnce() -> C; + D: Display + Send + Sync + 'static, + F: FnOnce() -> D; } // Not public API. Referenced by macro-generated code. diff --git a/tests/test_boxed.rs b/tests/test_boxed.rs index 59b99f4..affee5a 100644 --- a/tests/test_boxed.rs +++ b/tests/test_boxed.rs @@ -34,7 +34,7 @@ fn test_boxed_thiserror() { #[test] fn test_boxed_eyre() { - let error = eyre!("oh no!").context("it failed"); + let error = eyre!("oh no!").wrap_err("it failed"); let error = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 7e07fef..bc2215c 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -1,7 +1,7 @@ use eyre::{eyre, ErrReport}; fn error() -> ErrReport { - eyre!(0).context(1).context(2).context(3) + eyre!(0).wrap_err(1).wrap_err(2).wrap_err(3) } #[test] diff --git a/tests/test_context.rs b/tests/test_context.rs index f7849bf..ef6575b 100644 --- a/tests/test_context.rs +++ b/tests/test_context.rs @@ -9,7 +9,7 @@ use thiserror::Error; #[test] fn test_inference() -> Result<()> { let x = "1"; - let y: u32 = x.parse().context("...")?; + let y: u32 = x.parse().wrap_err("...")?; assert_eq!(y, 1); Ok(()) } @@ -70,7 +70,7 @@ fn make_chain() -> (ErrReport, Dropped) { // impl Report for Result let mid = Err::<(), LowLevel>(low) - .context(MidLevel { + .wrap_err(MidLevel { message: "failed to load config", drop: DetectDrop::new(&dropped.mid), }) @@ -78,7 +78,7 @@ fn make_chain() -> (ErrReport, Dropped) { // impl Report for Result let high = Err::<(), ErrReport>(mid) - .context(HighLevel { + .wrap_err(HighLevel { message: "failed to start server", drop: DetectDrop::new(&dropped.high), }) diff --git a/tests/test_fmt.rs b/tests/test_fmt.rs index ee03f0d..e3f1384 100644 --- a/tests/test_fmt.rs +++ b/tests/test_fmt.rs @@ -6,11 +6,11 @@ fn f() -> Result<()> { } fn g() -> Result<()> { - f().context("f failed") + f().wrap_err("f failed") } fn h() -> Result<()> { - g().context("g failed") + g().wrap_err("g failed") } const EXPECTED_ALTDISPLAY_F: &str = "oh no!"; diff --git a/tests/test_source.rs b/tests/test_source.rs index a17d697..6f7044c 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -56,7 +56,7 @@ fn test_io_source() { #[test] fn test_eyre_from_eyre() { - let error: ErrReport = eyre!("oh no!").context("context"); + let error: ErrReport = eyre!("oh no!").wrap_err("context"); let error = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } From 10570f5d725eb12575175dce3a7670b3ce933576 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 27 Feb 2020 17:23:09 -0800 Subject: [PATCH 05/10] Rename more of the API --- README.md | 2 +- src/context.rs | 73 +++++++++++++------------------------------ src/error.rs | 27 ++-------------- src/lib.rs | 26 ++++++++------- tests/test_context.rs | 2 +- tests/test_fmt.rs | 2 +- 6 files changed, 41 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 3c15b68..fc8fae7 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ eyre = "1.0" application was in the middle of. ```rust - use eyre::{Report, Result}; + use eyre::{WrapErr, Result}; fn main() -> Result<()> { ... diff --git a/src/context.rs b/src/context.rs index c760e0d..0d9d2c8 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,6 +1,5 @@ use crate::error::ContextError; -use crate::{Report, DefaultContext, ErrReport, StdError, EyreContext}; -use core::convert::Infallible; +use crate::{WrapErr, ErrReport, StdError, EyreContext}; use core::fmt::{self, Debug, Display, Write}; #[cfg(backtrace)] @@ -9,18 +8,22 @@ use std::backtrace::Backtrace; mod ext { use super::*; - pub trait StdError { - fn ext_report(self, msg: D) -> ErrReport + pub trait StdError + where + C: EyreContext, + { + fn ext_report(self, msg: D) -> ErrReport where D: Display + Send + Sync + 'static; } #[cfg(feature = "std")] - impl StdError for E + impl StdError for E where + C: EyreContext, E: std::error::Error + Send + Sync + 'static, { - fn ext_report(self, msg: D) -> ErrReport + fn ext_report(self, msg: D) -> ErrReport where D: Display + Send + Sync + 'static, { @@ -28,8 +31,11 @@ mod ext { } } - impl StdError for ErrReport { - fn ext_report(self, msg: D) -> ErrReport + impl StdError for ErrReport + where + C: EyreContext, + { + fn ext_report(self, msg: D) -> ErrReport where D: Display + Send + Sync + 'static, { @@ -38,18 +44,19 @@ mod ext { } } -impl Report for Result +impl WrapErr for Result where - E: ext::StdError + Send + Sync + 'static, + C: EyreContext, + E: ext::StdError + Send + Sync + 'static, { - fn wrap_err(self, msg: D) -> Result> + fn wrap_err(self, msg: D) -> Result> where D: Display + Send + Sync + 'static, { self.map_err(|error| error.ext_report(msg)) } - fn wrap_err_with(self, msg: F) -> Result> + fn wrap_err_with(self, msg: F) -> Result> where D: Display + Send + Sync + 'static, F: FnOnce() -> D, @@ -58,43 +65,6 @@ where } } -/// ``` -/// # type T = (); -/// # -/// use eyre::{Report, Result}; -/// -/// fn maybe_get() -> Option { -/// # const IGNORE: &str = stringify! { -/// ... -/// # }; -/// # unimplemented!() -/// } -/// -/// fn demo() -> Result<()> { -/// let t = maybe_get().wrap_err("there is no T")?; -/// # const IGNORE: &str = stringify! { -/// ... -/// # }; -/// # unimplemented!() -/// } -/// ``` -impl Report for Option { - fn wrap_err(self, msg: D) -> Result> - where - D: Display + Send + Sync + 'static, - { - self.ok_or_else(|| ErrReport::from_display(msg)) - } - - fn wrap_err_with(self, msg: F) -> Result> - where - D: Display + Send + Sync + 'static, - F: FnOnce() -> D, - { - self.ok_or_else(|| ErrReport::from_display(msg())) - } -} - impl Debug for ContextError where D: Display, @@ -170,8 +140,7 @@ impl Write for Quoted<&mut fmt::Formatter<'_>> { pub(crate) mod private { use super::*; - pub trait Sealed {} + pub trait Sealed {} - impl Sealed for Result where E: ext::StdError {} - impl Sealed for Option {} + impl Sealed for Result where E: ext::StdError {} } diff --git a/src/error.rs b/src/error.rs index fec9c63..5fda37e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -115,27 +115,6 @@ where unsafe { ErrReport::construct(error, vtable) } } - pub(crate) fn from_display(message: M) -> Self - where - M: Display + Send + Sync + 'static, - { - use crate::wrapper::DisplayError; - let error: DisplayError = DisplayError(message); - let vtable = &ErrorVTable { - object_drop: object_drop::, C>, - object_ref: object_ref::, C>, - #[cfg(feature = "std")] - object_mut: object_mut::, C>, - object_boxed: object_boxed::, C>, - object_downcast: object_downcast::, - object_drop_rest: object_drop_front::, - }; - - // Safety: DisplayError is repr(transparent) so it is okay for the - // vtable to allow casting the DisplayError to M. - unsafe { ErrReport::construct(error, vtable) } - } - #[cfg(feature = "std")] pub(crate) fn from_msg(msg: D, error: E) -> Self where @@ -211,11 +190,11 @@ where /// Wrap the error value with additional context. /// /// For attaching context to a `Result` as it is propagated, the - /// [`Report`][crate::Report] extension trait may be more convenient than + /// [`WrapErr`][crate::WrapErr] extension trait may be more convenient than /// this function. /// /// The primary reason to use `error.context(...)` instead of - /// `result.context(...)` via the `Report` trait would be if the context + /// `result.context(...)` via the `WrapErr` trait would be if the context /// needs to depend on some data held by the underlying error: /// /// ``` @@ -349,7 +328,7 @@ where /// context has been attached. For details about the interaction between /// context and downcasting, [see here]. /// - /// [see here]: trait.Report.html#effect-on-downcasting + /// [see here]: trait.WrapErr.html#effect-on-downcasting pub fn is(&self) -> bool where E: Display + Debug + Send + Sync + 'static, diff --git a/src/lib.rs b/src/lib.rs index 95c960f..4a56dd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ //! # } //! # } //! # -//! use eyre::{Report, Result}; +//! use eyre::{WrapErr, Result}; //! //! fn main() -> Result<()> { //! # return Ok(()); @@ -276,7 +276,7 @@ pub use eyre as format_err; /// at /git/eyre/src/backtrace.rs:26 /// 1: core::result::Result::map_err /// at /git/rustc/src/libcore/result.rs:596 -/// 2: eyre::context:: for core::result::Result>::wrap_err_with +/// 2: eyre::context:: for core::result::Result>::wrap_err_with /// at /git/eyre/src/context.rs:58 /// 3: testing::main /// at src/main.rs:5 @@ -305,7 +305,7 @@ pub use eyre as format_err; /// like this: /// /// ``` -/// use eyre::{Report, Result}; +/// use eyre::{WrapErr, Result}; /// /// fn main() { /// if let Err(err) = try_main() { @@ -383,9 +383,8 @@ pub struct Chain<'a> { /// `Result` /// -/// This is a reasonable return type to use throughout your application but also -/// for `fn main`; if you do, failures will be printed along with any -/// [context][Report] and a backtrace if one was captured. +/// This is a reasonable return type to use throughout your application but also for `fn main`; if +/// you do, failures will be printed along with a backtrace if one was captured. /// /// `eyre::Result` may be used with one *or* two type parameters. /// @@ -442,7 +441,7 @@ pub type Result> = core::result::Result; /// # Example /// /// ``` -/// use eyre::{Report, Result}; +/// use eyre::{WrapErr, Result}; /// use std::fs; /// use std::path::PathBuf; /// @@ -512,7 +511,7 @@ pub type Result> = core::result::Result; /// # bail!(SuspiciousError); /// # } /// # -/// use eyre::{Report, Result}; +/// use eyre::{WrapErr, Result}; /// /// fn do_it() -> Result<()> { /// helper().wrap_err("Failed to complete the work")?; @@ -552,7 +551,7 @@ pub type Result> = core::result::Result; /// # bail!("no such file or directory"); /// # } /// # -/// use eyre::{Report, Result}; +/// use eyre::{WrapErr, Result}; /// /// fn do_it() -> Result<()> { /// helper().wrap_err(HelperFailed)?; @@ -573,15 +572,18 @@ pub type Result> = core::result::Result; /// # panic!("expected downcast to succeed"); /// } /// ``` -pub trait Report: context::private::Sealed { +pub trait WrapErr: context::private::Sealed +where + C: EyreContext, +{ /// Wrap the error value with a new adhoc error - fn wrap_err(self, msg: D) -> Result> + fn wrap_err(self, msg: D) -> Result> where D: Display + Send + Sync + 'static; /// Wrap the error value with a new adhoc error that is evaluated lazily /// only once an error does occur. - fn wrap_err_with(self, f: F) -> Result> + fn wrap_err_with(self, f: F) -> Result> where D: Display + Send + Sync + 'static, F: FnOnce() -> D; diff --git a/tests/test_context.rs b/tests/test_context.rs index ef6575b..ea32dde 100644 --- a/tests/test_context.rs +++ b/tests/test_context.rs @@ -1,7 +1,7 @@ mod drop; use crate::drop::{DetectDrop, Flag}; -use eyre::{Report, ErrReport, Result}; +use eyre::{WrapErr, ErrReport, Result}; use std::fmt::{self, Display}; use thiserror::Error; diff --git a/tests/test_fmt.rs b/tests/test_fmt.rs index e3f1384..7c642e1 100644 --- a/tests/test_fmt.rs +++ b/tests/test_fmt.rs @@ -1,4 +1,4 @@ -use eyre::{bail, Report, Result}; +use eyre::{bail, WrapErr, Result}; use std::io; fn f() -> Result<()> { From 6c1a9af5b8870410751e9f41a135c634cd4f6a93 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Fri, 28 Feb 2020 08:21:08 -0800 Subject: [PATCH 06/10] move fmting to trait and add github actions support --- .github/workflows/ci.yml | 85 ++++++++++++++++++++++++++++++++++++++++ .travis.yml | 22 ----------- README.md | 15 +++---- src/error.rs | 2 +- src/fmt.rs | 61 ++++------------------------ src/lib.rs | 60 +++++++++++++++++++++++++++- 6 files changed, 160 insertions(+), 85 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2852c0d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,85 @@ +on: [push, pull_request] + +name: Continuous integration + +jobs: + check: + name: Check + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - 1.31.0 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + + test: + name: Test Suite + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - nightly + - 1.31.0 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + - uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - 1.31.0 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - 1.31.0 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + - run: rustup component add clippy + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f1b4c7f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: rust - -rust: - - nightly - - beta - - stable - -script: - - cargo test - - cargo check --no-default-features - -matrix: - include: - - rust: 1.34.0 - script: cargo check - - rust: 1.36.0 - script: cargo check --no-default-features - - rust: nightly - name: Clippy - script: - - rustup component add clippy || travis_terminate 0 - - cargo clippy -- -Dclippy::all diff --git a/README.md b/README.md index fc8fae7..906c916 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,11 @@ Eyre ¯\\\_(ツ)\_/¯ [![Latest Version](https://img.shields.io/crates/v/eyre.svg)](https://crates.io/crates/eyre) [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/eyre) -This library provides [`eyre::Error`][Error], a trait object based error type -for easy idiomatic error handling in Rust applications. +This library provides [`eyre::ErrReport`][ErrReport], a trait object based +error handling type for easy idiomatic error handling and reporting in Rust +applications. -[Error]: https://docs.rs/eyre/1.0/eyre/struct.Error.html +[ErrReport]: https://docs.rs/eyre/1.0/eyre/struct.ErrReport.html ```toml [dependencies] @@ -21,7 +22,7 @@ eyre = "1.0" ## Details -- Use `Result`, or equivalently `eyre::Result`, as the +- Use `Result`, or equivalently `eyre::Result`, as the return type of any fallible function. Within the function, use `?` to easily propagate any error that implements the @@ -99,7 +100,7 @@ eyre = "1.0" ``` - One-off error messages can be constructed using the `eyre!` macro, which - supports string interpolation and produces an `eyre::Error`. + supports string interpolation and produces an `eyre::ErrReport`. ```rust return Err(eyre!("Missing attribute: {}", missing)); @@ -120,14 +121,14 @@ eyre = { version = "1.0", default-features = false } Since the `?`-based error conversions would normally rely on the `std::error::Error` trait which is only available through std, no_std mode will -require an explicit `.map_err(Error::msg)` when working with a non-Eyre error +require an explicit `.map_err(ErrReport::msg)` when working with a non-Eyre error type inside a function that returns Eyre's error type.
## Comparison to failure -The `eyre::Error` type works something like `failure::Error`, but unlike +The `eyre::ErrReport` 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]. diff --git a/src/error.rs b/src/error.rs index 5fda37e..fd8762f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -674,7 +674,7 @@ where C: EyreContext, { vtable: &'static ErrorVTable, - context: C, + pub(crate) context: C, // NOTE: Don't use directly. Use only through vtable. Erased type may have // different alignment. _object: E, diff --git a/src/fmt.rs b/src/fmt.rs index 54b4e0a..afc5c82 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,6 +1,5 @@ -use crate::chain::Chain; use crate::error::ErrorImpl; -use core::fmt::{self, Debug, Write}; +use core::fmt::{self, Write}; use crate::EyreContext; impl ErrorImpl<(), C> @@ -8,64 +7,18 @@ where C: EyreContext, { pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.error())?; - - if f.alternate() { - for cause in self.chain().skip(1) { - write!(f, ": {}", cause)?; - } - } - - Ok(()) + self.context.display(self.error(), f) } pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { - let error = self.error(); - - if f.alternate() { - return Debug::fmt(error, f); - } - - write!(f, "{}", error)?; - - if let Some(cause) = error.source() { - write!(f, "\n\nCaused by:")?; - let multiple = cause.source().is_some(); - for (n, error) in Chain::new(cause).enumerate() { - writeln!(f)?; - let mut indented = Indented { - inner: f, - number: if multiple { Some(n) } else { None }, - started: false, - }; - write!(indented, "{}", error)?; - } - } - - #[cfg(backtrace)] - { - use std::backtrace::BacktraceStatus; - - let backtrace = self.backtrace(); - if let BacktraceStatus::Captured = backtrace.status() { - let mut backtrace = backtrace.to_string(); - if backtrace.starts_with("stack backtrace:") { - // Capitalize to match "Caused by:" - backtrace.replace_range(0..1, "S"); - } - backtrace.truncate(backtrace.trim_end().len()); - write!(f, "\n\n{}", backtrace)?; - } - } - - Ok(()) + self.context.debug(self.error(), f) } } -struct Indented<'a, D> { - inner: &'a mut D, - number: Option, - started: bool, +pub(crate) struct Indented<'a, D> { + pub(crate) inner: &'a mut D, + pub(crate) number: Option, + pub(crate) started: bool, } impl Write for Indented<'_, T> diff --git a/src/lib.rs b/src/lib.rs index 4a56dd2..c511cfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -333,10 +333,14 @@ pub trait EyreContext: Sized + Send + Sync + 'static { fn default(err: &(dyn std::error::Error + 'static)) -> Self; fn context_raw(&self, typeid: TypeId) -> Option<&dyn Any>; + + fn display(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result; + + fn debug(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result; } pub struct DefaultContext { - backtrace: Option, + pub backtrace: Option, } impl EyreContext for DefaultContext { @@ -354,6 +358,60 @@ impl EyreContext for DefaultContext { _ => None, } } + + fn display(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", error)?; + + if f.alternate() { + for cause in Chain::new(error).skip(1) { + write!(f, ": {}", cause)?; + } + } + + Ok(()) + } + + fn debug(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use core::fmt::Write as _; + + if f.alternate() { + return core::fmt::Debug::fmt(error, f); + } + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + let multiple = cause.source().is_some(); + for (n, error) in Chain::new(cause).enumerate() { + writeln!(f)?; + let mut indented = fmt::Indented { + inner: f, + number: if multiple { Some(n) } else { None }, + started: false, + }; + write!(indented, "{}", error)?; + } + } + + #[cfg(backtrace)] + { + use std::backtrace::BacktraceStatus; + + let backtrace = self.backtrace.as_ref().or_else(|| error.backtrace()).expect("backtrace capture failed"); + if let BacktraceStatus::Captured = backtrace.status() { + let mut backtrace = backtrace.to_string(); + if backtrace.starts_with("stack backtrace:") { + // Capitalize to match "Caused by:" + backtrace.replace_range(0..1, "S"); + } + backtrace.truncate(backtrace.trim_end().len()); + write!(f, "\n\n{}", backtrace)?; + } + } + + Ok(()) + } } /// Iterator of a chain of source errors. From 11757c1da5f74d23f47344754769936d2caa89a2 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Fri, 28 Feb 2020 08:54:39 -0800 Subject: [PATCH 07/10] make an effort at no_std support --- .github/workflows/ci.yml | 16 +++++----- src/backtrace.rs | 16 +--------- src/context.rs | 2 +- src/error.rs | 39 ++++++++++++------------ src/fmt.rs | 3 +- src/kind.rs | 2 +- src/lib.rs | 65 +++++++++++++++++++++++++++------------- tests/test_boxed.rs | 2 +- tests/test_context.rs | 2 +- tests/test_fmt.rs | 2 +- tests/test_macros.rs | 6 ++-- tests/test_repr.rs | 11 +++++-- tests/test_source.rs | 2 +- 13 files changed, 92 insertions(+), 76 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2852c0d..2a991f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: matrix: rust: - stable - - 1.31.0 + - 1.34.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 @@ -29,7 +29,7 @@ jobs: rust: - stable - nightly - - 1.31.0 + - 1.34.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 @@ -39,10 +39,10 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features + # - uses: actions-rs/cargo@v1 + # with: + # command: test + # args: --no-default-features fmt: name: Rustfmt @@ -51,7 +51,7 @@ jobs: matrix: rust: - stable - - 1.31.0 + - 1.34.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 @@ -71,7 +71,7 @@ jobs: matrix: rust: - stable - - 1.31.0 + - 1.34.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 diff --git a/src/backtrace.rs b/src/backtrace.rs index 462f5ca..6c00d7f 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -4,20 +4,6 @@ pub(crate) use std::backtrace::Backtrace; #[cfg(not(backtrace))] pub(crate) enum Backtrace {} -// #[cfg(backtrace)] -// macro_rules! backtrace { -// () => { -// Some(Backtrace::capture()) -// }; -// } - -#[cfg(not(backtrace))] -macro_rules! backtrace { - () => { - None - }; -} - #[cfg(backtrace)] macro_rules! backtrace_if_absent { ($err:expr) => { @@ -28,7 +14,7 @@ macro_rules! backtrace_if_absent { }; } -#[cfg(all(feature = "std", not(backtrace)))] +#[cfg(not(backtrace))] macro_rules! backtrace_if_absent { ($err:expr) => { None diff --git a/src/context.rs b/src/context.rs index 0d9d2c8..528bd71 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,5 +1,5 @@ use crate::error::ContextError; -use crate::{WrapErr, ErrReport, StdError, EyreContext}; +use crate::{ErrReport, EyreContext, StdError, WrapErr}; use core::fmt::{self, Debug, Display, Write}; #[cfg(backtrace)] diff --git a/src/error.rs b/src/error.rs index fd8762f..50ff665 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,15 @@ use crate::alloc::Box; -use std::any::Any; -use crate::backtrace::Backtrace; use crate::chain::Chain; +use crate::EyreContext; use crate::{ErrReport, StdError}; +use core::any::Any; use core::any::TypeId; use core::fmt::{self, Debug, Display}; use core::mem::{self, ManuallyDrop}; use core::ptr::{self, NonNull}; -use crate::EyreContext; + +#[cfg(backtrace)] +use crate::backtrace::Backtrace; #[cfg(feature = "std")] use core::ops::{Deref, DerefMut}; @@ -138,9 +140,7 @@ where } #[cfg(feature = "std")] - pub(crate) fn from_boxed( - error: Box, - ) -> Self { + pub(crate) fn from_boxed(error: Box) -> Self { use crate::wrapper::BoxedError; let error = BoxedError(error); let vtable = &ErrorVTable { @@ -163,10 +163,7 @@ where // // Unsafe because the given vtable must have sensible behavior on the error // value of type E. - unsafe fn construct( - error: E, - vtable: &'static ErrorVTable, - ) -> Self + unsafe fn construct(error: E, vtable: &'static ErrorVTable) -> Self where E: StdError + Send + Sync + 'static, { @@ -245,10 +242,7 @@ where where D: Display + Send + Sync + 'static, { - let error: ContextError> = ContextError { - msg, - error: self, - }; + let error: ContextError> = ContextError { msg, error: self }; let vtable = &ErrorVTable { object_drop: object_drop::>, C>, @@ -431,6 +425,10 @@ where Some(&mut *addr.cast::().as_ptr()) } } + + pub fn context(&self) -> Option<&T> { + self.inner.context() + } } #[cfg(feature = "std")] @@ -490,7 +488,7 @@ where struct ErrorVTable where - C: EyreContext, + C: EyreContext, { object_drop: unsafe fn(Box>), object_ref: unsafe fn(&ErrorImpl<(), C>) -> &(dyn StdError + Send + Sync + 'static), @@ -627,12 +625,14 @@ where D: 'static, { if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl>, C>; + let unerased = + e as *const ErrorImpl<(), C> as *const ErrorImpl>, C>; let addr = &(*unerased)._object.msg as *const D as *mut (); Some(NonNull::new_unchecked(addr)) } else { // Recurse down the context chain per the inner error's vtable. - let unerased = e as *const ErrorImpl<(), C> as *const ErrorImpl>, C>; + let unerased = + e as *const ErrorImpl<(), C> as *const ErrorImpl>, C>; let source = &(*unerased)._object.error; (source.inner.vtable.object_downcast)(&source.inner, target) } @@ -717,9 +717,10 @@ where unsafe { &mut *(self.vtable.object_mut)(self) } } - pub fn context(&self) -> Option<&T> { - self.context.context_raw(TypeId::of::())?.downcast_ref::() + self.context + .context_raw(TypeId::of::())? + .downcast_ref::() } #[cfg(backtrace)] diff --git a/src/fmt.rs b/src/fmt.rs index afc5c82..31e17ea 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,6 +1,6 @@ use crate::error::ErrorImpl; -use core::fmt::{self, Write}; use crate::EyreContext; +use core::fmt::{self, Write}; impl ErrorImpl<(), C> where @@ -52,6 +52,7 @@ where #[cfg(test)] mod tests { use super::*; + use crate::alloc::String; #[test] fn one_digit() { diff --git a/src/kind.rs b/src/kind.rs index eebf8ae..77a0ad4 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -44,7 +44,7 @@ // let error = $msg; // (&error).eyre_kind().new(error) -use crate::{EyreContext, ErrReport}; +use crate::{ErrReport, EyreContext}; use core::fmt::{Debug, Display}; #[cfg(feature = "std")] diff --git a/src/lib.rs b/src/lib.rs index c511cfe..b510145 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,6 +195,12 @@ mod alloc { #[cfg(feature = "std")] pub use std::boxed::Box; + + #[cfg(not(feature = "std"))] + pub use alloc::string::String; + + #[cfg(feature = "std")] + pub use std::string::String; } #[macro_use] @@ -208,12 +214,11 @@ mod macros; mod wrapper; use crate::alloc::Box; +use crate::backtrace::Backtrace; use crate::error::ErrorImpl; +use core::any::{Any, TypeId}; use core::fmt::Display; use core::mem::ManuallyDrop; -use std::backtrace::Backtrace; -use std::any::{Any, TypeId}; - #[cfg(not(feature = "std"))] use core::fmt::Debug; @@ -222,7 +227,7 @@ use core::fmt::Debug; use std::error::Error as StdError; #[cfg(not(feature = "std"))] -trait StdError: Debug + Display { +pub trait StdError: Debug + Display { fn source(&self) -> Option<&(dyn StdError + 'static)> { None } @@ -330,26 +335,32 @@ where } pub trait EyreContext: Sized + Send + Sync + 'static { - fn default(err: &(dyn std::error::Error + 'static)) -> Self; + fn default(err: &(dyn StdError + 'static)) -> Self; fn context_raw(&self, typeid: TypeId) -> Option<&dyn Any>; - fn display(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result; - - fn debug(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result; + fn display( + &self, + error: &(dyn StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result; + + fn debug( + &self, + error: &(dyn StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result; } pub struct DefaultContext { - pub backtrace: Option, + backtrace: Option, } impl EyreContext for DefaultContext { - fn default(error: &(dyn std::error::Error + 'static)) -> Self { + fn default(_error: &(dyn StdError + 'static)) -> Self { let backtrace = backtrace_if_absent!(error); - Self { - backtrace - } + Self { backtrace } } fn context_raw(&self, typid: TypeId) -> Option<&dyn Any> { @@ -359,11 +370,15 @@ impl EyreContext for DefaultContext { } } - fn display(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + fn display( + &self, + error: &(dyn StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { write!(f, "{}", error)?; if f.alternate() { - for cause in Chain::new(error).skip(1) { + for cause in crate::chain::Chain::new(error).skip(1) { write!(f, ": {}", cause)?; } } @@ -371,7 +386,11 @@ impl EyreContext for DefaultContext { Ok(()) } - fn debug(&self, error: &(dyn std::error::Error + 'static), f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + fn debug( + &self, + error: &(dyn StdError + 'static), + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { use core::fmt::Write as _; if f.alternate() { @@ -383,7 +402,7 @@ impl EyreContext for DefaultContext { if let Some(cause) = error.source() { write!(f, "\n\nCaused by:")?; let multiple = cause.source().is_some(); - for (n, error) in Chain::new(cause).enumerate() { + for (n, error) in crate::chain::Chain::new(cause).enumerate() { writeln!(f)?; let mut indented = fmt::Indented { inner: f, @@ -398,7 +417,11 @@ impl EyreContext for DefaultContext { { use std::backtrace::BacktraceStatus; - let backtrace = self.backtrace.as_ref().or_else(|| error.backtrace()).expect("backtrace capture failed"); + let backtrace = self + .backtrace + .as_ref() + .or_else(|| error.backtrace()) + .expect("backtrace capture failed"); if let BacktraceStatus::Captured = backtrace.status() { let mut backtrace = backtrace.to_string(); if backtrace.starts_with("stack backtrace:") { @@ -650,11 +673,11 @@ where // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { - use crate::{EyreContext, ErrReport}; + use crate::{ErrReport, EyreContext}; use core::fmt::{Debug, Display}; -// #[cfg(backtrace)] -// use std::backtrace::Backtrace; + // #[cfg(backtrace)] + // use std::backtrace::Backtrace; pub use core::result::Result::Err; diff --git a/tests/test_boxed.rs b/tests/test_boxed.rs index affee5a..42f8dbe 100644 --- a/tests/test_boxed.rs +++ b/tests/test_boxed.rs @@ -1,4 +1,4 @@ -use eyre::{ErrReport, eyre}; +use eyre::{eyre, ErrReport}; use std::error::Error as StdError; use std::io; use thiserror::Error; diff --git a/tests/test_context.rs b/tests/test_context.rs index ea32dde..e7471ab 100644 --- a/tests/test_context.rs +++ b/tests/test_context.rs @@ -1,7 +1,7 @@ mod drop; use crate::drop::{DetectDrop, Flag}; -use eyre::{WrapErr, ErrReport, Result}; +use eyre::{ErrReport, Result, WrapErr}; use std::fmt::{self, Display}; use thiserror::Error; diff --git a/tests/test_fmt.rs b/tests/test_fmt.rs index 7c642e1..f57f3b1 100644 --- a/tests/test_fmt.rs +++ b/tests/test_fmt.rs @@ -1,4 +1,4 @@ -use eyre::{bail, WrapErr, Result}; +use eyre::{bail, Result, WrapErr}; use std::io; fn f() -> Result<()> { diff --git a/tests/test_macros.rs b/tests/test_macros.rs index 01c60a0..5ba554a 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -1,7 +1,7 @@ mod common; use self::common::*; -use eyre::{Result, ensure}; +use eyre::{ensure, Result}; #[test] fn test_messages() { @@ -19,13 +19,13 @@ fn test_ensure() { assert!(f().is_ok()); let v = 1; - let f = || -> Result<()>{ + let f = || -> Result<()> { ensure!(v + v == 2, "This is correct, v: {}", v); Ok(()) }; assert!(f().is_ok()); - let f = || -> Result<()>{ + let f = || -> Result<()> { ensure!(v + v == 1, "This is not correct, v: {}", v); Ok(()) }; diff --git a/tests/test_repr.rs b/tests/test_repr.rs index 377ce56..d95462e 100644 --- a/tests/test_repr.rs +++ b/tests/test_repr.rs @@ -1,8 +1,8 @@ mod drop; use self::drop::{DetectDrop, Flag}; -use eyre::ErrReport; use eyre::DefaultContext; +use eyre::ErrReport; use std::marker::Unpin; use std::mem; @@ -13,7 +13,10 @@ fn test_error_size() { #[test] fn test_null_pointer_optimization() { - assert_eq!(mem::size_of::>(), mem::size_of::()); + assert_eq!( + mem::size_of::>(), + mem::size_of::() + ); } #[test] @@ -25,6 +28,8 @@ fn test_autotraits() { #[test] fn test_drop() { let has_dropped = Flag::new(); - drop(ErrReport::::new(DetectDrop::new(&has_dropped))); + drop(ErrReport::::new(DetectDrop::new( + &has_dropped, + ))); assert!(has_dropped.get()); } diff --git a/tests/test_source.rs b/tests/test_source.rs index 6f7044c..d8630a7 100644 --- a/tests/test_source.rs +++ b/tests/test_source.rs @@ -1,4 +1,4 @@ -use eyre::{ErrReport, eyre}; +use eyre::{eyre, ErrReport}; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io; From 4781fcd78cec86a81e5a092716dd1826bd59cffc Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Fri, 28 Feb 2020 08:57:56 -0800 Subject: [PATCH 08/10] fix nightly --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b510145..fe18c5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -357,7 +357,8 @@ pub struct DefaultContext { } impl EyreContext for DefaultContext { - fn default(_error: &(dyn StdError + 'static)) -> Self { + #[allow(unused_variables)] + fn default(error: &(dyn StdError + 'static)) -> Self { let backtrace = backtrace_if_absent!(error); Self { backtrace } From 82bf2a66e6c38f691390a5d2ed9614ede2d032fe Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Fri, 28 Feb 2020 08:59:55 -0800 Subject: [PATCH 09/10] fix clippy warning --- src/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/error.rs b/src/error.rs index 50ff665..ba13e80 100644 --- a/src/error.rs +++ b/src/error.rs @@ -494,6 +494,7 @@ where object_ref: unsafe fn(&ErrorImpl<(), C>) -> &(dyn StdError + Send + Sync + 'static), #[cfg(feature = "std")] object_mut: unsafe fn(&mut ErrorImpl<(), C>) -> &mut (dyn StdError + Send + Sync + 'static), + #[allow(clippy::type_complexity)] object_boxed: unsafe fn(Box>) -> Box, object_downcast: unsafe fn(&ErrorImpl<(), C>, TypeId) -> Option>, object_drop_rest: unsafe fn(Box>, TypeId), From 3b26dacea8aaac04d2e8944406aa0616fe880f00 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Fri, 28 Feb 2020 09:37:41 -0800 Subject: [PATCH 10/10] Update docs a little --- Cargo.toml | 11 ++--- README.md | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 13 +++--- 3 files changed, 141 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c0099e..8e1e5a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,15 @@ [package] name = "eyre" -version = "1.0.26" # remember to update html_root_url -authors = ["David Tolnay "] +version = "0.2.0" # remember to update html_root_url +authors = ["David Tolnay ", "Jane Lusby "] edition = "2018" license = "MIT OR Apache-2.0" -description = "Flexible concrete Error type built on std::error::Error" -repository = "https://github.com/dtolnay/eyre" +description = "Flexible concrete Error Handling and Reporting type built on std::error::Error" +repository = "https://github.com/yaahc/eyre" documentation = "https://docs.rs/eyre" readme = "README.md" categories = ["rust-patterns"] -[badges] -travis-ci = { repository = "dtolnay/eyre" } - [features] default = ["std"] std = [] diff --git a/README.md b/README.md index 906c916..8ef5319 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Eyre ¯\\\_(ツ)\_/¯ ========================= -[![Build Status](https://api.travis-ci.com/dtolnay/eyre.svg?branch=master)](https://travis-ci.com/dtolnay/eyre) +[![Build Status][actions-badge]][actions-url] [![Latest Version](https://img.shields.io/crates/v/eyre.svg)](https://crates.io/crates/eyre) [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/eyre) @@ -9,11 +9,134 @@ This library provides [`eyre::ErrReport`][ErrReport], a trait object based error handling type for easy idiomatic error handling and reporting in Rust applications. +First and foremost, this crate is a fork of `anyhow` by @dtolnay. My goal in +writing this crate is to explore new directions of handling context and to +explore new ways to communicate the intended usage of this crate via changes to +the API. + +The main changes this crate brings to anyhow are + +* Addition of the [`eyre::EyreContext`] trait and a type parameter on the core error + handling type which users can use to insert custom forms of context into + their general error type. +* Rebranding the type as principally for error reporting, rather than + describing it as an error type in its own right. This type is not an error, + it contains errors that it masqerades as, and provides helpers for creating + new errors to wrap those errors and for displaying those chains of errors, + and the included context, to the end user. The goal is to make it obvious + that this type is meant to be used when the only way you expect to handle + errors is to print them. +* Changing the [`anyhow::Context`] trait to [`eyre::WrapErr`] to make it clear + that it is unrelated to the [`eyre::EyreContext`] and the context member, and + is only for inserting new errors into the chain of errors. +* Addition of a new `context` function on [`eyre::ErrReport`] to assist with + extracting members from the inner Context, which is used by + [`eyre::ErrReport`] to extract [`std::backtrace::Backtrace`]'s from generic + contexts types. + +These changes were made in order to facilitate the usage of +[`tracing::SpanTrace`] with anyhow, which is a Backtrace-like type for +rendering custom defined runtime context. + +**Note**: The way the `eyre!` macro works in practice differs from how +`anyhow!` works due to the addition of the generic type parameter. In anyhow +the following is valid. + +```rust +let val = get_optional_val.ok_or_else(|| anyhow!("failed to get value)).unwrap(); +``` + +Where as with `eyre!` this will fail due to being unable to infer the type for +the Context parameter. The solution to this problem, should you encounter it, +is to give the compiler a hint for what type it should be resolving to, either +via your return type or a type annotation. + +```rust +// Will work fine +let val: ErrReport = get_optional_val.ok_or_else(|| eyre!("failed to get value)).unwrap(); +``` + [ErrReport]: https://docs.rs/eyre/1.0/eyre/struct.ErrReport.html +[actions-badge]: https://github.com/yaahc/eyre/workflows/ci/badge.svg +[actions-url]:https://github.com/yaahc/eyre/actions?query=workflow%3Aci + +## Customization + +In order to insert your own custom context type you must first implement the +`eyre::EyreContext` trait for said type, which has four required methods. + +* `fn default(error: &Error) -> Self` - For constructing default context while +allowing special case handling depending on the content of the error you're +wrapping. + +This is essentially `Default::default` but more flexible, for example, the +`eyre::DefaultContext` type provide by this crate uses this to only capture a +`Backtrace` if the inner `Error` does not already have one. + +```rust +fn default(error: &(dyn StdError + 'static)) -> Self { + let backtrace = backtrace_if_absent!(error); + + Self { backtrace } +} +``` + +* `fn context_raw(&self, typeid TypeID) -> Option<&dyn Any>` - For extracting + arbitrary members from a context based on their type. + +This method is like a flexible version of the `fn backtrace(&self)` method on +the `Error` trait. In the future we will likely support extracting `Backtrace`s +and `SpanTrace`s by default by relying on the implementation of `context_raw` +provided by the user. + + +Here is how the `eyre::DefaultContext` type uses this to return `Backtrace`s. + +```rust +fn context_raw(&self, typeid: TypeId) -> Option<&dyn Any> { + if typeid == TypeId::of::() { + self.backtrace.as_ref().map(|b| b as &dyn Any) + } else { + None + } +} +``` + +* `fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt Result` + it's companion `display` version. - For formatting the entire error chain and + the user provided context. + +When overriding the context it no longer makes sense for `eyre::ErrReport` to +provide the `Display` and `Debug` implementations for the user, becase we +cannot predict what forms of context you will need to display along side your +chain of errors. Instead we forward the implementations of `Display` and +`Debug` to these methods on the inner `EyreContext` type. + +This crate does provide a few helpers to assist in writing display +implementations, specifically the `Chain` type, for treating an error and its +sources like an iterator, and the `Indented` type, for indenting multi line +errors consistently without using heap allocations. + +**Note**: best practices for printing errors suggest that `{}` should only +print the current error and none of its sources, and that the primary method of +displaying an error, its sources, and its context should be handled by the +`Debug` implementation, which is what is used to print errors that are returned +from `main`. For examples on how to implement this please refer to the +implementations of `display` and `debug` on `eyre::DefaultContext` + +Once you've defined a custom Context type you can use it throughout your +application by defining a type alias. + +```rust +type ErrReport = eyre::ErrReport; + +// And optionally... +type Result> = core::result::Result; +``` ```toml [dependencies] -eyre = "1.0" +eyre = "0.2" ``` *Compiler support: requires rustc 1.34+* @@ -110,13 +233,17 @@ eyre = "1.0" ## No-std support +**NOTE**: tests are currently broken for `no_std` so I cannot guaruntee that +everything works still. I'm waiting for upstream fixes to be merged rather than +fixing them myself, so bear with me. + In no_std mode, the same API is almost all available and works the same way. To depend on Eyre in no_std mode, disable our default enabled "std" feature in Cargo.toml. A global allocator is required. ```toml [dependencies] -eyre = { version = "1.0", default-features = false } +eyre = { version = "0.2", default-features = false } ``` Since the `?`-based error conversions would normally rely on the diff --git a/src/lib.rs b/src/lib.rs index fe18c5c..7d31278 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,7 +169,7 @@ //! //! ```toml //! [dependencies] -//! eyre = { version = "1.0", default-features = false } +//! eyre = { version = "0.2", default-features = false } //! ``` //! //! Since the `?`-based error conversions would normally rely on the @@ -177,7 +177,7 @@ //! will require an explicit `.map_err(ErrReport::msg)` when working with a //! non-Eyre error type inside a function that returns Eyre's error type. -#![doc(html_root_url = "https://docs.rs/eyre/1.0.26")] +#![doc(html_root_url = "https://docs.rs/eyre/0.2.0")] #![cfg_attr(backtrace, feature(backtrace))] #![cfg_attr(not(feature = "std"), no_std)] #![allow( @@ -364,10 +364,11 @@ impl EyreContext for DefaultContext { Self { backtrace } } - fn context_raw(&self, typid: TypeId) -> Option<&dyn Any> { - match typid { - t if t == TypeId::of::() => self.backtrace.as_ref().map(|b| b as &dyn Any), - _ => None, + fn context_raw(&self, typeid: TypeId) -> Option<&dyn Any> { + if typeid == TypeId::of::() { + self.backtrace.as_ref().map(|b| b as &dyn Any) + } else { + None } }