diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2f81b29..3fc2545 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: version: - - 1.46.0 + - 1.54.0 - stable - beta - nightly @@ -70,16 +70,3 @@ jobs: with: command: clippy args: -- -D warnings - - readme: - name: cargo readme - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - profile: minimal - override: true - - run: cargo install cargo-readme - - run: cargo readme > README.md && git diff --exit-code diff --git a/Cargo.toml b/Cargo.toml index 6c4e589..dae493e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,3 @@ github = { repository = "haraldh/chainerror", workflow = "Rust" } maintenance = { status = "actively-developed" } is-it-maintained-issue-resolution = { repository = "haraldh/chainerror" } is-it-maintained-open-issues = { repository = "haraldh/chainerror" } - -[features] -default = [] -display-cause = [] diff --git a/README.md b/README.md index 2441848..7a7b3a9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ [![Crate](https://img.shields.io/crates/v/chainerror.svg)](https://crates.io/crates/chainerror) [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/chainerror/) -[![Coverage Status](https://coveralls.io/repos/github/haraldh/chainerror/badge.svg?branch=master)](https://coveralls.io/github/haraldh/chainerror?branch=master) -[![Workflow Status](https://github.com/haraldh/chainerror/workflows/Rust/badge.svg)](https://github.com/haraldh/chainerror/actions?query=workflow%3A%22Rust%22) -[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/haraldh/chainerror.svg)](https://isitmaintained.com/project/haraldh/chainerror "Average time to resolve an issue") -[![Percentage of issues still open](https://isitmaintained.com/badge/open/haraldh/chainerror.svg)](https://isitmaintained.com/project/haraldh/chainerror "Percentage of issues still open") +[![Coverage Status](https://codecov.io/gh/haraldh/chainerror/branch/master/graph/badge.svg?token=HGLJFGA11B)](https://codecov.io/gh/haraldh/chainerror) ![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg) # chainerror @@ -93,11 +90,6 @@ Along with the `ChainError` struct, `chainerror` comes with some useful helpe Debug information is worth it! -### Features - -`display-cause` -: turn on printing a backtrace of the errors in `Display` - ## Tutorial Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) diff --git a/examples/example.rs b/examples/example.rs index 370631e..859a12c 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -51,6 +51,10 @@ fn func1() -> ChainResult<(), Func1Error> { fn main() { if let Err(e) = func1() { + eprintln!("\nDisplay Error {{}}:\n{}", e); + + eprintln!("\nAlternative Display Error {{:#}}:\n{:#}", e); + eprintln!("\nDebug Error {{:?}}:\n{:?}", e); eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", e); diff --git a/examples/tutorial1.rs b/examples/tutorial1.rs index 4ac1eb3..939fb73 100644 --- a/examples/tutorial1.rs +++ b/examples/tutorial1.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match)] +#![allow(clippy::redundant_pattern_matching)] + use std::error::Error; use std::io; use std::result::Result; diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 19bc713..35dc832 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match)] +#![allow(clippy::redundant_pattern_matching)] + pub mod mycrate { use chainerror::prelude::v1::*; use std::io; diff --git a/examples/tutorial14.rs b/examples/tutorial14.rs index 9c624dc..5ec216e 100644 --- a/examples/tutorial14.rs +++ b/examples/tutorial14.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match)] +#![allow(clippy::redundant_pattern_matching)] + pub mod mycrate { use std::error::Error as StdError; diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs index 3b0eef3..39594b4 100644 --- a/examples/tutorial15.rs +++ b/examples/tutorial15.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match)] +#![allow(clippy::redundant_pattern_matching)] + pub mod mycrate { use chainerror::prelude::v1::*; use std::io; diff --git a/examples/tutorial6.rs b/examples/tutorial6.rs index 9310c8c..de1a0a8 100644 --- a/examples/tutorial6.rs +++ b/examples/tutorial6.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match)] +#![allow(clippy::redundant_pattern_matching)] + use chainerror::prelude::v1::*; use std::error::Error; use std::io; diff --git a/examples/tutorial7.rs b/examples/tutorial7.rs index 969588d..9a1ca67 100644 --- a/examples/tutorial7.rs +++ b/examples/tutorial7.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match)] +#![allow(clippy::redundant_pattern_matching)] + use chainerror::prelude::v1::*; use std::error::Error; use std::io; diff --git a/src/lib.rs b/src/lib.rs index 2c58ec4..50dbd51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,113 +1,11 @@ -//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your -//! binaries, you still have the error backtrace. -//! -//! Having nested function returning errors, the output doesn't tell where the error originates from. -//! -//! ```rust -//! use std::path::PathBuf; -//! -//! type BoxedError = Box; -//! fn read_config_file(path: PathBuf) -> Result<(), BoxedError> { -//! // do stuff, return other errors -//! let _buf = std::fs::read_to_string(&path)?; -//! // do stuff, return other errors -//! Ok(()) -//! } -//! -//! fn process_config_file() -> Result<(), BoxedError> { -//! // do stuff, return other errors -//! let _buf = read_config_file("foo.txt".into())?; -//! // do stuff, return other errors -//! Ok(()) -//! } -//! -//! fn main() { -//! if let Err(e) = process_config_file() { -//! eprintln!("Error:\n{:?}", e); -//! } -//! } -//! ``` -//! -//! This gives the output: -//! ```console -//! Error: -//! Os { code: 2, kind: NotFound, message: "No such file or directory" } -//! ``` -//! and you have no idea where it comes from. -//! -//! -//! With `chainerror`, you can supply a context and get a nice error backtrace: -//! -//! ```rust -//! use chainerror::prelude::v1::*; -//! use std::path::PathBuf; -//! -//! type BoxedError = Box; -//! fn read_config_file(path: PathBuf) -> Result<(), BoxedError> { -//! // do stuff, return other errors -//! let _buf = std::fs::read_to_string(&path).context(format!("Reading file: {:?}", &path))?; -//! // do stuff, return other errors -//! Ok(()) -//! } -//! -//! fn process_config_file() -> Result<(), BoxedError> { -//! // do stuff, return other errors -//! let _buf = read_config_file("foo.txt".into()).context("read the config file")?; -//! // do stuff, return other errors -//! Ok(()) -//! } -//! -//! fn main() { -//! if let Err(e) = process_config_file() { -//! eprintln!("Error:\n{:?}", e); -//! # let s = format!("{:?}", e); -//! # let lines = s.lines().collect::>(); -//! # assert_eq!(lines.len(), 5); -//! # assert!(lines[0].starts_with("src/lib.rs:")); -//! # assert_eq!(lines[1], "Caused by:"); -//! # assert!(lines[2].starts_with("src/lib.rs:")); -//! # assert_eq!(lines[3], "Caused by:"); -//! # assert_eq!(lines[4], "Os { code: 2, kind: NotFound, message: \"No such file or directory\" }"); -//! } -//! # else { panic!(); } -//! } -//! ``` -//! -//! with the output: -//! ```console -//! Error: -//! examples/simple.rs:14:51: read the config file -//! Caused by: -//! examples/simple.rs:7:47: Reading file: "foo.txt" -//! Caused by: -//! Os { code: 2, kind: NotFound, message: "No such file or directory" } -//! ``` -//! -//! `chainerror` uses `.source()` of `std::error::Error` along with `#[track_caller]` and `Location` to provide a nice debug error backtrace. -//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally. -//! -//! Along with the `ChainError` struct, `chainerror` comes with some useful helper macros to save a lot of typing. -//! -//! `chainerror` has no dependencies! -//! -//! Debug information is worth it! -//! -//! ## Features -//! -//! `display-cause` -//! : turn on printing a backtrace of the errors in `Display` -//! -//! # Tutorial -//! -//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) - +#![doc = include_str!("../README.md")] #![deny(clippy::all)] #![allow(clippy::needless_doctest_main)] #![deny(missing_docs)] use std::any::TypeId; -use std::error::Error; -use std::fmt::{Debug, Display, Formatter, Result}; +use std::error::Error as StdError; +use std::fmt::{Debug, Display, Formatter}; use std::panic::Location; pub mod prelude { @@ -115,28 +13,29 @@ pub mod prelude { pub mod v1 { //! convenience prelude pub use super::super::ChainErrorDown as _; + pub use super::super::Error as ChainError; + pub use super::super::Result as ChainResult; pub use super::super::ResultTrait as _; - pub use super::super::{ChainError, ChainResult}; pub use crate::{derive_err_kind, derive_str_context}; } } /// chains an inner error kind `T` with a causing error -pub struct ChainError { +pub struct Error { occurrence: Option, kind: T, - error_cause: Option>, + error_cause: Option>, } /// convenience type alias -pub type ChainResult = std::result::Result>; +pub type Result = std::result::Result>; -impl ChainError { +impl Error { /// Use the `context()` or `map_context()` Result methods instead of calling this directly #[inline] pub fn new( kind: T, - error_cause: Option>, + error_cause: Option>, occurrence: Option, ) -> Self { Self { @@ -147,7 +46,7 @@ impl ChainError { } /// return the root cause of the error chain, if any exists - pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { + pub fn root_cause(&self) -> Option<&(dyn StdError + 'static)> { self.iter().last() } @@ -195,9 +94,9 @@ impl ChainError { /// # } /// ``` #[inline] - pub fn find_cause(&self) -> Option<&U> { + pub fn find_cause(&self) -> Option<&U> { self.iter() - .filter_map(::downcast_ref::) + .filter_map(::downcast_ref::) .next() } @@ -218,9 +117,9 @@ impl ChainError { /// err.find_chain_cause::(); /// ``` #[inline] - pub fn find_chain_cause(&self) -> Option<&ChainError> { + pub fn find_chain_cause(&self) -> Option<&Error> { self.iter() - .filter_map(::downcast_ref::>) + .filter_map(::downcast_ref::>) .next() } @@ -245,10 +144,10 @@ impl ChainError { /// err.find_kind_or_cause::(); /// ``` #[inline] - pub fn find_kind_or_cause(&self) -> Option<&U> { + pub fn find_kind_or_cause(&self) -> Option<&U> { self.iter() .filter_map(|e| { - e.downcast_ref::>() + e.downcast_ref::>() .map(|e| e.kind()) .or_else(|| e.downcast_ref::()) }) @@ -318,7 +217,7 @@ impl ChainError { /// /// # Example #[inline] - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { ErrorIter { current: Some(self), } @@ -326,31 +225,26 @@ impl ChainError { } /// Convenience methods for `Result<>` to turn the error into a decorated ChainError -pub trait ResultTrait>> { +pub trait ResultTrait>> { /// Decorate the error with a `kind` of type `T` and the source `Location` - fn context( - self, - kind: T, - ) -> std::result::Result>; + fn context(self, kind: T) -> std::result::Result>; /// Decorate the `error` with a `kind` of type `T` produced with a `FnOnce(&error)` and the source `Location` fn map_context T>( self, op: F, - ) -> std::result::Result>; + ) -> std::result::Result>; } -impl>> ResultTrait +impl>> ResultTrait for std::result::Result { #[track_caller] - fn context( - self, - kind: T, - ) -> std::result::Result> { + #[inline] + fn context(self, kind: T) -> std::result::Result> { match self { Ok(t) => Ok(t), - Err(error_cause) => Err(ChainError::new( + Err(error_cause) => Err(Error::new( kind, Some(error_cause.into()), Some(Location::caller().to_string()), @@ -359,15 +253,16 @@ impl>> ResultTrait } #[track_caller] + #[inline] fn map_context T>( self, op: F, - ) -> std::result::Result> { + ) -> std::result::Result> { match self { Ok(t) => Ok(t), Err(error_cause) => { let kind = op(&error_cause); - Err(ChainError::new( + Err(Error::new( kind, Some(error_cause.into()), Some(Location::caller().to_string()), @@ -379,21 +274,21 @@ impl>> ResultTrait /// An iterator over all error causes/sources pub struct ErrorIter<'a> { - current: Option<&'a (dyn Error + 'static)>, + current: Option<&'a (dyn StdError + 'static)>, } impl<'a> Iterator for ErrorIter<'a> { - type Item = &'a (dyn Error + 'static); + type Item = &'a (dyn StdError + 'static); #[inline] fn next(&mut self) -> Option { let current = self.current; - self.current = self.current.and_then(Error::source); + self.current = self.current.and_then(StdError::source); current } } -impl std::ops::Deref for ChainError { +impl std::ops::Deref for Error { type Target = T; #[inline] @@ -407,28 +302,28 @@ pub trait ChainErrorDown { /// Test if of type `ChainError` fn is_chain(&self) -> bool; /// Downcast to a reference of `ChainError` - fn downcast_chain_ref(&self) -> Option<&ChainError>; + fn downcast_chain_ref(&self) -> Option<&Error>; /// Downcast to a mutable reference of `ChainError` - fn downcast_chain_mut(&mut self) -> Option<&mut ChainError>; + fn downcast_chain_mut(&mut self) -> Option<&mut Error>; /// Downcast to T of `ChainError` - fn downcast_inner_ref(&self) -> Option<&T>; + fn downcast_inner_ref(&self) -> Option<&T>; /// Downcast to T mutable reference of `ChainError` - fn downcast_inner_mut(&mut self) -> Option<&mut T>; + fn downcast_inner_mut(&mut self) -> Option<&mut T>; } -impl ChainErrorDown for ChainError { +impl ChainErrorDown for Error { #[inline] fn is_chain(&self) -> bool { TypeId::of::() == TypeId::of::() } #[inline] - fn downcast_chain_ref(&self) -> Option<&ChainError> { + fn downcast_chain_ref(&self) -> Option<&Error> { if self.is_chain::() { #[allow(clippy::cast_ptr_alignment)] unsafe { #[allow(trivial_casts)] - Some(*(self as *const dyn Error as *const &ChainError)) + Some(*(self as *const dyn StdError as *const &Error)) } } else { None @@ -436,24 +331,24 @@ impl ChainErrorDown for ChainError { } #[inline] - fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { + fn downcast_chain_mut(&mut self) -> Option<&mut Error> { if self.is_chain::() { #[allow(clippy::cast_ptr_alignment)] unsafe { #[allow(trivial_casts)] - Some(&mut *(self as *mut dyn Error as *mut &mut ChainError)) + Some(&mut *(self as *mut dyn StdError as *mut &mut Error)) } } else { None } } #[inline] - fn downcast_inner_ref(&self) -> Option<&T> { + fn downcast_inner_ref(&self) -> Option<&T> { if self.is_chain::() { #[allow(clippy::cast_ptr_alignment)] unsafe { #[allow(trivial_casts)] - Some(&(*(self as *const dyn Error as *const &ChainError)).kind) + Some(&(*(self as *const dyn StdError as *const &Error)).kind) } } else { None @@ -461,12 +356,12 @@ impl ChainErrorDown for ChainError { } #[inline] - fn downcast_inner_mut(&mut self) -> Option<&mut T> { + fn downcast_inner_mut(&mut self) -> Option<&mut T> { if self.is_chain::() { #[allow(clippy::cast_ptr_alignment)] unsafe { #[allow(trivial_casts)] - Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError)).kind) + Some(&mut (*(self as *mut dyn StdError as *mut &mut Error)).kind) } } else { None @@ -474,142 +369,141 @@ impl ChainErrorDown for ChainError { } } -impl ChainErrorDown for dyn Error + 'static { +impl ChainErrorDown for dyn StdError + 'static { #[inline] fn is_chain(&self) -> bool { - self.is::>() + self.is::>() } #[inline] - fn downcast_chain_ref(&self) -> Option<&ChainError> { - self.downcast_ref::>() + fn downcast_chain_ref(&self) -> Option<&Error> { + self.downcast_ref::>() } #[inline] - fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { - self.downcast_mut::>() + fn downcast_chain_mut(&mut self) -> Option<&mut Error> { + self.downcast_mut::>() } #[inline] - fn downcast_inner_ref(&self) -> Option<&T> { + fn downcast_inner_ref(&self) -> Option<&T> { self.downcast_ref::() - .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) } #[inline] - fn downcast_inner_mut(&mut self) -> Option<&mut T> { + fn downcast_inner_mut(&mut self) -> Option<&mut T> { if self.is::() { return self.downcast_mut::(); } - self.downcast_mut::>() + self.downcast_mut::>() .and_then(|e| e.downcast_inner_mut::()) } } -impl ChainErrorDown for dyn Error + 'static + Send { +impl ChainErrorDown for dyn StdError + 'static + Send { #[inline] fn is_chain(&self) -> bool { - self.is::>() + self.is::>() } #[inline] - fn downcast_chain_ref(&self) -> Option<&ChainError> { - self.downcast_ref::>() + fn downcast_chain_ref(&self) -> Option<&Error> { + self.downcast_ref::>() } #[inline] - fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { - self.downcast_mut::>() + fn downcast_chain_mut(&mut self) -> Option<&mut Error> { + self.downcast_mut::>() } #[inline] - fn downcast_inner_ref(&self) -> Option<&T> { + fn downcast_inner_ref(&self) -> Option<&T> { self.downcast_ref::() - .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) } #[inline] - fn downcast_inner_mut(&mut self) -> Option<&mut T> { + fn downcast_inner_mut(&mut self) -> Option<&mut T> { if self.is::() { return self.downcast_mut::(); } - self.downcast_mut::>() + self.downcast_mut::>() .and_then(|e| e.downcast_inner_mut::()) } } -impl ChainErrorDown for dyn Error + 'static + Send + Sync { +impl ChainErrorDown for dyn StdError + 'static + Send + Sync { #[inline] fn is_chain(&self) -> bool { - self.is::>() + self.is::>() } #[inline] - fn downcast_chain_ref(&self) -> Option<&ChainError> { - self.downcast_ref::>() + fn downcast_chain_ref(&self) -> Option<&Error> { + self.downcast_ref::>() } #[inline] - fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { - self.downcast_mut::>() + fn downcast_chain_mut(&mut self) -> Option<&mut Error> { + self.downcast_mut::>() } #[inline] - fn downcast_inner_ref(&self) -> Option<&T> { + fn downcast_inner_ref(&self) -> Option<&T> { self.downcast_ref::() - .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) } #[inline] - fn downcast_inner_mut(&mut self) -> Option<&mut T> { + fn downcast_inner_mut(&mut self) -> Option<&mut T> { if self.is::() { return self.downcast_mut::(); } - self.downcast_mut::>() + self.downcast_mut::>() .and_then(|e| e.downcast_inner_mut::()) } } -impl Error for ChainError { +impl StdError for Error { #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { + fn source(&self) -> Option<&(dyn StdError + 'static)> { self.error_cause .as_ref() - .map(|e| e.as_ref() as &(dyn Error + 'static)) + .map(|e| e.as_ref() as &(dyn StdError + 'static)) } } -impl Error for &mut ChainError { +impl StdError for &mut Error { #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { + fn source(&self) -> Option<&(dyn StdError + 'static)> { self.error_cause .as_ref() - .map(|e| e.as_ref() as &(dyn Error + 'static)) + .map(|e| e.as_ref() as &(dyn StdError + 'static)) } } -impl Display for ChainError { +impl Display for Error { #[inline] - fn fmt(&self, f: &mut Formatter<'_>) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.kind)?; - #[cfg(feature = "display-cause")] - { + if f.alternate() { if let Some(e) = self.source() { - writeln!(f, "\nCaused by:")?; - Display::fmt(&e, f)?; + write!(f, "\nCaused by:\n {:#}", &e)?; } } + Ok(()) } } -impl Debug for ChainError { +impl Debug for Error { #[inline] - fn fmt(&self, f: &mut Formatter<'_>) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if f.alternate() { let mut f = f.debug_struct(&format!("ChainError<{}>", std::any::type_name::())); @@ -633,54 +527,29 @@ impl Debug for ChainError { } if let Some(e) = self.source() { - writeln!(f, "\nCaused by:")?; - Debug::fmt(&e, f)?; + write!(f, "\nCaused by:\n{:?}", &e)?; } Ok(()) } } } -/// `ChainErrorFrom` is similar to `From` -pub trait ChainErrorFrom: Sized { - /// similar to From::from() - fn chain_error_from(from: T, line_filename: Option) -> ChainError; -} - -/// `IntoChainError` is similar to `Into` -pub trait IntoChainError: Sized { - /// similar to Into::into() - fn into_chain_error(self, line_filename: Option) -> ChainError; -} - -impl IntoChainError for T +impl From for Error where - U: ChainErrorFrom, + T: 'static + Display + Debug, { + #[track_caller] #[inline] - fn into_chain_error(self, line_filename: Option) -> ChainError { - U::chain_error_from(self, line_filename) + fn from(e: T) -> Error { + Error::new(e, None, Some(Location::caller().to_string())) } } - -impl ChainErrorFrom for U -where - T: Into, - U: 'static + Display + Debug, -{ - #[inline] - fn chain_error_from(t: T, line_filename: Option) -> ChainError { - let e: U = t.into(); - ChainError::new(e, None, line_filename) - } -} - /// Convenience macro to create a "new type" T(String) and implement Display + Debug for T /// /// # Examples /// /// ```rust -/// # use crate::chainerror::*; +/// # use chainerror::prelude::v1::*; /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; @@ -799,7 +668,7 @@ macro_rules! derive_str_context { #[macro_export] macro_rules! derive_err_kind { ($e:ident, $k:ident) => { - pub struct $e($crate::ChainError<$k>); + pub struct $e($crate::Error<$k>); impl $e { pub fn kind(&self) -> &$k { @@ -809,12 +678,12 @@ macro_rules! derive_err_kind { impl From<$k> for $e { fn from(e: $k) -> Self { - $e($crate::ChainError::new(e, None, None)) + $e($crate::Error::new(e, None, None)) } } impl From> for $e { - fn from(e: $crate::ChainError<$k>) -> Self { + fn from(e: $crate::Error<$k>) -> Self { $e(e) } } diff --git a/tests/test_iter.rs b/tests/test_iter.rs index ad4f9f1..f5f0e58 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -2,7 +2,6 @@ use chainerror::prelude::v1::*; use std::error::Error; use std::io; -#[cfg(not(feature = "display-cause"))] #[test] fn test_iter() -> Result<(), Box> { use std::fmt::Write; @@ -32,9 +31,8 @@ fn test_iter() -> Result<(), Box> { Ok(()) } -#[cfg(feature = "display-cause")] #[test] -fn test_iter() -> Result<(), Box> { +fn test_iter_alternate() -> Result<(), Box> { let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); let err = err.context("1"); let err = err.context("2"); @@ -44,9 +42,9 @@ fn test_iter() -> Result<(), Box> { let err = err.context("6"); let err = err.err().unwrap(); - let res = err.to_string(); + let res = format!("{:#}", err); - assert_eq!(res, "6\nCaused by:\n5\nCaused by:\n4\nCaused by:\n3\nCaused by:\n2\nCaused by:\n1\nCaused by:\nentity not found"); + assert_eq!(res, format!("6\nCaused by:\n 5\nCaused by:\n 4\nCaused by:\n 3\nCaused by:\n 2\nCaused by:\n 1\nCaused by:\n {:#}", io::Error::from(io::ErrorKind::NotFound))); let io_error: Option<&io::Error> = err .iter()