From a869a2e3ea33c39b2c31bfae2d781ac8635f359f Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 15 Jan 2019 09:17:58 +0100 Subject: [PATCH 01/85] more idiomatic --- src/lib.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 09df831..b052a53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -462,31 +462,19 @@ impl ChainErrorDown for dyn Error + 'static + Send + Sync { impl Error for ChainError { fn source(&self) -> Option<&(dyn Error + 'static)> { - if let Some(ref e) = self.error_cause { - Some(e.as_ref()) - } else { - None - } + self.error_cause.as_ref().map(|e| e.as_ref()) } } impl Error for &ChainError { fn source(&self) -> Option<&(dyn Error + 'static)> { - if let Some(ref e) = self.error_cause { - Some(e.as_ref()) - } else { - None - } + self.error_cause.as_ref().map(|e| e.as_ref()) } } impl Error for &mut ChainError { fn source(&self) -> Option<&(dyn Error + 'static)> { - if let Some(ref e) = self.error_cause { - Some(e.as_ref()) - } else { - None - } + self.error_cause.as_ref().map(|e| e.as_ref()) } } From 49515b86f914db689e9946634aedd1a6ca358596 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 23 Jan 2019 10:04:18 +0100 Subject: [PATCH 02/85] fixed travis badge and link in README.md --- Cargo.toml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4b4d99d..71b089e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.4.0" +version = "0.4.1" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/README.md b/README.md index b24834d..71a829d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # chainerror -[![Build Status](https://travis-ci.com/haraldh/chainerror.svg?branch=master)](https://travis-ci.com/haraldh/chainerror) +[![Build Status](https://travis-ci.org/haraldh/chainerror.svg?branch=master)](https://travis-ci.org/haraldh/chainerror) [![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/) From d14af67560d13210dc92b932268d149e833c9e9e Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 25 Jan 2019 13:53:30 +0100 Subject: [PATCH 03/85] add ChainError Iterator --- src/lib.rs | 52 ++++++++++++++++++------------------ tests/test_iter.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 27 deletions(-) create mode 100644 tests/test_iter.rs diff --git a/src/lib.rs b/src/lib.rs index b052a53..faa60d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -214,11 +214,7 @@ impl ChainError { /// return the root cause of the error chain, if any exists pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { - let mut cause = self as &(dyn Error + 'static); - while let Some(c) = cause.source() { - cause = c; - } - Some(cause) + self.iter().last() } /** find the first error cause of type U, if any exists @@ -270,17 +266,7 @@ impl ChainError { ~~~ **/ pub fn find_cause(&self) -> Option<&U> { - let mut cause = self as &(dyn Error + 'static); - loop { - if cause.is::() { - return cause.downcast_ref::(); - } - - match cause.source() { - Some(c) => cause = c, - None => return None, - } - } + self.iter().filter_map(Error::downcast_ref::()).next() } /** find the first error cause of type ChainError, if any exists @@ -299,17 +285,9 @@ impl ChainError { **/ pub fn find_chain_cause(&self) -> Option<&ChainError> { - let mut cause = self as &(dyn Error + 'static); - loop { - if cause.is::>() { - return cause.downcast_ref::>(); - } - - match cause.source() { - Some(c) => cause = c, - None => return None, - } - } + self.iter() + .filter_map(Error::downcast_ref::>()) + .next() } /** return a reference to T of `ChainError` @@ -374,6 +352,26 @@ impl ChainError { pub fn kind(&self) -> &T { &self.kind } + + pub fn iter(&self) -> impl Iterator { + ErrorIter { + current: Some(self), + } + } +} + +struct ErrorIter<'a> { + current: Option<&'a (dyn Error + 'static)>, +} + +impl<'a> Iterator for ErrorIter<'a> { + type Item = &'a (dyn Error + 'static); + + fn next(&mut self) -> Option { + let current = self.current; + self.current = self.current.and_then(Error::source); + current + } } /** convenience trait to hide the `ChainError` implementation internals diff --git a/tests/test_iter.rs b/tests/test_iter.rs new file mode 100644 index 0000000..c351160 --- /dev/null +++ b/tests/test_iter.rs @@ -0,0 +1,66 @@ +use chainerror::*; +use std::error::Error; +use std::fmt::Write; +use std::io; + +#[test] +fn test_iter() -> Result<(), Box> { + let err = io::Error::from(io::ErrorKind::NotFound); + let err = cherr!(err, "1"); + let err = cherr!(err, "2"); + let err = cherr!(err, "3"); + let err = cherr!(err, "4"); + let err = cherr!(err, "5"); + let err = cherr!(err, "6"); + + let mut res = String::new(); + + for e in err.iter() { + write!(res, "{}", e.to_string())?; + } + assert_eq!(res, "654321entity not found"); + + let io_error: Option<&io::Error> = err + .iter() + .filter_map(Error::downcast_ref::) + .next(); + + assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound); + + Ok(()) +} + +#[test] +fn test_find_cause() -> Result<(), Box> { + let err = io::Error::from(io::ErrorKind::NotFound); + let err = cherr!(err, "1"); + let err = cherr!(err, "2"); + let err = cherr!(err, "3"); + let err = cherr!(err, "4"); + let err = cherr!(err, "5"); + let err = cherr!(err, "6"); + + let io_error: Option<&io::Error> = err.find_cause::(); + + assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound); + + Ok(()) +} + +#[test] +fn test_root_cause() -> Result<(), Box> { + let err = io::Error::from(io::ErrorKind::NotFound); + let err = cherr!(err, "1"); + let err = cherr!(err, "2"); + let err = cherr!(err, "3"); + let err = cherr!(err, "4"); + let err = cherr!(err, "5"); + let err = cherr!(err, "6"); + + let err: Option<&(dyn std::error::Error + 'static)> = err.root_cause(); + let io_error: Option<&io::Error> = err.and_then(Error::downcast_ref::); + + assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound); + + Ok(()) +} From e7d4afb86cc619d4092d0136b1a7eb850a2cbeb0 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Sat, 26 Jan 2019 10:23:00 +0100 Subject: [PATCH 04/85] fixed filter() and filter_map() calls --- Cargo.toml | 2 +- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71b089e..99e0ac4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.4.1" +version = "0.4.2" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index faa60d5..3a51d1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,7 +266,7 @@ impl ChainError { ~~~ **/ pub fn find_cause(&self) -> Option<&U> { - self.iter().filter_map(Error::downcast_ref::()).next() + self.iter().filter_map(Error::downcast_ref::).next() } /** find the first error cause of type ChainError, if any exists @@ -286,7 +286,7 @@ impl ChainError { **/ pub fn find_chain_cause(&self) -> Option<&ChainError> { self.iter() - .filter_map(Error::downcast_ref::>()) + .filter_map(Error::downcast_ref::>) .next() } From 694cb4f27314755c9af12c1f2fd7c7cd90de172c Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Sat, 26 Jan 2019 10:44:25 +0100 Subject: [PATCH 05/85] add find_chain_or_cause() --- src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 3a51d1e..e89a848 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -290,6 +290,16 @@ impl ChainError { .next() } + // FIXME: naming + fn find_chain_or_cause(&self) -> Option<&U> { + self.iter() + .filter_map(|e| { + e.downcast_ref::>().map(|e| e.kind()) + .or_else(|| e.downcast_ref::()) + }) + .next() + } + /** return a reference to T of `ChainError` # Examples From ef7edb10614147294da389ccb579428ceceaa9b0 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 1 Feb 2019 10:17:14 +0100 Subject: [PATCH 06/85] Use line comments instead of block comments --- src/lib.rs | 1096 ++++++++++++++++++++++++++-------------------------- 1 file changed, 540 insertions(+), 556 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e89a848..547e4f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,182 +1,177 @@ -/*! - -`chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your -binaries, you still have the error backtrace. - -`chainerror` has no dependencies! - -`chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` 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. - -## Features - -`no-fileline` -: completely turn off storing filename and line - -`display-cause` -: turn on printing a backtrace of the errors in `Display` - -`no-debug-cause` -: turn off printing a backtrace of the errors in `Debug` - - -# Tutorial - -Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) - -# Examples - -~~~rust -use chainerror::*; -use std::error::Error; -use std::io; -use std::result::Result; - -fn do_some_io() -> Result<(), Box> { - Err(io::Error::from(io::ErrorKind::NotFound))?; - Ok(()) -} - -fn func2() -> Result<(), Box> { - let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; - Ok(()) -} - -fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!("func1 error"))?; - Ok(()) -} - -fn main() { - if let Err(e) = func1() { - #[cfg(not(windows))] - assert_eq!( - format!("\n{:?}\n", e), r#" -src/lib.rs:20: func1 error -Caused by: -src/lib.rs:15: Error reading 'foo.txt' -Caused by: -Kind(NotFound) -"# - ); - } -# else { -# unreachable!(); -# } -} -~~~ - - -~~~rust -use chainerror::*; -use std::error::Error; -use std::io; -use std::result::Result; - -fn do_some_io() -> Result<(), Box> { - Err(io::Error::from(io::ErrorKind::NotFound))?; - Ok(()) -} - -fn func3() -> Result<(), Box> { - let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; - Ok(()) -} - -derive_str_cherr!(Func2Error); - -fn func2() -> ChainResult<(), Func2Error> { - func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; - Ok(()) -} - -enum Func1Error { - Func2, - IO(String), -} - -impl ::std::fmt::Display for Func1Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match self { - Func1Error::Func2 => write!(f, "func1 error calling func2"), - Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename), - } - } -} - -impl ::std::fmt::Debug for Func1Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}", self) - } -} - -fn func1() -> ChainResult<(), Func1Error> { - func2().map_err(|e| cherr!(e, Func1Error::Func2))?; - let filename = String::from("bar.txt"); - do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; - Ok(()) -} - -fn main() { - if let Err(e) = func1() { - assert!( - match e.kind() { - Func1Error::Func2 => { - eprintln!("Main Error Report: func1 error calling func2"); - true - } - Func1Error::IO(filename) => { - eprintln!("Main Error Report: func1 error reading '{}'", filename); - false - } - } - ); - - assert!(e.find_chain_cause::().is_some()); - - if let Some(e) = e.find_chain_cause::() { - eprintln!("\nError reported by Func2Error: {}", e) - } - - - assert!(e.root_cause().is_some()); - - if let Some(e) = e.root_cause() { - let ioerror = e.downcast_ref::().unwrap(); - eprintln!("\nThe root cause was: std::io::Error: {:#?}", ioerror); - } - - #[cfg(not(windows))] - assert_eq!( - format!("\n{:?}\n", e), r#" -src/lib.rs:47: func1 error calling func2 -Caused by: -src/lib.rs:22: Func2Error(func2 error: calling func3) -Caused by: -src/lib.rs:15: Error reading 'foo.txt' -Caused by: -Kind(NotFound) -"# - ); - } -# else { -# unreachable!(); -# } -} -~~~ - -!*/ +//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your +//! binaries, you still have the error backtrace. +//! +//! `chainerror` has no dependencies! +//! +//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` 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. +//! +//! ## Features +//! +//! `no-fileline` +//! : completely turn off storing filename and line +//! +//! `display-cause` +//! : turn on printing a backtrace of the errors in `Display` +//! +//! `no-debug-cause` +//! : turn off printing a backtrace of the errors in `Debug` +//! +//! +//! # Tutorial +//! +//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) +//! +//! # Examples +//! +//! ~~~rust +//! use chainerror::*; +//! use std::error::Error; +//! use std::io; +//! use std::result::Result; +//! +//! fn do_some_io() -> Result<(), Box> { +//! Err(io::Error::from(io::ErrorKind::NotFound))?; +//! Ok(()) +//! } +//! +//! fn func2() -> Result<(), Box> { +//! let filename = "foo.txt"; +//! do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! fn func1() -> Result<(), Box> { +//! func2().map_err(mstrerr!("func1 error"))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! if let Err(e) = func1() { +//! #[cfg(not(windows))] +//! assert_eq!( +//! format!("\n{:?}\n", e), r#" +//! src/lib.rs:20: func1 error +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ~~~ +//! +//! +//! ~~~rust +//! use chainerror::*; +//! use std::error::Error; +//! use std::io; +//! use std::result::Result; +//! +//! fn do_some_io() -> Result<(), Box> { +//! Err(io::Error::from(io::ErrorKind::NotFound))?; +//! Ok(()) +//! } +//! +//! fn func3() -> Result<(), Box> { +//! let filename = "foo.txt"; +//! do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! derive_str_cherr!(Func2Error); +//! +//! fn func2() -> ChainResult<(), Func2Error> { +//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; +//! Ok(()) +//! } +//! +//! enum Func1Error { +//! Func2, +//! IO(String), +//! } +//! +//! impl ::std::fmt::Display for Func1Error { +//! fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +//! match self { +//! Func1Error::Func2 => write!(f, "func1 error calling func2"), +//! Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename), +//! } +//! } +//! } +//! +//! impl ::std::fmt::Debug for Func1Error { +//! fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +//! write!(f, "{}", self) +//! } +//! } +//! +//! fn func1() -> ChainResult<(), Func1Error> { +//! func2().map_err(|e| cherr!(e, Func1Error::Func2))?; +//! let filename = String::from("bar.txt"); +//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! if let Err(e) = func1() { +//! assert!( +//! match e.kind() { +//! Func1Error::Func2 => { +//! eprintln!("Main Error Report: func1 error calling func2"); +//! true +//! } +//! Func1Error::IO(filename) => { +//! eprintln!("Main Error Report: func1 error reading '{}'", filename); +//! false +//! } +//! } +//! ); +//! +//! assert!(e.find_chain_cause::().is_some()); +//! +//! if let Some(e) = e.find_chain_cause::() { +//! eprintln!("\nError reported by Func2Error: {}", e) +//! } +//! +//! +//! assert!(e.root_cause().is_some()); +//! +//! if let Some(e) = e.root_cause() { +//! let ioerror = e.downcast_ref::().unwrap(); +//! eprintln!("\nThe root cause was: std::io::Error: {:#?}", ioerror); +//! } +//! +//! #[cfg(not(windows))] +//! assert_eq!( +//! format!("\n{:?}\n", e), r#" +//! src/lib.rs:47: func1 error calling func2 +//! Caused by: +//! src/lib.rs:22: Func2Error(func2 error: calling func3) +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ~~~ use std::any::TypeId; use std::error::Error; use std::fmt::{Debug, Display, Formatter, Result}; -/** chains an inner error kind `T` with a causing error -**/ +/// chains an inner error kind `T` with a causing error pub struct ChainError { #[cfg(not(feature = "no-fileline"))] occurrence: Option<(u32, &'static str)>, @@ -217,73 +212,70 @@ impl ChainError { self.iter().last() } - /** find the first error cause of type U, if any exists - - # Examples - - ~~~rust - # use crate::chainerror::*; - # use std::error::Error; - # use std::io; - # use std::result::Result; - # - fn do_some_io() -> Result<(), Box> { - Err(io::Error::from(io::ErrorKind::NotFound))?; - Ok(()) - } - - derive_str_cherr!(Func2Error); - - fn func2() -> Result<(), Box> { - let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; - Ok(()) - } - - derive_str_cherr!(Func1Error); - - fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!(Func1Error, "func1 error"))?; - Ok(()) - } - - fn main() { - if let Err(e) = func1() { - if let Some(f1err) = e.downcast_chain_ref::() { - - assert!(f1err.find_cause::().is_some()); - - assert!(f1err.find_chain_cause::().is_some()); - } - # else { - # panic!(); - # } - } - # else { - # unreachable!(); - # } - } - ~~~ - **/ + /// Find the first error cause of type U, if any exists + /// + /// # Examples + /// + /// ~~~rust + /// # use crate::chainerror::*; + /// # use std::error::Error; + /// # use std::io; + /// # use std::result::Result; + /// # + /// fn do_some_io() -> Result<(), Box> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func1Error); + /// + /// fn func1() -> Result<(), Box> { + /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// if let Some(f1err) = e.downcast_chain_ref::() { + /// + /// assert!(f1err.find_cause::().is_some()); + /// + /// assert!(f1err.find_chain_cause::().is_some()); + /// } + /// # else { + /// # panic!(); + /// # } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ~~~ pub fn find_cause(&self) -> Option<&U> { self.iter().filter_map(Error::downcast_ref::).next() } - /** find the first error cause of type ChainError, if any exists - - Same as `find_cause`, but hides the `ChainError` implementation internals - - # Examples - - ~~~rust,ignore - /// Instead of writing - err.find_cause::>(); - - /// leave out the ChainError implementation detail - err.find_chain_cause::(); - ~~~ - - **/ + /// Find the first error cause of type ChainError, if any exists + /// + /// Same as `find_cause`, but hides the `ChainError` implementation internals + /// + /// # Examples + /// + /// ~~~rust,ignore + /// // Instead of writing + /// err.find_cause::>(); + /// + /// // leave out the ChainError implementation detail + /// err.find_chain_cause::(); + /// ~~~ pub fn find_chain_cause(&self) -> Option<&ChainError> { self.iter() .filter_map(Error::downcast_ref::>) @@ -300,65 +292,63 @@ impl ChainError { .next() } - /** return a reference to T of `ChainError` - - # Examples - - ~~~rust - # use crate::chainerror::*; - # use std::error::Error; - # use std::io; - # use std::result::Result; - # - fn do_some_io() -> Result<(), Box> { - Err(io::Error::from(io::ErrorKind::NotFound))?; - Ok(()) - } - - derive_str_cherr!(Func2Error); - - fn func2() -> Result<(), Box> { - let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; - Ok(()) - } - - #[derive(Debug)] - enum Func1ErrorKind { - Func2, - IO(String), - } - - // impl ::std::fmt::Display for Func1ErrorKind {…} - # impl ::std::fmt::Display for Func1ErrorKind { - # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - # match self { - # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"), - # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), - # } - # } - # } - - fn func1() -> ChainResult<(), Func1ErrorKind> { - func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; - do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?; - Ok(()) - } - - fn main() { - if let Err(e) = func1() { - match e.kind() { - Func1ErrorKind::Func2 => {}, - Func1ErrorKind::IO(filename) => panic!(), - } - } - # else { - # unreachable!(); - # } - } - ~~~ - - **/ + /// Return a reference to T of `ChainError` + /// + /// # Examples + /// + /// ~~~rust + /// # use crate::chainerror::*; + /// # use std::error::Error; + /// # use std::io; + /// # use std::result::Result; + /// # + /// fn do_some_io() -> Result<(), Box> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// #[derive(Debug)] + /// enum Func1ErrorKind { + /// Func2, + /// IO(String), + /// } + /// + /// /// impl ::std::fmt::Display for Func1ErrorKind {…} + /// # impl ::std::fmt::Display for Func1ErrorKind { + /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + /// # match self { + /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"), + /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + /// # } + /// # } + /// # } + /// + /// fn func1() -> ChainResult<(), Func1ErrorKind> { + /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// match e.kind() { + /// Func1ErrorKind::Func2 => {}, + /// Func1ErrorKind::IO(filename) => panic!(), + /// } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ~~~ pub fn kind(&self) -> &T { &self.kind } @@ -384,17 +374,13 @@ impl<'a> Iterator for ErrorIter<'a> { } } -/** convenience trait to hide the `ChainError` implementation internals -**/ +/// Convenience trait to hide the `ChainError` implementation internals pub trait ChainErrorDown { - /** test if of type `ChainError` - **/ + /// Test if of type `ChainError` fn is_chain(&self) -> bool; - /** downcast to a reference of `ChainError` - **/ + /// Downcast to a reference of `ChainError` fn downcast_chain_ref(&self) -> Option<&ChainError>; - /** downcast to a mutable reference of `ChainError` - **/ + /// Downcast to a mutable reference of `ChainError` fn downcast_chain_mut(&mut self) -> Option<&mut ChainError>; } @@ -569,93 +555,91 @@ macro_rules! into_cherr { }; } -/** creates a new `ChainError` - -# Examples - -Create a new ChainError, where `FooError` must implement `Display` and `Debug`. -~~~rust -# use chainerror::*; -# -# #[derive(Debug)] -enum FooError { - Bar, - Baz(&'static str), -} -# -# impl ::std::fmt::Display for FooError { -# fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { -# match self { -# FooError::Bar => write!(f, "Bar Error"), -# FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), -# } -# } -# } - -// impl ::std::fmt::Display for FooError - -fn do_some_stuff() -> bool { - false -} - -fn func() -> ChainResult<(), FooError> { - if ! do_some_stuff() { - Err(cherr!(FooError::Baz("Error")))?; - } - Ok(()) -} -# -# pub fn main() { -# match func().unwrap_err().kind() { -# FooError::Baz(s) if s == &"Error" => {}, -# _ => panic!(), -# } -# } -~~~ - -Additionally an error cause can be added. - -~~~rust -# use chainerror::*; -# use std::io; -# use std::error::Error; -# -# #[derive(Debug)] -# enum FooError { -# Bar, -# Baz(&'static str), -# } -# -# impl ::std::fmt::Display for FooError { -# fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { -# match self { -# FooError::Bar => write!(f, "Bar Error"), -# FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), -# } -# } -# } -# -fn do_some_stuff() -> Result<(), Box> { - Err(io::Error::from(io::ErrorKind::NotFound))?; - Ok(()) -} - -fn func() -> ChainResult<(), FooError> { - do_some_stuff().map_err( - |e| cherr!(e, FooError::Baz("Error")) - )?; - Ok(()) -} -# -# pub fn main() { -# match func().unwrap_err().kind() { -# FooError::Baz(s) if s == &"Error" => {}, -# _ => panic!(), -# } -# } -~~~ - -**/ +/// Creates a new `ChainError` +/// +/// # Examples +/// +/// Create a new ChainError, where `FooError` must implement `Display` and `Debug`. +/// ~~~rust +/// # use chainerror::*; +/// # +/// # #[derive(Debug)] +/// enum FooError { +/// Bar, +/// Baz(&'static str), +/// } +/// # +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// +/// // impl ::std::fmt::Display for FooError +/// +/// fn do_some_stuff() -> bool { +/// false +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// if ! do_some_stuff() { +/// Err(cherr!(FooError::Baz("Error")))?; +/// } +/// Ok(()) +/// } +/// # +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {}, +/// # _ => panic!(), +/// # } +/// # } +/// ~~~ +/// +/// Additionally an error cause can be added. +/// +/// ~~~rust +/// # use chainerror::*; +/// # use std::io; +/// # use std::error::Error; +/// # +/// # #[derive(Debug)] +/// # enum FooError { +/// # Bar, +/// # Baz(&'static str), +/// # } +/// # +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// # +/// fn do_some_stuff() -> Result<(), Box> { +/// Err(io::Error::from(io::ErrorKind::NotFound))?; +/// Ok(()) +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// do_some_stuff().map_err( +/// |e| cherr!(e, FooError::Baz("Error")) +/// )?; +/// Ok(()) +/// } +/// # +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {}, +/// # _ => panic!(), +/// # } +/// # } +/// ~~~ #[macro_export] macro_rules! cherr { ( $k:expr ) => ({ @@ -685,93 +669,92 @@ macro_rules! cherr { } -/** convenience macro for |e| cherr!(e, format!(…)) - -# Examples - -~~~rust -# use crate::chainerror::*; -# use std::error::Error; -# use std::io; -# use std::result::Result; -# -# fn do_some_io() -> Result<(), Box> { -# Err(io::Error::from(io::ErrorKind::NotFound))?; -# Ok(()) -# } -# -fn func2() -> Result<(), Box> { - let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; - Ok(()) -} - -fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!("func1 error"))?; - Ok(()) -} - -# fn main() { -# if let Err(e) = func1() { -# #[cfg(not(windows))] -# assert_eq!( -# format!("\n{:?}\n", e), r#" -# src/lib.rs:20: func1 error -# Caused by: -# src/lib.rs:15: Error reading 'foo.txt' -# Caused by: -# Kind(NotFound) -# "# -# ); -# } else { -# unreachable!(); -# } -# } -~~~ - -`mstrerr!()` can also be used to map a new `ChainError`, where T was defined with -`derive_str_cherr!(T)` - -~~~rust -# use crate::chainerror::*; -# use std::error::Error; -# use std::io; -# use std::result::Result; -# -# fn do_some_io() -> Result<(), Box> { -# Err(io::Error::from(io::ErrorKind::NotFound))?; -# Ok(()) -# } -# -derive_str_cherr!(Func2Error); - -fn func2() -> Result<(), Box> { - let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; - Ok(()) -} - -derive_str_cherr!(Func1Error); - -fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!(Func1Error, "func1 error"))?; - Ok(()) -} -# -# fn main() { -# if let Err(e) = func1() { -# if let Some(f1err) = e.downcast_chain_ref::() { -# assert!(f1err.find_cause::>().is_some()); -# assert!(f1err.find_chain_cause::().is_some()); -# } else { -# panic!(); -# } -# } else { -# unreachable!(); -# } -# } -~~~ -**/ +/// Convenience macro for `|e| cherr!(e, format!(…))` +/// +/// # Examples +/// +/// ~~~rust +/// # use crate::chainerror::*; +/// # use std::error::Error; +/// # use std::io; +/// # use std::result::Result; +/// # +/// # fn do_some_io() -> Result<(), Box> { +/// # Err(io::Error::from(io::ErrorKind::NotFound))?; +/// # Ok(()) +/// # } +/// # +/// fn func2() -> Result<(), Box> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// fn func1() -> Result<(), Box> { +/// func2().map_err(mstrerr!("func1 error"))?; +/// Ok(()) +/// } +/// +/// # fn main() { +/// # if let Err(e) = func1() { +/// # #[cfg(not(windows))] +/// # assert_eq!( +/// # format!("\n{:?}\n", e), r#" +/// # src/lib.rs:20: func1 error +/// # Caused by: +/// # src/lib.rs:15: Error reading 'foo.txt' +/// # Caused by: +/// # Kind(NotFound) +/// # "# +/// # ); +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ~~~ +/// +/// `mstrerr!()` can also be used to map a new `ChainError`, where T was defined with +/// `derive_str_cherr!(T)` +/// +/// ~~~rust +/// # use crate::chainerror::*; +/// # use std::error::Error; +/// # use std::io; +/// # use std::result::Result; +/// # +/// # fn do_some_io() -> Result<(), Box> { +/// # Err(io::Error::from(io::ErrorKind::NotFound))?; +/// # Ok(()) +/// # } +/// # +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> Result<(), Box> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::() { +/// # assert!(f1err.find_cause::>().is_some()); +/// # assert!(f1err.find_chain_cause::().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ~~~ #[macro_export] macro_rules! mstrerr { ( $t:ident, $msg:expr ) => ({ @@ -803,41 +786,42 @@ macro_rules! mstrerr { }); } -/** convenience macro for cherr!(T(format!(…))) where T(String) -~~~rust -# use crate::chainerror::*; -# use std::error::Error; -# use std::result::Result; -# -derive_str_cherr!(Func2Error); - -fn func2() -> ChainResult<(), Func2Error> { - let filename = "foo.txt"; - Err(strerr!(Func2Error, "Error reading '{}'", filename)) -} - -derive_str_cherr!(Func1Error); - -fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!(Func1Error, "func1 error"))?; - Ok(()) -} -# -# fn main() { -# if let Err(e) = func1() { -# if let Some(f1err) = e.downcast_chain_ref::() { -# assert!(f1err.find_cause::>().is_some()); -# assert!(f1err.find_chain_cause::().is_some()); -# } else { -# panic!(); -# } -# } else { -# unreachable!(); -# } -# } -~~~ - -**/ +/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)` +/// +/// # Examples +/// +/// ~~~rust +/// # use crate::chainerror::*; +/// # use std::error::Error; +/// # use std::result::Result; +/// # +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// Err(strerr!(Func2Error, "Error reading '{}'", filename)) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::() { +/// # assert!(f1err.find_cause::>().is_some()); +/// # assert!(f1err.find_chain_cause::().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ~~~ #[macro_export] macro_rules! strerr { ( $t:ident, $msg:expr ) => ({ @@ -869,49 +853,49 @@ macro_rules! strerr { }); } -/** convenience macro to create a "new type" T(String) and implement Display + Debug for T - -~~~rust -# use crate::chainerror::*; -# use std::error::Error; -# use std::io; -# use std::result::Result; -# -# fn do_some_io() -> Result<(), Box> { -# Err(io::Error::from(io::ErrorKind::NotFound))?; -# Ok(()) -# } -# -derive_str_cherr!(Func2Error); - -fn func2() -> ChainResult<(), Func2Error> { - let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; - Ok(()) -} - -derive_str_cherr!(Func1Error); - -fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!(Func1Error, "func1 error"))?; - Ok(()) -} -# -# fn main() { -# if let Err(e) = func1() { -# if let Some(f1err) = e.downcast_chain_ref::() { -# assert!(f1err.find_cause::>().is_some()); -# assert!(f1err.find_chain_cause::().is_some()); -# } else { -# panic!(); -# } -# } else { -# unreachable!(); -# } -# } -~~~ - -**/ +/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T +/// +/// # Examples +/// +/// ~~~rust +/// # use crate::chainerror::*; +/// # use std::error::Error; +/// # use std::io; +/// # use std::result::Result; +/// # +/// # fn do_some_io() -> Result<(), Box> { +/// # Err(io::Error::from(io::ErrorKind::NotFound))?; +/// # Ok(()) +/// # } +/// # +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::() { +/// # assert!(f1err.find_cause::>().is_some()); +/// # assert!(f1err.find_chain_cause::().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ~~~ #[macro_export] macro_rules! derive_str_cherr { ($e:ident) => { From 4762a75cfe8ced3f4108c178a030ef2feb530361 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 1 Feb 2019 11:40:46 +0100 Subject: [PATCH 07/85] impl Deref for ChainError --- booksrc/tutorial12.md | 12 ++++++++ examples/tutorial12.rs | 64 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 ++++++ 3 files changed, 84 insertions(+) create mode 100644 booksrc/tutorial12.md create mode 100644 examples/tutorial12.rs diff --git a/booksrc/tutorial12.md b/booksrc/tutorial12.md new file mode 100644 index 0000000..682eaa8 --- /dev/null +++ b/booksrc/tutorial12.md @@ -0,0 +1,12 @@ +# Deref for the ErrorKind + +Because ChainError implements Deref to &T, we can also match on `*e` instead of `e.kind()`. + +~~~rust +use crate::chainerror::*; +{{#include ../examples/tutorial12.rs:2:}} +# #[allow(dead_code)] +# mod chainerror { +{{#includecomment ../src/lib.rs}} +# } +~~~ diff --git a/examples/tutorial12.rs b/examples/tutorial12.rs new file mode 100644 index 0000000..38ff09e --- /dev/null +++ b/examples/tutorial12.rs @@ -0,0 +1,64 @@ +use chainerror::*; +use std::error::Error; +use std::io; +use std::result::Result; + +fn do_some_io() -> Result<(), Box> { + Err(io::Error::from(io::ErrorKind::NotFound))?; + Ok(()) +} + +derive_str_cherr!(Func2Error); + +fn func2() -> Result<(), Box> { + let filename = "foo.txt"; + do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + Ok(()) +} + +enum Func1ErrorKind { + Func2, + IO(String), +} + +impl ::std::fmt::Display for Func1ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"), + Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + } + } +} + +impl ::std::fmt::Debug for Func1ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", self) + } +} + +impl ::std::error::Error for Func1ErrorKind {} + +fn func1() -> ChainResult<(), Func1ErrorKind> { + func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + let filename = String::from("bar.txt"); + do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO(filename)))?; + Ok(()) +} + +fn main() -> Result<(), Box> { + if let Err(e) = func1() { + match *e { + Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), + Func1ErrorKind::IO(ref filename) => { + eprintln!("Main Error Report: func1 error reading '{}'", filename) + } + } + + if let Some(e) = e.find_chain_cause::() { + eprintln!("\nError reported by Func2Error: {}", e) + } + + eprintln!("\nDebug Error:\n{:?}", e); + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 547e4f7..ca9d75e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -374,6 +374,14 @@ impl<'a> Iterator for ErrorIter<'a> { } } +impl std::ops::Deref for ChainError { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.kind() + } +} + /// Convenience trait to hide the `ChainError` implementation internals pub trait ChainErrorDown { /// Test if of type `ChainError` From f5457a2a51d09c097d027b3069ed50339a0dcdef Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Sun, 3 Feb 2019 19:33:26 +0100 Subject: [PATCH 08/85] add more docs --- booksrc/SUMMARY.md | 1 + booksrc/tutorial12.md | 4 ++-- examples/tutorial12.rs | 11 +++++++++++ src/lib.rs | 31 ++++++++++++++++++++++++++----- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/booksrc/SUMMARY.md b/booksrc/SUMMARY.md index ac070ee..367577f 100644 --- a/booksrc/SUMMARY.md +++ b/booksrc/SUMMARY.md @@ -13,5 +13,6 @@ - [Selective Error Handling](tutorial9.md) - [ErrorKind to the rescue](tutorial10.md) - [Debug for the ErrorKind](tutorial11.md) +- [Deref for the ErrorKind](tutorial12.md) [The End](end.md) \ No newline at end of file diff --git a/booksrc/tutorial12.md b/booksrc/tutorial12.md index 682eaa8..6373ca0 100644 --- a/booksrc/tutorial12.md +++ b/booksrc/tutorial12.md @@ -1,7 +1,7 @@ # Deref for the ErrorKind -Because ChainError implements Deref to &T, we can also match on `*e` instead of `e.kind()`. - +Because ChainError implements Deref to &T, we can also match on `*e` instead of `e.kind()` +or call a function with `&e` ~~~rust use crate::chainerror::*; {{#include ../examples/tutorial12.rs:2:}} diff --git a/examples/tutorial12.rs b/examples/tutorial12.rs index 38ff09e..a20fede 100644 --- a/examples/tutorial12.rs +++ b/examples/tutorial12.rs @@ -45,6 +45,15 @@ fn func1() -> ChainResult<(), Func1ErrorKind> { Ok(()) } +fn handle_func1errorkind(e: &Func1ErrorKind) { + match e { + Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), + Func1ErrorKind::IO(ref filename) => { + eprintln!("Main Error Report: func1 error reading '{}'", filename) + } + } +} + fn main() -> Result<(), Box> { if let Err(e) = func1() { match *e { @@ -54,6 +63,8 @@ fn main() -> Result<(), Box> { } } + handle_func1errorkind(&e); + if let Some(e) = e.find_chain_cause::() { eprintln!("\nError reported by Func2Error: {}", e) } diff --git a/src/lib.rs b/src/lib.rs index ca9d75e..b71e51d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,7 +263,7 @@ impl ChainError { self.iter().filter_map(Error::downcast_ref::).next() } - /// Find the first error cause of type ChainError, if any exists + /// Find the first error cause of type `ChainError`, if any exists /// /// Same as `find_cause`, but hides the `ChainError` implementation internals /// @@ -273,7 +273,7 @@ impl ChainError { /// // Instead of writing /// err.find_cause::>(); /// - /// // leave out the ChainError implementation detail + /// // leave out the ChainError implementation detail /// err.find_chain_cause::(); /// ~~~ pub fn find_chain_cause(&self) -> Option<&ChainError> { @@ -282,8 +282,24 @@ impl ChainError { .next() } - // FIXME: naming - fn find_chain_or_cause(&self) -> Option<&U> { + /// Find the first error cause of type `ChainError` or `U`, if any exists and return `U` + /// + /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError` implementation internals + /// + /// # Examples + /// + /// ~~~rust,ignore + /// // Instead of writing + /// err.find_cause::>(); + /// // and/or + /// err.find_chain_cause::(); + /// // and/or + /// err.find_cause::(); + /// + /// // leave out the ChainError implementation detail + /// err.find_chain_or_kind::(); + /// ~~~ + pub fn find_kind_or_cause(&self) -> Option<&U> { self.iter() .filter_map(|e| { e.downcast_ref::>().map(|e| e.kind()) @@ -353,6 +369,11 @@ impl ChainError { &self.kind } + /// Returns an Iterator over all error causes/sources + /// + /// # Example + /// + /// pub fn iter(&self) -> impl Iterator { ErrorIter { current: Some(self), @@ -378,7 +399,7 @@ impl std::ops::Deref for ChainError { type Target = T; fn deref(&self) -> &Self::Target { - self.kind() + &self.kind } } From 2f8c68b36de97e6cafa18350b9549b3ba3469cd9 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 7 Feb 2019 08:57:50 +0100 Subject: [PATCH 09/85] cargo fmt --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b71e51d..b238c67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -302,7 +302,8 @@ impl ChainError { pub fn find_kind_or_cause(&self) -> Option<&U> { self.iter() .filter_map(|e| { - e.downcast_ref::>().map(|e| e.kind()) + e.downcast_ref::>() + .map(|e| e.kind()) .or_else(|| e.downcast_ref::()) }) .next() From 8bd4ffca56169d9f2c20bf5e14a2688fd6bce5ae Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 4 Mar 2019 11:37:34 +0100 Subject: [PATCH 10/85] simplify macros --- src/lib.rs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b238c67..902c51a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -684,15 +684,9 @@ macro_rules! cherr { ( None, $fmt:expr, $($arg:tt)+ ) => ({ cherr!(None, format!($fmt, $($arg)+ )) }); - ( $e:ident, $k:expr ) => ({ - ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) - }); ( $e:path, $k:expr ) => ({ ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) }); - ( $e:ident, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!($e, format!($fmt, $($arg)+ )) - }); ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ cherr!($e, format!($fmt, $($arg)+ )) }); @@ -787,21 +781,12 @@ macro_rules! cherr { /// ~~~ #[macro_export] macro_rules! mstrerr { - ( $t:ident, $msg:expr ) => ({ - |e| cherr!(e, $t ($msg.to_string())) - }); ( $t:path, $msg:expr ) => ({ |e| cherr!(e, $t ($msg.to_string())) }); - ( $t:ident, $msg:expr, ) => ({ - |e| cherr!(e, $t ($msg.to_string())) - }); ( $t:path, $msg:expr, ) => ({ |e| cherr!(e, $t ($msg.to_string())) }); - ( $t:ident, $fmt:expr, $($arg:tt)+ ) => ({ - |e| cherr!(e, $t (format!($fmt, $($arg)+ ))) - }); ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ |e| cherr!(e, $t (format!($fmt, $($arg)+ ))) }); @@ -854,21 +839,12 @@ macro_rules! mstrerr { /// ~~~ #[macro_export] macro_rules! strerr { - ( $t:ident, $msg:expr ) => ({ - cherr!($t ($msg.to_string())) - }); ( $t:path, $msg:expr ) => ({ cherr!($t ($msg.to_string())) }); - ( $t:ident, $msg:expr, ) => ({ - cherr!($t ($msg.to_string())) - }); ( $t:path, $msg:expr, ) => ({ cherr!($t ($msg.to_string())) }); - ( $t:ident, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!($t (format!($fmt, $($arg)+ ))) - }); ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ cherr!($t (format!($fmt, $($arg)+ ))) }); From dfe97b34d636998b63a39a6eef4ea1b3ae1bb277 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 4 Mar 2019 11:38:11 +0100 Subject: [PATCH 11/85] add library chapter tutorial13 --- booksrc/SUMMARY.md | 1 + booksrc/tutorial13.md | 18 ++++++++++ examples/tutorial13.rs | 76 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 booksrc/tutorial13.md create mode 100644 examples/tutorial13.rs diff --git a/booksrc/SUMMARY.md b/booksrc/SUMMARY.md index 367577f..9c8d13a 100644 --- a/booksrc/SUMMARY.md +++ b/booksrc/SUMMARY.md @@ -14,5 +14,6 @@ - [ErrorKind to the rescue](tutorial10.md) - [Debug for the ErrorKind](tutorial11.md) - [Deref for the ErrorKind](tutorial12.md) +- [Writing a library](tutorial13.md) [The End](end.md) \ No newline at end of file diff --git a/booksrc/tutorial13.md b/booksrc/tutorial13.md new file mode 100644 index 0000000..2b3dde2 --- /dev/null +++ b/booksrc/tutorial13.md @@ -0,0 +1,18 @@ +# Writing a library + +I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `ChainError` +so you can tell your library users to use the `.kind()` method as `std::io::Error` does. + +If you later decide to make your own `Error` implementation, your library users don't +have to change much or anything. + +~~~rust +# #[allow(dead_code)] +# #[macro_use] +# pub mod chainerror { +{{#includecomment ../src/lib.rs}} +# } +pub mod mycrate { + use crate::chainerror::*; // omit the `crate::` part +{{#include ../examples/tutorial13.rs:3:}} +~~~ diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs new file mode 100644 index 0000000..fdc0939 --- /dev/null +++ b/examples/tutorial13.rs @@ -0,0 +1,76 @@ +pub mod mycrate { + use chainerror::*; + use std::io; + + fn do_some_io() -> std::result::Result<(), Box> { + Err(io::Error::from(io::ErrorKind::NotFound))?; + Ok(()) + } + + derive_str_cherr!(Func2Error); + + fn func2() -> std::result::Result<(), Box> { + let filename = "foo.txt"; + do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + Ok(()) + } + + #[derive(Debug)] + pub enum ErrorKind { + Func2, + IO(String), + } + + pub type Error = ChainError; + pub type Result = std::result::Result; + + impl ::std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + ErrorKind::Func2 => write!(f, "func1 error calling func2"), + ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + } + } + } + + pub fn func1() -> Result<()> { + func2().map_err(|e| cherr!(e, ErrorKind::Func2))?; + let filename = String::from("bar.txt"); + do_some_io().map_err(|e| cherr!(e, ErrorKind::IO(filename)))?; + Ok(()) + } +} + +fn main() -> Result<(), Box> { + use mycrate::func1; + use mycrate::ErrorKind; + use std::error::Error; + use std::io; + + if let Err(e) = func1() { + match e.kind() { + ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), + ErrorKind::IO(ref filename) => { + eprintln!("Main Error Report: func1 error reading '{}'", filename) + } + } + + eprintln!(); + let mut s : &Error = &e; + while let Some(c) = s.source() { + if let Some(ioerror) = c.downcast_ref::() { + eprintln!("caused by: std::io::Error: {}", ioerror); + match ioerror.kind() { + io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"), + _ => {} + } + } else { + eprintln!("caused by: {}", c); + } + s = c; + } + + eprintln!("\nDebug Error:\n{:?}", e); + } + Ok(()) +} From bc5cbd18f0f5ac34dc901ef0821cc12b0b4dc2b1 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 4 Mar 2019 11:38:35 +0100 Subject: [PATCH 12/85] simplify booksrc imports --- booksrc/tutorial10.md | 3 +-- booksrc/tutorial11.md | 3 +-- booksrc/tutorial12.md | 3 +-- booksrc/tutorial2.md | 3 +-- booksrc/tutorial3.md | 3 +-- booksrc/tutorial4.md | 3 +-- booksrc/tutorial5.md | 3 +-- booksrc/tutorial6.md | 3 +-- booksrc/tutorial7.md | 3 +-- booksrc/tutorial8.md | 3 +-- booksrc/tutorial9.md | 3 +-- 11 files changed, 11 insertions(+), 22 deletions(-) diff --git a/booksrc/tutorial10.md b/booksrc/tutorial10.md index 7feaf6e..eb92ff2 100644 --- a/booksrc/tutorial10.md +++ b/booksrc/tutorial10.md @@ -17,8 +17,7 @@ Also a nice `match` on `ChainError.kind()` is now possible, which returns `&T `&Func1ErrorKind` here. ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial10.rs:2:}} +{{#include ../examples/tutorial10.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial11.md b/booksrc/tutorial11.md index c679792..610051b 100644 --- a/booksrc/tutorial11.md +++ b/booksrc/tutorial11.md @@ -25,8 +25,7 @@ Also noteworthy is [custom_error](https://crates.io/crates/custom_error) to defi which can then be used with `chainerror`. ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial11.rs:2:}} +{{#include ../examples/tutorial11.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial12.md b/booksrc/tutorial12.md index 6373ca0..81ab142 100644 --- a/booksrc/tutorial12.md +++ b/booksrc/tutorial12.md @@ -3,8 +3,7 @@ Because ChainError implements Deref to &T, we can also match on `*e` instead of `e.kind()` or call a function with `&e` ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial12.rs:2:}} +{{#include ../examples/tutorial12.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial2.md b/booksrc/tutorial2.md index 86e2726..4629605 100644 --- a/booksrc/tutorial2.md +++ b/booksrc/tutorial2.md @@ -6,8 +6,7 @@ the `String` errors are now chained together. Press the play button in the upper right corner and see the nice debug output. ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial2.rs:2:}} +{{#include ../examples/tutorial2.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial3.md b/booksrc/tutorial3.md index b86e0ad..0abf07c 100644 --- a/booksrc/tutorial3.md +++ b/booksrc/tutorial3.md @@ -3,8 +3,7 @@ Now let's get more rust idiomatic by using `.map_err()`. ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial3.rs:2:}} +{{#include ../examples/tutorial3.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial4.md b/booksrc/tutorial4.md index 95a6fab..70331c0 100644 --- a/booksrc/tutorial4.md +++ b/booksrc/tutorial4.md @@ -9,8 +9,7 @@ more debug strings. `mstrerror!()` even understands `format!()` syntax like `println!()`. ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial4.rs:2:}} +{{#include ../examples/tutorial4.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial5.md b/booksrc/tutorial5.md index d98f09e..5595220 100644 --- a/booksrc/tutorial5.md +++ b/booksrc/tutorial5.md @@ -4,8 +4,7 @@ Sometimes you want to inspect the `source()` of an `Error`. `chainerror` implements `std::error::Error::source()`, so you can get the cause of an error. ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial5.rs:2:}} +{{#include ../examples/tutorial5.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial6.md b/booksrc/tutorial6.md index 8154874..1d4a64e 100644 --- a/booksrc/tutorial6.md +++ b/booksrc/tutorial6.md @@ -11,8 +11,7 @@ pub fn downcast_mut(&mut self) -> Option<&mut T> This is how it looks like, when using those: ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial6.rs:2:}} +{{#include ../examples/tutorial6.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial7.md b/booksrc/tutorial7.md index 4ddd17f..cf65d04 100644 --- a/booksrc/tutorial7.md +++ b/booksrc/tutorial7.md @@ -27,8 +27,7 @@ or to use `.root_cause()`, which of course can be of any type implementing `std: ~~~ ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial7.rs:2:}} +{{#include ../examples/tutorial7.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial8.md b/booksrc/tutorial8.md index ce123e1..ed03342 100644 --- a/booksrc/tutorial8.md +++ b/booksrc/tutorial8.md @@ -23,8 +23,7 @@ as a shortcut to hiding the `ChainError` implementation detail. ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial8.rs:2:}} +{{#include ../examples/tutorial8.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} diff --git a/booksrc/tutorial9.md b/booksrc/tutorial9.md index e9107ca..df2e292 100644 --- a/booksrc/tutorial9.md +++ b/booksrc/tutorial9.md @@ -25,8 +25,7 @@ but this is not valid rust code, so we end up doing it the hard way. In the next chapter, we will see, how to solve this more elegantly. ~~~rust -use crate::chainerror::*; -{{#include ../examples/tutorial9.rs:2:}} +{{#include ../examples/tutorial9.rs}} # #[allow(dead_code)] # mod chainerror { {{#includecomment ../src/lib.rs}} From deaaf4ce9fbe515c49b0529358206ff7c57d8b59 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 4 Mar 2019 11:38:57 +0100 Subject: [PATCH 13/85] Cargo.toml: 0.4.3 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 99e0ac4..1ca8330 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.4.2" +version = "0.4.3" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" From 15f983ce55fe27279285fe633169e80f52d5666c Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 5 Mar 2019 13:28:53 +0100 Subject: [PATCH 14/85] doc spelling --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 902c51a..91b1bca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,8 +144,8 @@ //! assert!(e.root_cause().is_some()); //! //! if let Some(e) = e.root_cause() { -//! let ioerror = e.downcast_ref::().unwrap(); -//! eprintln!("\nThe root cause was: std::io::Error: {:#?}", ioerror); +//! let io_error = e.downcast_ref::().unwrap(); +//! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); //! } //! //! #[cfg(not(windows))] From 490eaa474b48d0270377d5a71b113538d2e5511b Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 5 Mar 2019 18:00:15 +0100 Subject: [PATCH 15/85] deny more lints and add more docs --- src/lib.rs | 97 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 91b1bca..2a53b0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ //! //! # Examples //! -//! ~~~rust +//! ```rust //! use chainerror::*; //! use std::error::Error; //! use std::io; @@ -65,10 +65,10 @@ //! # unreachable!(); //! # } //! } -//! ~~~ +//! ``` //! //! -//! ~~~rust +//! ```rust //! use chainerror::*; //! use std::error::Error; //! use std::io; @@ -165,7 +165,32 @@ //! # unreachable!(); //! # } //! } -//! ~~~ +//! ``` + +#![deny( + warnings, + absolute_paths_not_starting_with_crate, + deprecated_in_future, + keyword_idents, + macro_use_extern_crate, + missing_debug_implementations, + missing_docs, + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_results, + unused_labels, + unused_lifetimes, + unstable_features, + unreachable_pub, + future_incompatible, + missing_copy_implementations, + missing_doc_code_examples, + rust_2018_idioms, + rust_2018_compatibility +)] use std::any::TypeId; use std::error::Error; @@ -216,7 +241,7 @@ impl ChainError { /// /// # Examples /// - /// ~~~rust + /// ```rust /// # use crate::chainerror::*; /// # use std::error::Error; /// # use std::io; @@ -258,7 +283,7 @@ impl ChainError { /// # unreachable!(); /// # } /// } - /// ~~~ + /// ``` pub fn find_cause(&self) -> Option<&U> { self.iter().filter_map(Error::downcast_ref::).next() } @@ -269,13 +294,16 @@ impl ChainError { /// /// # Examples /// - /// ~~~rust,ignore + /// ```rust + /// # use chainerror::{ChainError, derive_str_cherr}; + /// # derive_str_cherr!(FooError); + /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing /// err.find_cause::>(); /// /// // leave out the ChainError implementation detail /// err.find_chain_cause::(); - /// ~~~ + /// ``` pub fn find_chain_cause(&self) -> Option<&ChainError> { self.iter() .filter_map(Error::downcast_ref::>) @@ -288,7 +316,10 @@ impl ChainError { /// /// # Examples /// - /// ~~~rust,ignore + /// ```rust + /// # use chainerror::{ChainError, derive_str_cherr}; + /// # derive_str_cherr!(FooErrorKind); + /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing /// err.find_cause::>(); /// // and/or @@ -297,8 +328,8 @@ impl ChainError { /// err.find_cause::(); /// /// // leave out the ChainError implementation detail - /// err.find_chain_or_kind::(); - /// ~~~ + /// err.find_kind_or_cause::(); + /// ``` pub fn find_kind_or_cause(&self) -> Option<&U> { self.iter() .filter_map(|e| { @@ -313,7 +344,7 @@ impl ChainError { /// /// # Examples /// - /// ~~~rust + /// ```rust /// # use crate::chainerror::*; /// # use std::error::Error; /// # use std::io; @@ -365,7 +396,7 @@ impl ChainError { /// # unreachable!(); /// # } /// } - /// ~~~ + /// ``` pub fn kind(&self) -> &T { &self.kind } @@ -423,6 +454,7 @@ impl ChainErrorDown for ChainError { if self.is_chain::() { #[allow(clippy::cast_ptr_alignment)] unsafe { + #[allow(trivial_casts)] Some(&*(self as *const dyn Error as *const &ChainError)) } } else { @@ -434,6 +466,7 @@ impl ChainErrorDown for ChainError { if self.is_chain::() { #[allow(clippy::cast_ptr_alignment)] unsafe { + #[allow(trivial_casts)] Some(&mut *(self as *mut dyn Error as *mut &mut ChainError)) } } else { @@ -503,7 +536,7 @@ impl Error for &mut ChainError { } impl Display for ChainError { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{}", self.kind)?; #[cfg(feature = "display-cause")] @@ -518,7 +551,7 @@ impl Display for ChainError { } impl Debug for ChainError { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { #[cfg(not(feature = "no-fileline"))] { if let Some(o) = self.occurrence { @@ -543,11 +576,15 @@ impl Debug for ChainError { } } +/// `ChainErrorFrom` is similar to `From` pub trait ChainErrorFrom: Sized { + /// similar to From::from() fn chain_error_from(_: T, line_filename: Option<(u32, &'static str)>) -> ChainError; } +/// `IntoChainError` is similar to `Into` pub trait IntoChainError: Sized { + /// similar to Into::into() fn into_chain_error(self, line_filename: Option<(u32, &'static str)>) -> ChainError; } @@ -571,6 +608,9 @@ where } } +/// map into `ChainError` with `IntoChainError` +/// +/// adds `line!()` and `file!()` information #[macro_export] macro_rules! minto_cherr { ( ) => { @@ -578,6 +618,9 @@ macro_rules! minto_cherr { }; } +/// into `ChainError` with `IntoChainError` +/// +/// adds `line!()` and `file!()` information #[macro_export] macro_rules! into_cherr { ( $t:expr ) => { @@ -590,7 +633,7 @@ macro_rules! into_cherr { /// # Examples /// /// Create a new ChainError, where `FooError` must implement `Display` and `Debug`. -/// ~~~rust +/// ```rust /// # use chainerror::*; /// # /// # #[derive(Debug)] @@ -627,11 +670,11 @@ macro_rules! into_cherr { /// # _ => panic!(), /// # } /// # } -/// ~~~ +/// ``` /// /// Additionally an error cause can be added. /// -/// ~~~rust +/// ```rust /// # use chainerror::*; /// # use std::io; /// # use std::error::Error; @@ -669,7 +712,7 @@ macro_rules! into_cherr { /// # _ => panic!(), /// # } /// # } -/// ~~~ +/// ``` #[macro_export] macro_rules! cherr { ( $k:expr ) => ({ @@ -697,7 +740,7 @@ macro_rules! cherr { /// /// # Examples /// -/// ~~~rust +/// ```rust /// # use crate::chainerror::*; /// # use std::error::Error; /// # use std::io; @@ -735,12 +778,12 @@ macro_rules! cherr { /// # unreachable!(); /// # } /// # } -/// ~~~ +/// ``` /// /// `mstrerr!()` can also be used to map a new `ChainError`, where T was defined with /// `derive_str_cherr!(T)` /// -/// ~~~rust +/// ```rust /// # use crate::chainerror::*; /// # use std::error::Error; /// # use std::io; @@ -778,7 +821,7 @@ macro_rules! cherr { /// # unreachable!(); /// # } /// # } -/// ~~~ +/// ``` #[macro_export] macro_rules! mstrerr { ( $t:path, $msg:expr ) => ({ @@ -805,7 +848,7 @@ macro_rules! mstrerr { /// /// # Examples /// -/// ~~~rust +/// ```rust /// # use crate::chainerror::*; /// # use std::error::Error; /// # use std::result::Result; @@ -836,7 +879,7 @@ macro_rules! mstrerr { /// # unreachable!(); /// # } /// # } -/// ~~~ +/// ``` #[macro_export] macro_rules! strerr { ( $t:path, $msg:expr ) => ({ @@ -863,7 +906,7 @@ macro_rules! strerr { /// /// # Examples /// -/// ~~~rust +/// ```rust /// # use crate::chainerror::*; /// # use std::error::Error; /// # use std::io; @@ -901,7 +944,7 @@ macro_rules! strerr { /// # unreachable!(); /// # } /// # } -/// ~~~ +/// ``` #[macro_export] macro_rules! derive_str_cherr { ($e:ident) => { From c014f1a9220b17c3e6a480ed079dc7ea5012bef7 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 6 Mar 2019 08:57:16 +0100 Subject: [PATCH 16/85] cargo fmt and doc format --- examples/tutorial13.rs | 2 +- src/lib.rs | 25 +++---------------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index fdc0939..6a06694 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -56,7 +56,7 @@ fn main() -> Result<(), Box> { } eprintln!(); - let mut s : &Error = &e; + let mut s: &Error = &e; while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::() { eprintln!("caused by: std::io::Error: {}", ioerror); diff --git a/src/lib.rs b/src/lib.rs index 2a53b0f..1ba8dcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,7 +246,6 @@ impl ChainError { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; - /// # /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) @@ -349,7 +348,6 @@ impl ChainError { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; - /// # /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) @@ -388,7 +386,7 @@ impl ChainError { /// fn main() { /// if let Err(e) = func1() { /// match e.kind() { - /// Func1ErrorKind::Func2 => {}, + /// Func1ErrorKind::Func2 => {} /// Func1ErrorKind::IO(filename) => panic!(), /// } /// } @@ -635,13 +633,11 @@ macro_rules! into_cherr { /// Create a new ChainError, where `FooError` must implement `Display` and `Debug`. /// ```rust /// # use chainerror::*; -/// # /// # #[derive(Debug)] /// enum FooError { /// Bar, /// Baz(&'static str), /// } -/// # /// # impl ::std::fmt::Display for FooError { /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { /// # match self { @@ -663,10 +659,9 @@ macro_rules! into_cherr { /// } /// Ok(()) /// } -/// # /// # pub fn main() { /// # match func().unwrap_err().kind() { -/// # FooError::Baz(s) if s == &"Error" => {}, +/// # FooError::Baz(s) if s == &"Error" => {} /// # _ => panic!(), /// # } /// # } @@ -678,13 +673,11 @@ macro_rules! into_cherr { /// # use chainerror::*; /// # use std::io; /// # use std::error::Error; -/// # /// # #[derive(Debug)] /// # enum FooError { /// # Bar, /// # Baz(&'static str), /// # } -/// # /// # impl ::std::fmt::Display for FooError { /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { /// # match self { @@ -693,7 +686,6 @@ macro_rules! into_cherr { /// # } /// # } /// # } -/// # /// fn do_some_stuff() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) @@ -705,10 +697,9 @@ macro_rules! into_cherr { /// )?; /// Ok(()) /// } -/// # /// # pub fn main() { /// # match func().unwrap_err().kind() { -/// # FooError::Baz(s) if s == &"Error" => {}, +/// # FooError::Baz(s) if s == &"Error" => {} /// # _ => panic!(), /// # } /// # } @@ -745,12 +736,10 @@ macro_rules! cherr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # /// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// # /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; @@ -788,12 +777,10 @@ macro_rules! cherr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # /// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// # /// derive_str_cherr!(Func2Error); /// /// fn func2() -> Result<(), Box> { @@ -808,7 +795,6 @@ macro_rules! cherr { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } -/// # /// # fn main() { /// # if let Err(e) = func1() { /// # if let Some(f1err) = e.downcast_chain_ref::() { @@ -852,7 +838,6 @@ macro_rules! mstrerr { /// # use crate::chainerror::*; /// # use std::error::Error; /// # use std::result::Result; -/// # /// derive_str_cherr!(Func2Error); /// /// fn func2() -> ChainResult<(), Func2Error> { @@ -866,7 +851,6 @@ macro_rules! mstrerr { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } -/// # /// # fn main() { /// # if let Err(e) = func1() { /// # if let Some(f1err) = e.downcast_chain_ref::() { @@ -911,12 +895,10 @@ macro_rules! strerr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # /// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// # /// derive_str_cherr!(Func2Error); /// /// fn func2() -> ChainResult<(), Func2Error> { @@ -931,7 +913,6 @@ macro_rules! strerr { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } -/// # /// # fn main() { /// # if let Err(e) = func1() { /// # if let Some(f1err) = e.downcast_chain_ref::() { From 51af6da378297e88597ba1bd04e582829c9309cd Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 7 Mar 2019 16:50:16 +0100 Subject: [PATCH 17/85] add derive_err_kind() and more docs --- Cargo.toml | 2 +- booksrc/SUMMARY.md | 1 + booksrc/tutorial14.md | 9 ++ examples/tutorial13.rs | 7 +- examples/tutorial14.rs | 217 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 115 +++++++++++++++++++--- 6 files changed, 331 insertions(+), 20 deletions(-) create mode 100644 booksrc/tutorial14.md create mode 100644 examples/tutorial14.rs diff --git a/Cargo.toml b/Cargo.toml index 1ca8330..d07ba7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.4.3" +version = "0.4.4" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/booksrc/SUMMARY.md b/booksrc/SUMMARY.md index 9c8d13a..91ef467 100644 --- a/booksrc/SUMMARY.md +++ b/booksrc/SUMMARY.md @@ -15,5 +15,6 @@ - [Debug for the ErrorKind](tutorial11.md) - [Deref for the ErrorKind](tutorial12.md) - [Writing a library](tutorial13.md) +- [Going back to std](tutorial14.md) [The End](end.md) \ No newline at end of file diff --git a/booksrc/tutorial14.md b/booksrc/tutorial14.md new file mode 100644 index 0000000..1bdb97b --- /dev/null +++ b/booksrc/tutorial14.md @@ -0,0 +1,9 @@ +# Going back to std + +Not using `chainerror` and going full `std` would look like this: + +Btw, the code size is bigger than using `chainerror` :-) + +~~~rust +{{#include ../examples/tutorial14.rs}} +~~~ diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 6a06694..6c30358 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -21,11 +21,12 @@ pub mod mycrate { IO(String), } - pub type Error = ChainError; + derive_err_kind!(Error, ErrorKind); + pub type Result = std::result::Result; - impl ::std::fmt::Display for ErrorKind { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + impl std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { match self { ErrorKind::Func2 => write!(f, "func1 error calling func2"), ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), diff --git a/examples/tutorial14.rs b/examples/tutorial14.rs new file mode 100644 index 0000000..5ccd7ce --- /dev/null +++ b/examples/tutorial14.rs @@ -0,0 +1,217 @@ +pub mod mycrate { + use std::error::Error as StdError; + + use func2mod::{do_some_io, func2}; + + pub mod func2mod { + use std::error::Error as StdError; + use std::io; + + pub enum ErrorKind { + IO(String), + } + + impl std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { + match self { + ErrorKind::IO(s) => std::fmt::Display::fmt(s, f), + } + } + } + + impl std::fmt::Debug for ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { + match self { + ErrorKind::IO(s) => std::fmt::Display::fmt(s, f), + } + } + } + + macro_rules! mcherr { + ( $k:expr ) => {{ + |e| { + Error( + $k, + Some(Box::from(e)), + Some(concat!(file!(), ":", line!(), ": ")), + ) + } + }}; + } + + pub struct Error( + ErrorKind, + Option>, + Option<&'static str>, + ); + + impl Error { + pub fn kind(&self) -> &ErrorKind { + &self.0 + } + } + + impl From for Error { + fn from(e: ErrorKind) -> Self { + Error(e, None, None) + } + } + + impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.1.as_ref().map(|e| e.as_ref()) + } + } + + impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } + } + + impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if let Some(ref o) = self.2 { + std::fmt::Display::fmt(o, f)?; + } + + std::fmt::Debug::fmt(&self.0, f)?; + + if let Some(e) = self.source() { + std::fmt::Display::fmt("\nCaused by:\n", f)?; + std::fmt::Debug::fmt(&e, f)?; + } + Ok(()) + } + } + + pub fn do_some_io() -> std::result::Result<(), Box> { + Err(io::Error::from(io::ErrorKind::NotFound))?; + Ok(()) + } + + pub fn func2() -> std::result::Result<(), Error> { + let filename = "foo.txt"; + do_some_io().map_err(mcherr!(ErrorKind::IO(format!( + "Error reading '{}'", + filename + ))))?; + Ok(()) + } + } + + #[derive(Debug)] + pub enum ErrorKind { + Func2, + IO(String), + } + + impl std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { + match self { + ErrorKind::Func2 => write!(f, "func1 error calling func2"), + ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + } + } + } + + macro_rules! mcherr { + ( $k:expr ) => {{ + |e| { + Error( + $k, + Some(Box::from(e)), + Some(concat!(file!(), ":", line!(), ": ")), + ) + } + }}; + } + + pub struct Error( + ErrorKind, + Option>, + Option<&'static str>, + ); + + impl Error { + pub fn kind(&self) -> &ErrorKind { + &self.0 + } + } + + impl From for Error { + fn from(e: ErrorKind) -> Self { + Error(e, None, None) + } + } + + impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.1.as_ref().map(|e| e.as_ref()) + } + } + + impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } + } + + impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if let Some(ref o) = self.2 { + std::fmt::Display::fmt(o, f)?; + } + + std::fmt::Debug::fmt(&self.0, f)?; + if let Some(e) = self.source() { + std::fmt::Display::fmt("\nCaused by:\n", f)?; + std::fmt::Debug::fmt(&e, f)?; + } + Ok(()) + } + } + + pub type Result = std::result::Result; + + pub fn func1() -> Result<()> { + func2().map_err(mcherr!(ErrorKind::Func2))?; + let filename = String::from("bar.txt"); + do_some_io().map_err(mcherr!(ErrorKind::IO(filename)))?; + Ok(()) + } +} + +fn main() -> Result<(), Box> { + use mycrate::func1; + use mycrate::ErrorKind; + use std::error::Error; + use std::io; + + if let Err(e) = func1() { + match e.kind() { + ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), + ErrorKind::IO(ref filename) => { + eprintln!("Main Error Report: func1 error reading '{}'", filename) + } + } + + eprintln!(); + let mut s: &Error = &e; + while let Some(c) = s.source() { + if let Some(ioerror) = c.downcast_ref::() { + eprintln!("caused by: std::io::Error: {}", ioerror); + match ioerror.kind() { + io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"), + _ => {} + } + } else { + eprintln!("caused by: {}", c); + } + s = c; + } + + eprintln!("\nDebug Error:\n{:?}", e); + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 1ba8dcb..dd1471c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -199,7 +199,7 @@ use std::fmt::{Debug, Display, Formatter, Result}; /// chains an inner error kind `T` with a causing error pub struct ChainError { #[cfg(not(feature = "no-fileline"))] - occurrence: Option<(u32, &'static str)>, + occurrence: Option<&'static str>, kind: T, error_cause: Option>, } @@ -210,10 +210,11 @@ pub type ChainResult = std::result::Result>; impl ChainError { #[cfg(not(feature = "no-fileline"))] /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] pub fn new( kind: T, error_cause: Option>, - occurrence: Option<(u32, &'static str)>, + occurrence: Option<&'static str>, ) -> Self { Self { occurrence, @@ -224,10 +225,11 @@ impl ChainError { #[cfg(feature = "no-fileline")] /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] pub fn new( kind: T, error_cause: Option>, - _occurrence: Option<(u32, &'static str)>, + _occurrence: Option<&'static str>, ) -> Self { Self { kind, error_cause } } @@ -283,6 +285,7 @@ impl ChainError { /// # } /// } /// ``` + #[inline] pub fn find_cause(&self) -> Option<&U> { self.iter().filter_map(Error::downcast_ref::).next() } @@ -303,6 +306,7 @@ impl ChainError { /// // leave out the ChainError implementation detail /// err.find_chain_cause::(); /// ``` + #[inline] pub fn find_chain_cause(&self) -> Option<&ChainError> { self.iter() .filter_map(Error::downcast_ref::>) @@ -329,6 +333,7 @@ impl ChainError { /// // leave out the ChainError implementation detail /// err.find_kind_or_cause::(); /// ``` + #[inline] pub fn find_kind_or_cause(&self) -> Option<&U> { self.iter() .filter_map(|e| { @@ -395,6 +400,7 @@ impl ChainError { /// # } /// } /// ``` + #[inline] pub fn kind(&self) -> &T { &self.kind } @@ -404,6 +410,7 @@ impl ChainError { /// # Example /// /// + #[inline] pub fn iter(&self) -> impl Iterator { ErrorIter { current: Some(self), @@ -418,6 +425,7 @@ struct ErrorIter<'a> { impl<'a> Iterator for ErrorIter<'a> { type Item = &'a (dyn Error + 'static); + #[inline] fn next(&mut self) -> Option { let current = self.current; self.current = self.current.and_then(Error::source); @@ -428,6 +436,7 @@ impl<'a> Iterator for ErrorIter<'a> { impl std::ops::Deref for ChainError { type Target = T; + #[inline] fn deref(&self) -> &Self::Target { &self.kind } @@ -444,10 +453,12 @@ pub trait ChainErrorDown { } impl ChainErrorDown for ChainError { + #[inline] fn is_chain(&self) -> bool { TypeId::of::() == TypeId::of::() } + #[inline] fn downcast_chain_ref(&self) -> Option<&ChainError> { if self.is_chain::() { #[allow(clippy::cast_ptr_alignment)] @@ -460,6 +471,7 @@ impl ChainErrorDown for ChainError { } } + #[inline] fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { if self.is_chain::() { #[allow(clippy::cast_ptr_alignment)] @@ -474,66 +486,79 @@ impl ChainErrorDown for ChainError { } impl ChainErrorDown for dyn Error + 'static { + #[inline] fn is_chain(&self) -> bool { self.is::>() } + #[inline] fn downcast_chain_ref(&self) -> Option<&ChainError> { self.downcast_ref::>() } + #[inline] fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } } impl ChainErrorDown for dyn Error + 'static + Send { + #[inline] fn is_chain(&self) -> bool { self.is::>() } + #[inline] fn downcast_chain_ref(&self) -> Option<&ChainError> { self.downcast_ref::>() } + #[inline] fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } } impl ChainErrorDown for dyn Error + 'static + Send + Sync { + #[inline] fn is_chain(&self) -> bool { self.is::>() } + #[inline] fn downcast_chain_ref(&self) -> Option<&ChainError> { self.downcast_ref::>() } + #[inline] fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } } impl Error for ChainError { + #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { self.error_cause.as_ref().map(|e| e.as_ref()) } } impl Error for &ChainError { + #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { self.error_cause.as_ref().map(|e| e.as_ref()) } } impl Error for &mut ChainError { + #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { self.error_cause.as_ref().map(|e| e.as_ref()) } } impl Display for ChainError { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{}", self.kind)?; @@ -549,11 +574,12 @@ impl Display for ChainError { } impl Debug for ChainError { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { #[cfg(not(feature = "no-fileline"))] { - if let Some(o) = self.occurrence { - write!(f, "{}:{}: ", o.1, o.0)?; + if let Some(ref o) = self.occurrence { + Display::fmt(o, f)?; } } @@ -577,20 +603,21 @@ impl Debug for ChainError { /// `ChainErrorFrom` is similar to `From` pub trait ChainErrorFrom: Sized { /// similar to From::from() - fn chain_error_from(_: T, line_filename: Option<(u32, &'static str)>) -> ChainError; + fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError; } /// `IntoChainError` is similar to `Into` pub trait IntoChainError: Sized { /// similar to Into::into() - fn into_chain_error(self, line_filename: Option<(u32, &'static str)>) -> ChainError; + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError; } impl IntoChainError for T where U: ChainErrorFrom, { - fn into_chain_error(self, line_filename: Option<(u32, &'static str)>) -> ChainError { + #[inline] + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError { U::chain_error_from(self, line_filename) } } @@ -600,7 +627,8 @@ where T: Into, U: 'static + Display + Debug, { - fn chain_error_from(t: T, line_filename: Option<(u32, &'static str)>) -> ChainError { + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError { let e: U = t.into(); ChainError::<_>::new(e, None, line_filename) } @@ -612,7 +640,7 @@ where #[macro_export] macro_rules! minto_cherr { ( ) => { - |e| e.into_chain_error(Some((line!(), file!()))) + |e| ChainErrorFrom::chain_error_from(e, Some(concat!(file!(), ":", line!(), ": "))) }; } @@ -622,7 +650,7 @@ macro_rules! minto_cherr { #[macro_export] macro_rules! into_cherr { ( $t:expr ) => { - $t.into_chain_error(Some((line!(), file!()))) + ChainErrorFrom::chain_error_from($t, Some(concat!(file!(), ":", line!(), ": "))) }; } @@ -707,10 +735,10 @@ macro_rules! into_cherr { #[macro_export] macro_rules! cherr { ( $k:expr ) => ({ - ChainError::<_>::new($k, None, Some((line!(), file!()))) + ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) }); ( None, $k:expr ) => ({ - ChainError::<_>::new($k, None, Some((line!(), file!()))) + ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) }); ( None, $fmt:expr, $($arg:tt)+ ) => ({ cherr!(None, format!($fmt, $($arg)+ )) @@ -719,7 +747,7 @@ macro_rules! cherr { cherr!(None, format!($fmt, $($arg)+ )) }); ( $e:path, $k:expr ) => ({ - ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) + ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) }); ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ cherr!($e, format!($fmt, $($arg)+ )) @@ -727,6 +755,14 @@ macro_rules! cherr { } +/// shortcut for |e| cherr!(e, $k) +#[macro_export] +macro_rules! mcherr { + ( $k:expr ) => {{ + |e| cherr!(e, $k) + }}; +} + /// Convenience macro for `|e| cherr!(e, format!(…))` /// /// # Examples @@ -756,9 +792,9 @@ macro_rules! cherr { /// # #[cfg(not(windows))] /// # assert_eq!( /// # format!("\n{:?}\n", e), r#" -/// # src/lib.rs:20: func1 error +/// # src/lib.rs:18: func1 error /// # Caused by: -/// # src/lib.rs:15: Error reading 'foo.txt' +/// # src/lib.rs:13: Error reading 'foo.txt' /// # Caused by: /// # Kind(NotFound) /// # "# @@ -929,6 +965,7 @@ macro_rules! strerr { #[macro_export] macro_rules! derive_str_cherr { ($e:ident) => { + #[derive(Clone)] pub struct $e(pub String); impl ::std::fmt::Display for $e { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { @@ -943,3 +980,49 @@ macro_rules! derive_str_cherr { impl ::std::error::Error for $e {} }; } + +/// Derive an Error, which wraps ChainError and implements a kind() method +/// +/// e.kind() returns the kind +#[macro_export] +macro_rules! derive_err_kind { + ($e:ident, $k:ident) => { + pub struct $e(ChainError<$k>); + + impl $e { + pub fn kind(&self) -> &$k { + self.0.kind() + } + } + + impl From<$k> for $e { + fn from(e: $k) -> Self { + $e(ChainError::new(e, None, None)) + } + } + + impl From> for $e { + fn from(e: ChainError<$k>) -> Self { + $e(e) + } + } + + impl std::error::Error for $e { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } + } + + impl std::fmt::Display for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } + } + + impl std::fmt::Debug for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } + } + }; +} From a558c35ba4d97ddfcb12718f6ac640292f13cfa2 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 12 Mar 2019 16:43:53 +0100 Subject: [PATCH 18/85] make ChainError Send + Sync --- README.md | 4 ++-- booksrc/tutorial10.md | 2 +- booksrc/tutorial2.md | 2 +- booksrc/tutorial9.md | 2 +- examples/example.rs | 4 ++-- examples/tutorial1.rs | 8 +++---- examples/tutorial10.rs | 6 ++--- examples/tutorial11.rs | 6 ++--- examples/tutorial12.rs | 6 ++--- examples/tutorial13.rs | 4 ++-- examples/tutorial2.rs | 8 +++---- examples/tutorial3.rs | 8 +++---- examples/tutorial4.rs | 8 +++---- examples/tutorial5.rs | 8 +++---- examples/tutorial6.rs | 10 ++++---- examples/tutorial7.rs | 8 +++---- examples/tutorial8.rs | 8 +++---- examples/tutorial9.rs | 8 +++---- src/lib.rs | 53 +++++++++++++++++++++--------------------- tests/test_iter.rs | 6 ++--- 20 files changed, 84 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index 71a829d..9252c1d 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,12 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func3() -> Result<(), Box> { +fn func3() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) diff --git a/booksrc/tutorial10.md b/booksrc/tutorial10.md index eb92ff2..84fc436 100644 --- a/booksrc/tutorial10.md +++ b/booksrc/tutorial10.md @@ -8,7 +8,7 @@ a `std::error::Error`. Not using `String` errors anymore, the `cherr!()` macro seen in the beginning of the tutorial has to be used again. -Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box>` and we can +Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box>` and we can use `ChainResult<(), Func1ErrorKind>`. In `main` we can now directly use the methods of `ChainError` without downcasting the error first. diff --git a/booksrc/tutorial2.md b/booksrc/tutorial2.md index 4629605..e217291 100644 --- a/booksrc/tutorial2.md +++ b/booksrc/tutorial2.md @@ -24,7 +24,7 @@ along with the filename (`file!()`) and line number (`line!()`) and returns `newerror`. `Err()?` then returns the inner error applying `.into()`, so that we -again have a `Err(Box)` as a result. +again have a `Err(Box)` as a result. The `Debug` implementation of `ChainError` (which is returned by `cherr!()`) prints the `Debug` of `T` prefixed with the stored filename and line number. diff --git a/booksrc/tutorial9.md b/booksrc/tutorial9.md index df2e292..4027cdc 100644 --- a/booksrc/tutorial9.md +++ b/booksrc/tutorial9.md @@ -7,7 +7,7 @@ In this example `func1()` can return either `Func1ErrorFunc2` or `Func1ErrorIO`. We might want to `match` on `func1()` with something like: ~~~rust,ignore -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { match func1() { Err(e) if let Some(s) = e.downcast_chain_ref::() => eprintln!("Func1ErrorIO:\n{:?}", s), diff --git a/examples/example.rs b/examples/example.rs index 3d97f70..8aadc8d 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -3,12 +3,12 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func3() -> Result<(), Box> { +fn func3() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) diff --git a/examples/tutorial1.rs b/examples/tutorial1.rs index 87778d9..d296a92 100644 --- a/examples/tutorial1.rs +++ b/examples/tutorial1.rs @@ -2,25 +2,25 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { if let Err(_) = do_some_io() { Err("func2 error")?; } Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { if let Err(_) = func2() { Err("func1 error")?; } Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { func1() } diff --git a/examples/tutorial10.rs b/examples/tutorial10.rs index b22d0b7..15eae31 100644 --- a/examples/tutorial10.rs +++ b/examples/tutorial10.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -39,7 +39,7 @@ fn func1() -> ChainResult<(), Func1ErrorKind> { Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { match e.kind() { Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), diff --git a/examples/tutorial11.rs b/examples/tutorial11.rs index 46cf00c..00e3d74 100644 --- a/examples/tutorial11.rs +++ b/examples/tutorial11.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -45,7 +45,7 @@ fn func1() -> ChainResult<(), Func1ErrorKind> { Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { match e.kind() { Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), diff --git a/examples/tutorial12.rs b/examples/tutorial12.rs index a20fede..8cdc8aa 100644 --- a/examples/tutorial12.rs +++ b/examples/tutorial12.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -54,7 +54,7 @@ fn handle_func1errorkind(e: &Func1ErrorKind) { } } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { match *e { Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 6c30358..9cfe090 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -2,14 +2,14 @@ pub mod mycrate { use chainerror::*; use std::io; - fn do_some_io() -> std::result::Result<(), Box> { + fn do_some_io() -> std::result::Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); - fn func2() -> std::result::Result<(), Box> { + fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) diff --git a/examples/tutorial2.rs b/examples/tutorial2.rs index d75f0a4..349c5cd 100644 --- a/examples/tutorial2.rs +++ b/examples/tutorial2.rs @@ -4,25 +4,25 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { if let Err(e) = do_some_io() { Err(cherr!(e, "func2 error"))?; } Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { if let Err(e) = func2() { Err(cherr!(e, "func1 error"))?; } Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { func1() } diff --git a/examples/tutorial3.rs b/examples/tutorial3.rs index 6915a62..cdbffed 100644 --- a/examples/tutorial3.rs +++ b/examples/tutorial3.rs @@ -4,22 +4,22 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { do_some_io().map_err(|e| cherr!(e, "func2 error"))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(|e| cherr!(e, "func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{:?}", e); } diff --git a/examples/tutorial4.rs b/examples/tutorial4.rs index de51745..d037215 100644 --- a/examples/tutorial4.rs +++ b/examples/tutorial4.rs @@ -3,23 +3,23 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{:?}", e); } diff --git a/examples/tutorial5.rs b/examples/tutorial5.rs index e6f36f3..4d673e3 100644 --- a/examples/tutorial5.rs +++ b/examples/tutorial5.rs @@ -3,18 +3,18 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { if let Err(e) = func2() { if let Some(s) = e.source() { eprintln!("func2 failed because of '{}'", s); @@ -24,7 +24,7 @@ fn func1() -> Result<(), Box> { Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{}", e); } diff --git a/examples/tutorial6.rs b/examples/tutorial6.rs index aca48b4..22e0e78 100644 --- a/examples/tutorial6.rs +++ b/examples/tutorial6.rs @@ -3,26 +3,26 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("Error: {}", e); - let mut s = e.as_ref(); + let mut s : &(dyn Error) = e.as_ref(); while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::() { eprintln!("caused by: std::io::Error: {}", ioerror); diff --git a/examples/tutorial7.rs b/examples/tutorial7.rs index 3cb3d2c..bf09449 100644 --- a/examples/tutorial7.rs +++ b/examples/tutorial7.rs @@ -3,23 +3,23 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("Error: {}", e); if let Some(s) = e.downcast_chain_ref::() { diff --git a/examples/tutorial8.rs b/examples/tutorial8.rs index 831e6ed..be92df7 100644 --- a/examples/tutorial8.rs +++ b/examples/tutorial8.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -18,12 +18,12 @@ fn func2() -> Result<(), Box> { derive_str_cherr!(Func1Error); -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!(Func1Error, "func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { if let Some(f1err) = e.downcast_chain_ref::() { eprintln!("Func1Error: {}", f1err); diff --git a/examples/tutorial9.rs b/examples/tutorial9.rs index f1954d3..d9436f4 100644 --- a/examples/tutorial9.rs +++ b/examples/tutorial9.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -19,14 +19,14 @@ fn func2() -> Result<(), Box> { derive_str_cherr!(Func1ErrorFunc2); derive_str_cherr!(Func1ErrorIO); -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!(Func1ErrorFunc2, "func1 error calling func2"))?; let filename = "bar.txt"; do_some_io().map_err(mstrerr!(Func1ErrorIO, "Error reading '{}'", filename))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { if let Some(s) = e.downcast_ref::>() { eprintln!("Func1ErrorIO:\n{:?}", s); diff --git a/src/lib.rs b/src/lib.rs index dd1471c..2107335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,18 +32,18 @@ //! use std::io; //! use std::result::Result; //! -//! fn do_some_io() -> Result<(), Box> { +//! fn do_some_io() -> Result<(), Box> { //! Err(io::Error::from(io::ErrorKind::NotFound))?; //! Ok(()) //! } //! -//! fn func2() -> Result<(), Box> { +//! fn func2() -> Result<(), Box> { //! let filename = "foo.txt"; //! do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; //! Ok(()) //! } //! -//! fn func1() -> Result<(), Box> { +//! fn func1() -> Result<(), Box> { //! func2().map_err(mstrerr!("func1 error"))?; //! Ok(()) //! } @@ -74,12 +74,12 @@ //! use std::io; //! use std::result::Result; //! -//! fn do_some_io() -> Result<(), Box> { +//! fn do_some_io() -> Result<(), Box> { //! Err(io::Error::from(io::ErrorKind::NotFound))?; //! Ok(()) //! } //! -//! fn func3() -> Result<(), Box> { +//! fn func3() -> Result<(), Box> { //! let filename = "foo.txt"; //! do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; //! Ok(()) @@ -175,7 +175,6 @@ macro_use_extern_crate, missing_debug_implementations, missing_docs, - trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, @@ -201,7 +200,7 @@ pub struct ChainError { #[cfg(not(feature = "no-fileline"))] occurrence: Option<&'static str>, kind: T, - error_cause: Option>, + error_cause: Option>, } /// convenience type alias @@ -213,7 +212,7 @@ impl ChainError { #[inline] pub fn new( kind: T, - error_cause: Option>, + error_cause: Option>, occurrence: Option<&'static str>, ) -> Self { Self { @@ -228,7 +227,7 @@ impl ChainError { #[inline] pub fn new( kind: T, - error_cause: Option>, + error_cause: Option>, _occurrence: Option<&'static str>, ) -> Self { Self { kind, error_cause } @@ -248,14 +247,14 @@ impl ChainError { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; - /// fn do_some_io() -> Result<(), Box> { + /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) /// } /// /// derive_str_cherr!(Func2Error); /// - /// fn func2() -> Result<(), Box> { + /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; /// Ok(()) @@ -263,7 +262,7 @@ impl ChainError { /// /// derive_str_cherr!(Func1Error); /// - /// fn func1() -> Result<(), Box> { + /// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -353,14 +352,14 @@ impl ChainError { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; - /// fn do_some_io() -> Result<(), Box> { + /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) /// } /// /// derive_str_cherr!(Func2Error); /// - /// fn func2() -> Result<(), Box> { + /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; /// Ok(()) @@ -539,21 +538,21 @@ impl ChainErrorDown for dyn Error + 'static + Send + Sync { impl Error for ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref()) + self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) } } impl Error for &ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref()) + self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) } } impl Error for &mut ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref()) + self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) } } @@ -714,7 +713,7 @@ macro_rules! into_cherr { /// # } /// # } /// # } -/// fn do_some_stuff() -> Result<(), Box> { +/// fn do_some_stuff() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) /// } @@ -772,17 +771,17 @@ macro_rules! mcherr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # fn do_some_io() -> Result<(), Box> { +/// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// fn func2() -> Result<(), Box> { +/// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; /// Ok(()) /// } /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!("func1 error"))?; /// Ok(()) /// } @@ -813,13 +812,13 @@ macro_rules! mcherr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # fn do_some_io() -> Result<(), Box> { +/// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } /// derive_str_cherr!(Func2Error); /// -/// fn func2() -> Result<(), Box> { +/// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; /// Ok(()) @@ -827,7 +826,7 @@ macro_rules! mcherr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -883,7 +882,7 @@ macro_rules! mstrerr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -931,7 +930,7 @@ macro_rules! strerr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # fn do_some_io() -> Result<(), Box> { +/// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } @@ -945,7 +944,7 @@ macro_rules! strerr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } diff --git a/tests/test_iter.rs b/tests/test_iter.rs index c351160..c451f7f 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -4,7 +4,7 @@ use std::fmt::Write; use std::io; #[test] -fn test_iter() -> Result<(), Box> { +fn test_iter() -> Result<(), Box> { let err = io::Error::from(io::ErrorKind::NotFound); let err = cherr!(err, "1"); let err = cherr!(err, "2"); @@ -31,7 +31,7 @@ fn test_iter() -> Result<(), Box> { } #[test] -fn test_find_cause() -> Result<(), Box> { +fn test_find_cause() -> Result<(), Box> { let err = io::Error::from(io::ErrorKind::NotFound); let err = cherr!(err, "1"); let err = cherr!(err, "2"); @@ -48,7 +48,7 @@ fn test_find_cause() -> Result<(), Box> { } #[test] -fn test_root_cause() -> Result<(), Box> { +fn test_root_cause() -> Result<(), Box> { let err = io::Error::from(io::ErrorKind::NotFound); let err = cherr!(err, "1"); let err = cherr!(err, "2"); From ae6c01aab2627abe1b695af3801c9baa4b30b617 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 13 Mar 2019 11:34:53 +0100 Subject: [PATCH 19/85] macro hygiene --- Cargo.toml | 2 +- examples/example.rs | 3 +- examples/tutorial13.rs | 4 +- examples/tutorial14.rs | 2 +- examples/tutorial15.rs | 139 ++++++++++++++++++++ src/lib.rs | 290 ++++++++++++++++++++++++++++++++--------- 6 files changed, 372 insertions(+), 68 deletions(-) create mode 100644 examples/tutorial15.rs diff --git a/Cargo.toml b/Cargo.toml index d07ba7e..327757b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.4.4" +version = "0.5.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/examples/example.rs b/examples/example.rs index 8aadc8d..c383000 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,8 +1,9 @@ -use chainerror::*; use std::error::Error; use std::io; use std::result::Result; +use chainerror::*; + fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 9cfe090..78dee31 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -15,7 +15,7 @@ pub mod mycrate { Ok(()) } - #[derive(Debug)] + #[derive(Debug, Clone)] pub enum ErrorKind { Func2, IO(String), @@ -42,7 +42,7 @@ pub mod mycrate { } } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { use mycrate::func1; use mycrate::ErrorKind; use std::error::Error; diff --git a/examples/tutorial14.rs b/examples/tutorial14.rs index 5ccd7ce..e73d747 100644 --- a/examples/tutorial14.rs +++ b/examples/tutorial14.rs @@ -182,7 +182,7 @@ pub mod mycrate { } } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { use mycrate::func1; use mycrate::ErrorKind; use std::error::Error; diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs new file mode 100644 index 0000000..c0f5fe7 --- /dev/null +++ b/examples/tutorial15.rs @@ -0,0 +1,139 @@ +pub mod mycrate { + use std::io; + + use chainerror::*; + + fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { + Err(io::Error::from(io::ErrorKind::NotFound))?; + Ok(()) + } + + derive_str_cherr!(Func2Error); + + fn func2() -> std::result::Result<(), Box> { + let filename = "foo.txt"; + do_some_io(filename) + .map_err(|e| cherr!(e, Func2Error(format!("Error reading '{}'", filename))))?; + Ok(()) + } + + #[derive(Debug, Clone)] + pub enum ErrorKind { + Func2, + IO(String), + FatalError(String), + Unknown, + } + + derive_err_kind!(Error, ErrorKind); + pub type Result = std::result::Result; + + impl std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { + match self { + ErrorKind::FatalError(e) => write!(f, "fatal error {}", e), + ErrorKind::Unknown => write!(f, "unknown error"), + ErrorKind::Func2 => write!(f, "func1 error calling func2"), + ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + } + } + } + + impl ErrorKind { + fn from_io_error(e: &io::Error, f: String) -> Self { + match e.kind() { + io::ErrorKind::BrokenPipe => panic!("Should not happen"), + io::ErrorKind::ConnectionReset => { + ErrorKind::FatalError(format!("While reading `{}`: {}", f, e)) + } + _ => ErrorKind::IO(f), + } + } + } + + impl From<&(dyn std::error::Error + 'static + Send + Sync)> for ErrorKind { + fn from(e: &(dyn std::error::Error + 'static + Send + Sync)) -> Self { + if let Some(_) = e.downcast_ref::() { + ErrorKind::IO(String::from("Unknown filename")) + } else if let Some(_) = e.downcast_inner_ref::() { + ErrorKind::Func2 + } else { + ErrorKind::Unknown + } + } + } + + impl From<&std::boxed::Box> for ErrorKind { + fn from(e: &std::boxed::Box) -> Self { + Self::from(&**e) + } + } + + impl From<&Func2Error> for ErrorKind { + fn from(_: &Func2Error) -> Self { + ErrorKind::Func2 + } + } + + impl From<&io::Error> for ErrorKind { + fn from(e: &io::Error) -> Self { + ErrorKind::IO(format!("{}", e)) + } + } + + pub fn func1() -> Result<()> { + func2().map_err(|e| cherr!(e, ErrorKind::from(&e)))?; + + let filename = "bar.txt"; + + do_some_io(filename) + .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; + do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; + do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; + do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; + + Ok(()) + } + + pub fn super_func1() -> Result<()> { + func1().map_err(minto_cherr!(ErrorKind))?; + Ok(()) + } + +} + +fn main() -> Result<(), Box> { + use mycrate::super_func1; + use mycrate::ErrorKind; + use std::error::Error; + use std::io; + + if let Err(e) = super_func1() { + match e.kind() { + ErrorKind::FatalError(f) => eprintln!("Main Error Report: Fatal Error: {}", f), + ErrorKind::Unknown => eprintln!("Main Error Report: Unknown error occurred"), + ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), + ErrorKind::IO(ref filename) => { + eprintln!("Main Error Report: func1 error reading '{}'", filename) + } + } + + eprintln!(); + let mut s: &Error = &e; + while let Some(c) = s.source() { + if let Some(ioerror) = c.downcast_ref::() { + eprintln!("caused by: std::io::Error: {}", ioerror); + match ioerror.kind() { + io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"), + _ => {} + } + } else { + eprintln!("caused by: {}", c); + } + s = c; + } + + eprintln!("\nDebug Error:\n{:?}", e); + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 2107335..f378585 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,7 +174,6 @@ keyword_idents, macro_use_extern_crate, missing_debug_implementations, - missing_docs, trivial_numeric_casts, unused_extern_crates, unused_import_braces, @@ -243,10 +242,10 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use crate::chainerror::*; - /// # use std::error::Error; - /// # use std::io; - /// # use std::result::Result; + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) @@ -296,7 +295,7 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use chainerror::{ChainError, derive_str_cherr}; + /// # use chainerror::*; /// # derive_str_cherr!(FooError); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing @@ -319,7 +318,7 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use chainerror::{ChainError, derive_str_cherr}; + /// # use chainerror::*; /// # derive_str_cherr!(FooErrorKind); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing @@ -348,10 +347,10 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use crate::chainerror::*; - /// # use std::error::Error; - /// # use std::io; - /// # use std::result::Result; + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) @@ -449,6 +448,10 @@ pub trait ChainErrorDown { fn downcast_chain_ref(&self) -> Option<&ChainError>; /// Downcast to a mutable reference of `ChainError` fn downcast_chain_mut(&mut self) -> Option<&mut ChainError>; + /// Downcast to T of `ChainError` + fn downcast_inner_ref(&self) -> Option<&T>; + /// Downcast to T mutable reference of `ChainError` + fn downcast_inner_mut(&mut self) -> Option<&mut T>; } impl ChainErrorDown for ChainError { @@ -482,6 +485,31 @@ impl ChainErrorDown for ChainError { None } } + #[inline] + 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) + } + } else { + None + } + } + + #[inline] + 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) + } + } else { + None + } + } } impl ChainErrorDown for dyn Error + 'static { @@ -499,6 +527,22 @@ impl ChainErrorDown for dyn Error + 'static { fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } + + #[inline] + fn downcast_inner_ref(&self) -> Option<&T> { + self.downcast_ref::() + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut(&mut self) -> Option<&mut T> { + if self.is::() { + return self.downcast_mut::(); + } + + self.downcast_mut::>() + .and_then(|e| e.downcast_inner_mut::()) + } } impl ChainErrorDown for dyn Error + 'static + Send { @@ -516,6 +560,22 @@ impl ChainErrorDown for dyn Error + 'static + Send { fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } + + #[inline] + fn downcast_inner_ref(&self) -> Option<&T> { + self.downcast_ref::() + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut(&mut self) -> Option<&mut T> { + if self.is::() { + return self.downcast_mut::(); + } + + self.downcast_mut::>() + .and_then(|e| e.downcast_inner_mut::()) + } } impl ChainErrorDown for dyn Error + 'static + Send + Sync { @@ -533,26 +593,48 @@ impl ChainErrorDown for dyn Error + 'static + Send + Sync { fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } + + #[inline] + fn downcast_inner_ref(&self) -> Option<&T> { + self.downcast_ref::() + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut(&mut self) -> Option<&mut T> { + if self.is::() { + return self.downcast_mut::(); + } + + self.downcast_mut::>() + .and_then(|e| e.downcast_inner_mut::()) + } } impl Error for ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) } } impl Error for &ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) } } impl Error for &mut ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) } } @@ -629,28 +711,34 @@ where #[inline] fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError { let e: U = t.into(); - ChainError::<_>::new(e, None, line_filename) + ChainError::new(e, None, line_filename) } } -/// map into `ChainError` with `IntoChainError` +/* +impl ChainErrorFrom for U + where + T: 'static + Error + Into> + Clone, + U: 'static + Display + Debug + From, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError { + ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename) + } +} +*/ + +/// map into `ChainError` with `T::from(err)` /// /// adds `line!()` and `file!()` information #[macro_export] macro_rules! minto_cherr { - ( ) => { - |e| ChainErrorFrom::chain_error_from(e, Some(concat!(file!(), ":", line!(), ": "))) - }; -} - -/// into `ChainError` with `IntoChainError` -/// -/// adds `line!()` and `file!()` information -#[macro_export] -macro_rules! into_cherr { - ( $t:expr ) => { - ChainErrorFrom::chain_error_from($t, Some(concat!(file!(), ":", line!(), ": "))) - }; + ( $k:ident ) => ( + |e| $crate::cherr!(e, $k::from(&e)) + ); + ( $enum:ident $(:: $enum_path:ident)* ) => ( + |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e)) + ); } /// Creates a new `ChainError` @@ -734,32 +822,23 @@ macro_rules! into_cherr { #[macro_export] macro_rules! cherr { ( $k:expr ) => ({ - ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) }); ( None, $k:expr ) => ({ - ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) }); ( None, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!(None, format!($fmt, $($arg)+ )) + $crate::cherr!(None, format!($fmt, $($arg)+ )) }); ( None, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!(None, format!($fmt, $($arg)+ )) + $crate::cherr!(None, format!($fmt, $($arg)+ )) }); ( $e:path, $k:expr ) => ({ - ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) + $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) }); ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!($e, format!($fmt, $($arg)+ )) + $crate::cherr!($e, format!($fmt, $($arg)+ )) }); - -} - -/// shortcut for |e| cherr!(e, $k) -#[macro_export] -macro_rules! mcherr { - ( $k:expr ) => {{ - |e| cherr!(e, $k) - }}; } /// Convenience macro for `|e| cherr!(e, format!(…))` @@ -846,22 +925,22 @@ macro_rules! mcherr { #[macro_export] macro_rules! mstrerr { ( $t:path, $msg:expr ) => ({ - |e| cherr!(e, $t ($msg.to_string())) + |e| $crate::cherr!(e, $t ($msg.to_string())) }); ( $t:path, $msg:expr, ) => ({ - |e| cherr!(e, $t ($msg.to_string())) + |e| $crate::cherr!(e, $t ($msg.to_string())) }); ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ - |e| cherr!(e, $t (format!($fmt, $($arg)+ ))) + |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ ))) }); ($msg:expr) => ({ - |e| cherr!(e, $msg.to_string()) + |e| $crate::cherr!(e, $msg.to_string()) }); ($msg:expr, ) => ({ - |e| cherr!(e, $msg.to_string()) + |e| $crate::cherr!(e, $msg.to_string()) }); ($fmt:expr, $($arg:tt)+) => ({ - |e| cherr!(e, format!($fmt, $($arg)+ )) + |e| $crate::cherr!(e, format!($fmt, $($arg)+ )) }); } @@ -882,7 +961,7 @@ macro_rules! mstrerr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -902,22 +981,22 @@ macro_rules! mstrerr { #[macro_export] macro_rules! strerr { ( $t:path, $msg:expr ) => ({ - cherr!($t ($msg.to_string())) + $crate::cherr!($t ($msg.to_string())) }); ( $t:path, $msg:expr, ) => ({ - cherr!($t ($msg.to_string())) + $crate::cherr!($t ($msg.to_string())) }); ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!($t (format!($fmt, $($arg)+ ))) + $crate::cherr!($t (format!($fmt, $($arg)+ ))) }); ($msg:expr) => ({ - cherr!($msg.to_string()) + $crate::cherr!($msg.to_string()) }); ($msg:expr, ) => ({ - cherr!($msg.to_string()) + $crate::cherr!($msg.to_string()) }); ($fmt:expr, $($arg:tt)+) => ({ - cherr!(format!($fmt, $($arg)+ )) + $crate::cherr!(format!($fmt, $($arg)+ )) }); } @@ -944,7 +1023,7 @@ macro_rules! strerr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -980,13 +1059,76 @@ macro_rules! derive_str_cherr { }; } -/// Derive an Error, which wraps ChainError and implements a kind() method +/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method /// -/// e.kind() returns the kind +/// It basically hides `ChainError` to the outside and only exposes the `kind()` +/// method. +/// +/// Error::kind() returns the ErrorKind +/// Error::source() returns the parent error +/// +/// # Examples +/// +/// ```rust +/// use std::io; +/// use chainerror::*; +/// +/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { +/// return Err(io::Error::from(io::ErrorKind::NotFound)); +/// } +/// +/// #[derive(Debug, Clone)] +/// pub enum ErrorKind { +/// IO(String), +/// FatalError(String), +/// Unknown, +/// } +/// +/// derive_err_kind!(Error, ErrorKind); +/// +/// impl std::fmt::Display for ErrorKind { +/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { +/// match self { +/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e), +/// ErrorKind::Unknown => write!(f, "unknown error"), +/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), +/// } +/// } +/// } +/// +/// impl ErrorKind { +/// fn from_io_error(e: &io::Error, f: String) -> Self { +/// match e.kind() { +/// io::ErrorKind::BrokenPipe => panic!("Should not happen"), +/// io::ErrorKind::ConnectionReset => { +/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e)) +/// } +/// _ => ErrorKind::IO(f), +/// } +/// } +/// } +/// +/// impl From<&io::Error> for ErrorKind { +/// fn from(e: &io::Error) -> Self { +/// ErrorKind::IO(format!("{}", e)) +/// } +/// } +/// +/// pub fn func1() -> std::result::Result<(), Error> { +/// let filename = "bar.txt"; +/// +/// do_some_io(filename) +/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; +/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; +/// Ok(()) +/// } +/// ``` #[macro_export] macro_rules! derive_err_kind { ($e:ident, $k:ident) => { - pub struct $e(ChainError<$k>); + pub struct $e($crate::ChainError<$k>); impl $e { pub fn kind(&self) -> &$k { @@ -996,16 +1138,38 @@ macro_rules! derive_err_kind { impl From<$k> for $e { fn from(e: $k) -> Self { - $e(ChainError::new(e, None, None)) + $e($crate::ChainError::new(e, None, None)) } } impl From> for $e { - fn from(e: ChainError<$k>) -> Self { + fn from(e: $crate::ChainError<$k>) -> Self { $e(e) } } + impl From<&$e> for $k + where + $k: Clone, + { + fn from(e: &$e) -> Self { + e.kind().clone() + } + } + + impl $crate::ChainErrorFrom<$e> for $k + where + $k: Clone, + { + #[inline] + fn chain_error_from( + t: $e, + line_filename: Option<&'static str>, + ) -> $crate::ChainError<$k> { + $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename) + } + } + impl std::error::Error for $e { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.0.source() From 648dc871c29e4a97c2e70039c8963c2000a0c174 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 3 Mar 2020 15:01:02 +0100 Subject: [PATCH 20/85] Create rust.yml --- .github/workflows/rust.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..312cbfd --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,15 @@ +name: Rust + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose From 903f245278258f95bf1aeba5914ee6ab7682bc63 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 3 Mar 2020 15:01:25 +0100 Subject: [PATCH 21/85] Create gh-pages.yml --- .github/workflows/gh-pages.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/gh-pages.yml diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 0000000..8661869 --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,24 @@ +name: github pages + +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + + - name: Build mdbook + run: cargo install mdbook + + - name: Build + run: mdbook build + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} + publish_dir: ./book From 3e270f1e1abfd40debce85ec30529a15204d51f3 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 3 Mar 2020 14:25:37 +0100 Subject: [PATCH 22/85] book: use rustdoc_include --- booksrc/tutorial1.md | 4 +- booksrc/tutorial10.md | 2 +- booksrc/tutorial11.md | 2 +- booksrc/tutorial12.md | 2 +- booksrc/tutorial13.md | 2 +- booksrc/tutorial2.md | 4 +- booksrc/tutorial3.md | 2 +- booksrc/tutorial4.md | 2 +- booksrc/tutorial5.md | 2 +- booksrc/tutorial6.md | 2 +- booksrc/tutorial7.md | 2 +- booksrc/tutorial8.md | 2 +- booksrc/tutorial9.md | 2 +- theme/book.js | 611 ------------------------------------------ 14 files changed, 15 insertions(+), 626 deletions(-) delete mode 100644 theme/book.js diff --git a/booksrc/tutorial1.md b/booksrc/tutorial1.md index cf1d9d0..ae73400 100644 --- a/booksrc/tutorial1.md +++ b/booksrc/tutorial1.md @@ -17,10 +17,10 @@ and improves inspecting the sources of an error. You can also run the tutorial examples in the checked out [chainerror git repo](https://github.com/haraldh/chainerror). -~~~ +~~~console $ cargo run -q --example tutorial1 ~~~ ~~~rust {{#include ../examples/tutorial1.rs}} -~~~ \ No newline at end of file +~~~ diff --git a/booksrc/tutorial10.md b/booksrc/tutorial10.md index 84fc436..e7f2ee4 100644 --- a/booksrc/tutorial10.md +++ b/booksrc/tutorial10.md @@ -20,6 +20,6 @@ Also a nice `match` on `ChainError.kind()` is now possible, which returns `&T {{#include ../examples/tutorial10.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ \ No newline at end of file diff --git a/booksrc/tutorial11.md b/booksrc/tutorial11.md index 610051b..0d2c0cc 100644 --- a/booksrc/tutorial11.md +++ b/booksrc/tutorial11.md @@ -28,6 +28,6 @@ which can then be used with `chainerror`. {{#include ../examples/tutorial11.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ diff --git a/booksrc/tutorial12.md b/booksrc/tutorial12.md index 81ab142..a8c80e6 100644 --- a/booksrc/tutorial12.md +++ b/booksrc/tutorial12.md @@ -6,6 +6,6 @@ or call a function with `&e` {{#include ../examples/tutorial12.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ diff --git a/booksrc/tutorial13.md b/booksrc/tutorial13.md index 2b3dde2..6d438f4 100644 --- a/booksrc/tutorial13.md +++ b/booksrc/tutorial13.md @@ -10,7 +10,7 @@ have to change much or anything. # #[allow(dead_code)] # #[macro_use] # pub mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } pub mod mycrate { use crate::chainerror::*; // omit the `crate::` part diff --git a/booksrc/tutorial2.md b/booksrc/tutorial2.md index e217291..e7c5e6d 100644 --- a/booksrc/tutorial2.md +++ b/booksrc/tutorial2.md @@ -9,7 +9,7 @@ Press the play button in the upper right corner and see the nice debug output. {{#include ../examples/tutorial2.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ @@ -29,4 +29,4 @@ again have a `Err(Box)` as a result. The `Debug` implementation of `ChainError` (which is returned by `cherr!()`) prints the `Debug` of `T` prefixed with the stored filename and line number. -`ChainError` in our case is `ChainError`. \ No newline at end of file +`ChainError` in our case is `ChainError`. diff --git a/booksrc/tutorial3.md b/booksrc/tutorial3.md index 0abf07c..cc832a9 100644 --- a/booksrc/tutorial3.md +++ b/booksrc/tutorial3.md @@ -6,7 +6,7 @@ Now let's get more rust idiomatic by using `.map_err()`. {{#include ../examples/tutorial3.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ diff --git a/booksrc/tutorial4.md b/booksrc/tutorial4.md index 70331c0..68cc16d 100644 --- a/booksrc/tutorial4.md +++ b/booksrc/tutorial4.md @@ -12,6 +12,6 @@ more debug strings. {{#include ../examples/tutorial4.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ \ No newline at end of file diff --git a/booksrc/tutorial5.md b/booksrc/tutorial5.md index 5595220..05db784 100644 --- a/booksrc/tutorial5.md +++ b/booksrc/tutorial5.md @@ -7,7 +7,7 @@ Sometimes you want to inspect the `source()` of an `Error`. {{#include ../examples/tutorial5.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ diff --git a/booksrc/tutorial6.md b/booksrc/tutorial6.md index 1d4a64e..72fe33a 100644 --- a/booksrc/tutorial6.md +++ b/booksrc/tutorial6.md @@ -14,6 +14,6 @@ This is how it looks like, when using those: {{#include ../examples/tutorial6.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ \ No newline at end of file diff --git a/booksrc/tutorial7.md b/booksrc/tutorial7.md index cf65d04..ccb6ff6 100644 --- a/booksrc/tutorial7.md +++ b/booksrc/tutorial7.md @@ -30,6 +30,6 @@ or to use `.root_cause()`, which of course can be of any type implementing `std: {{#include ../examples/tutorial7.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ \ No newline at end of file diff --git a/booksrc/tutorial8.md b/booksrc/tutorial8.md index ed03342..8060788 100644 --- a/booksrc/tutorial8.md +++ b/booksrc/tutorial8.md @@ -26,6 +26,6 @@ hiding the `ChainError` implementation detail. {{#include ../examples/tutorial8.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ \ No newline at end of file diff --git a/booksrc/tutorial9.md b/booksrc/tutorial9.md index 4027cdc..f66dd0e 100644 --- a/booksrc/tutorial9.md +++ b/booksrc/tutorial9.md @@ -28,6 +28,6 @@ In the next chapter, we will see, how to solve this more elegantly. {{#include ../examples/tutorial9.rs}} # #[allow(dead_code)] # mod chainerror { -{{#includecomment ../src/lib.rs}} +{{#rustdoc_include ../src/lib.rs:-1}} # } ~~~ diff --git a/theme/book.js b/theme/book.js deleted file mode 100644 index 8c6946d..0000000 --- a/theme/book.js +++ /dev/null @@ -1,611 +0,0 @@ -"use strict"; - -// Fix back button cache problem -window.onunload = function () { }; - -// Global variable, shared between modules -function playpen_text(playpen) { - let code_block = playpen.querySelector("code"); - - if (window.ace && code_block.classList.contains("editable")) { - let editor = window.ace.edit(code_block); - return editor.getValue(); - } else { - return code_block.textContent; - } -} - -(function codeSnippets() { - // Hide Rust code lines prepended with a specific character - var hiding_character = "#"; - - function fetch_with_timeout(url, options, timeout = 6000) { - return Promise.race([ - fetch(url, options), - new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)) - ]); - } - - var playpens = Array.from(document.querySelectorAll(".playpen")); - if (playpens.length > 0) { - fetch_with_timeout("https://play.rust-lang.org/meta/crates", { - headers: { - 'Content-Type': "application/json", - }, - method: 'POST', - mode: 'cors', - }) - .then(response => response.json()) - .then(response => { - // get list of crates available in the rust playground - let playground_crates = response.crates.map(item => item["id"]); - playpens.forEach(block => handle_crate_list_update(block, playground_crates)); - }); - } - - function handle_crate_list_update(playpen_block, playground_crates) { - // update the play buttons after receiving the response - update_play_button(playpen_block, playground_crates); - - // and install on change listener to dynamically update ACE editors - if (window.ace) { - let code_block = playpen_block.querySelector("code"); - if (code_block.classList.contains("editable")) { - let editor = window.ace.edit(code_block); - editor.addEventListener("change", function (e) { - update_play_button(playpen_block, playground_crates); - }); - } - } - } - - // updates the visibility of play button based on `no_run` class and - // used crates vs ones available on http://play.rust-lang.org - function update_play_button(pre_block, playground_crates) { - var play_button = pre_block.querySelector(".play-button"); - - // skip if code is `no_run` - if (pre_block.querySelector('code').classList.contains("no_run")) { - play_button.classList.add("hidden"); - return; - } - - // get list of `extern crate`'s from snippet - var txt = playpen_text(pre_block); - var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; - var snippet_crates = []; - var item; - while (item = re.exec(txt)) { - snippet_crates.push(item[1]); - } - - // check if all used crates are available on play.rust-lang.org - var all_available = snippet_crates.every(function (elem) { - return playground_crates.indexOf(elem) > -1; - }); - - if (all_available) { - play_button.classList.remove("hidden"); - } else { - play_button.classList.add("hidden"); - } - } - - function run_rust_code(code_block) { - var result_block = code_block.querySelector(".result"); - if (!result_block) { - result_block = document.createElement('code'); - result_block.className = 'result hljs language-bash'; - - code_block.append(result_block); - } - - let text = playpen_text(code_block); - - var params = { - channel: "stable", - mode: "debug", - edition: "2018", - crateType: "bin", - tests: false, - code: text - }; - - if (text.indexOf("#![feature") !== -1) { - params.channel = "nightly"; - } - - if (text.indexOf("#[test]") !== -1) { - params.tests = "true"; - } - - if (text.indexOf("#![crate_type=\"lib\"]") !== -1) { - params.crateType = "lib"; - } - - result_block.innerText = "Running..."; - - fetch_with_timeout("https://play.rust-lang.org/execute", { - headers: { - 'Content-Type': "application/json", - }, - method: 'POST', - mode: 'cors', - body: JSON.stringify(params) - }) - .then(response => response.json()) - .then(response => result_block.innerText = response.stderr + "\n\n" + response.stdout) - .catch(error => result_block.innerText = "Playground Communication: " + error.message); - } - - // Syntax highlighting Configuration - hljs.configure({ - tabReplace: ' ', // 4 spaces - languages: [], // Languages used for auto-detection - }); - - if (window.ace) { - // language-rust class needs to be removed for editable - // blocks or highlightjs will capture events - Array - .from(document.querySelectorAll('code.editable')) - .forEach(function (block) { block.classList.remove('language-rust'); }); - - Array - .from(document.querySelectorAll('code:not(.editable)')) - .forEach(function (block) { hljs.highlightBlock(block); }); - } else { - Array - .from(document.querySelectorAll('code')) - .forEach(function (block) { hljs.highlightBlock(block); }); - } - - // Adding the hljs class gives code blocks the color css - // even if highlighting doesn't apply - Array - .from(document.querySelectorAll('code')) - .forEach(function (block) { block.classList.add('hljs'); }); - - Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { - - var code_block = block; - var pre_block = block.parentNode; - // hide lines - var lines = code_block.innerHTML.split("\n"); - var first_non_hidden_line = false; - var lines_hidden = false; - var trimmed_line = ""; - - for (var n = 0; n < lines.length; n++) { - trimmed_line = lines[n].trim(); - if (trimmed_line[0] == hiding_character && trimmed_line[1] != hiding_character) { - if (first_non_hidden_line) { - lines[n] = "" + "\n" + lines[n].replace(/(\s*)# ?/, "$1") + ""; - } - else { - lines[n] = "" + lines[n].replace(/(\s*)# ?/, "$1") + "\n" + ""; - } - lines_hidden = true; - } - else if (first_non_hidden_line) { - lines[n] = "\n" + lines[n]; - } - else { - first_non_hidden_line = true; - } - if (trimmed_line[0] == hiding_character && trimmed_line[1] == hiding_character) { - lines[n] = lines[n].replace("##", "#") - } - } - code_block.innerHTML = lines.join(""); - - // If no lines were hidden, return - if (!lines_hidden) { return; } - - var buttons = document.createElement('div'); - buttons.className = 'buttons'; - buttons.innerHTML = ""; - - // add expand button - pre_block.insertBefore(buttons, pre_block.firstChild); - - pre_block.querySelector('.buttons').addEventListener('click', function (e) { - if (e.target.classList.contains('fa-expand')) { - var lines = pre_block.querySelectorAll('span.hidden'); - - e.target.classList.remove('fa-expand'); - e.target.classList.add('fa-compress'); - e.target.title = 'Hide lines'; - e.target.setAttribute('aria-label', e.target.title); - - Array.from(lines).forEach(function (line) { - line.classList.remove('hidden'); - line.classList.add('unhidden'); - }); - } else if (e.target.classList.contains('fa-compress')) { - var lines = pre_block.querySelectorAll('span.unhidden'); - - e.target.classList.remove('fa-compress'); - e.target.classList.add('fa-expand'); - e.target.title = 'Show hidden lines'; - e.target.setAttribute('aria-label', e.target.title); - - Array.from(lines).forEach(function (line) { - line.classList.remove('unhidden'); - line.classList.add('hidden'); - }); - } - }); - }); - - Array.from(document.querySelectorAll('pre code')).forEach(function (block) { - var pre_block = block.parentNode; - if (!pre_block.classList.contains('playpen')) { - var buttons = pre_block.querySelector(".buttons"); - if (!buttons) { - buttons = document.createElement('div'); - buttons.className = 'buttons'; - pre_block.insertBefore(buttons, pre_block.firstChild); - } - - var clipButton = document.createElement('button'); - clipButton.className = 'fa fa-copy clip-button'; - clipButton.title = 'Copy to clipboard'; - clipButton.setAttribute('aria-label', clipButton.title); - clipButton.innerHTML = ''; - - buttons.insertBefore(clipButton, buttons.firstChild); - } - }); - - // Process playpen code blocks - Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) { - // Add play button - var buttons = pre_block.querySelector(".buttons"); - if (!buttons) { - buttons = document.createElement('div'); - buttons.className = 'buttons'; - pre_block.insertBefore(buttons, pre_block.firstChild); - } - - var runCodeButton = document.createElement('button'); - runCodeButton.className = 'fa fa-play play-button'; - runCodeButton.hidden = true; - runCodeButton.title = 'Run this code'; - runCodeButton.setAttribute('aria-label', runCodeButton.title); - - var copyCodeClipboardButton = document.createElement('button'); - copyCodeClipboardButton.className = 'fa fa-copy clip-button'; - copyCodeClipboardButton.innerHTML = ''; - copyCodeClipboardButton.title = 'Copy to clipboard'; - copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); - - buttons.insertBefore(runCodeButton, buttons.firstChild); - buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); - - runCodeButton.addEventListener('click', function (e) { - run_rust_code(pre_block); - }); - - let code_block = pre_block.querySelector("code"); - if (window.ace && code_block.classList.contains("editable")) { - var undoChangesButton = document.createElement('button'); - undoChangesButton.className = 'fa fa-history reset-button'; - undoChangesButton.title = 'Undo changes'; - undoChangesButton.setAttribute('aria-label', undoChangesButton.title); - - buttons.insertBefore(undoChangesButton, buttons.firstChild); - - undoChangesButton.addEventListener('click', function () { - let editor = window.ace.edit(code_block); - editor.setValue(editor.originalCode); - editor.clearSelection(); - }); - } - }); -})(); - -(function themes() { - var html = document.querySelector('html'); - var themeToggleButton = document.getElementById('theme-toggle'); - var themePopup = document.getElementById('theme-list'); - var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); - var stylesheets = { - ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), - tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), - highlight: document.querySelector("[href$='highlight.css']"), - }; - - function showThemes() { - themePopup.style.display = 'block'; - themeToggleButton.setAttribute('aria-expanded', true); - themePopup.querySelector("button#" + document.body.className).focus(); - } - - function hideThemes() { - themePopup.style.display = 'none'; - themeToggleButton.setAttribute('aria-expanded', false); - themeToggleButton.focus(); - } - - function set_theme(theme) { - let ace_theme; - - if (theme == 'coal' || theme == 'navy') { - stylesheets.ayuHighlight.disabled = true; - stylesheets.tomorrowNight.disabled = false; - stylesheets.highlight.disabled = true; - - ace_theme = "ace/theme/tomorrow_night"; - } else if (theme == 'ayu') { - stylesheets.ayuHighlight.disabled = false; - stylesheets.tomorrowNight.disabled = true; - stylesheets.highlight.disabled = true; - - ace_theme = "ace/theme/tomorrow_night"; - } else { - stylesheets.ayuHighlight.disabled = true; - stylesheets.tomorrowNight.disabled = true; - stylesheets.highlight.disabled = false; - - ace_theme = "ace/theme/dawn"; - } - - setTimeout(function () { - themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor; - }, 1); - - if (window.ace && window.editors) { - window.editors.forEach(function (editor) { - editor.setTheme(ace_theme); - }); - } - - var previousTheme; - try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { } - if (previousTheme === null || previousTheme === undefined) { previousTheme = default_theme; } - - try { localStorage.setItem('mdbook-theme', theme); } catch (e) { } - - document.body.className = theme; - html.classList.remove(previousTheme); - html.classList.add(theme); - } - - // Set theme - var theme; - try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } - if (theme === null || theme === undefined) { theme = default_theme; } - - set_theme(theme); - - themeToggleButton.addEventListener('click', function () { - if (themePopup.style.display === 'block') { - hideThemes(); - } else { - showThemes(); - } - }); - - themePopup.addEventListener('click', function (e) { - var theme = e.target.id || e.target.parentElement.id; - set_theme(theme); - }); - - themePopup.addEventListener('focusout', function(e) { - // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) - if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { - hideThemes(); - } - }); - - // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang-nursery/mdBook/issues/628 - document.addEventListener('click', function(e) { - if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { - hideThemes(); - } - }); - - document.addEventListener('keydown', function (e) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } - if (!themePopup.contains(e.target)) { return; } - - switch (e.key) { - case 'Escape': - e.preventDefault(); - hideThemes(); - break; - case 'ArrowUp': - e.preventDefault(); - var li = document.activeElement.parentElement; - if (li && li.previousElementSibling) { - li.previousElementSibling.querySelector('button').focus(); - } - break; - case 'ArrowDown': - e.preventDefault(); - var li = document.activeElement.parentElement; - if (li && li.nextElementSibling) { - li.nextElementSibling.querySelector('button').focus(); - } - break; - case 'Home': - e.preventDefault(); - themePopup.querySelector('li:first-child button').focus(); - break; - case 'End': - e.preventDefault(); - themePopup.querySelector('li:last-child button').focus(); - break; - } - }); -})(); - -(function sidebar() { - var html = document.querySelector("html"); - var sidebar = document.getElementById("sidebar"); - var sidebarLinks = document.querySelectorAll('#sidebar a'); - var sidebarToggleButton = document.getElementById("sidebar-toggle"); - var firstContact = null; - - function showSidebar() { - html.classList.remove('sidebar-hidden') - html.classList.add('sidebar-visible'); - Array.from(sidebarLinks).forEach(function (link) { - link.setAttribute('tabIndex', 0); - }); - sidebarToggleButton.setAttribute('aria-expanded', true); - sidebar.setAttribute('aria-hidden', false); - try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } - } - - function hideSidebar() { - html.classList.remove('sidebar-visible') - html.classList.add('sidebar-hidden'); - Array.from(sidebarLinks).forEach(function (link) { - link.setAttribute('tabIndex', -1); - }); - sidebarToggleButton.setAttribute('aria-expanded', false); - sidebar.setAttribute('aria-hidden', true); - try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { } - } - - // Toggle sidebar - sidebarToggleButton.addEventListener('click', function sidebarToggle() { - if (html.classList.contains("sidebar-hidden")) { - showSidebar(); - } else if (html.classList.contains("sidebar-visible")) { - hideSidebar(); - } else { - if (getComputedStyle(sidebar)['transform'] === 'none') { - hideSidebar(); - } else { - showSidebar(); - } - } - }); - - document.addEventListener('touchstart', function (e) { - firstContact = { - x: e.touches[0].clientX, - time: Date.now() - }; - }, { passive: true }); - - document.addEventListener('touchmove', function (e) { - if (!firstContact) - return; - - var curX = e.touches[0].clientX; - var xDiff = curX - firstContact.x, - tDiff = Date.now() - firstContact.time; - - if (tDiff < 250 && Math.abs(xDiff) >= 150) { - if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) - showSidebar(); - else if (xDiff < 0 && curX < 300) - hideSidebar(); - - firstContact = null; - } - }, { passive: true }); - - // Scroll sidebar to current active section - var activeSection = sidebar.querySelector(".active"); - if (activeSection) { - sidebar.scrollTop = activeSection.offsetTop; - } -})(); - -(function chapterNavigation() { - document.addEventListener('keydown', function (e) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } - if (window.search && window.search.hasFocus()) { return; } - - switch (e.key) { - case 'ArrowRight': - e.preventDefault(); - var nextButton = document.querySelector('.nav-chapters.next'); - if (nextButton) { - window.location.href = nextButton.href; - } - break; - case 'ArrowLeft': - e.preventDefault(); - var previousButton = document.querySelector('.nav-chapters.previous'); - if (previousButton) { - window.location.href = previousButton.href; - } - break; - } - }); -})(); - -(function clipboard() { - var clipButtons = document.querySelectorAll('.clip-button'); - - function hideTooltip(elem) { - elem.firstChild.innerText = ""; - elem.className = 'fa fa-copy clip-button'; - } - - function showTooltip(elem, msg) { - elem.firstChild.innerText = msg; - elem.className = 'fa fa-copy tooltipped'; - } - - var clipboardSnippets = new Clipboard('.clip-button', { - text: function (trigger) { - hideTooltip(trigger); - let playpen = trigger.closest("pre"); - return playpen_text(playpen); - } - }); - - Array.from(clipButtons).forEach(function (clipButton) { - clipButton.addEventListener('mouseout', function (e) { - hideTooltip(e.currentTarget); - }); - }); - - clipboardSnippets.on('success', function (e) { - e.clearSelection(); - showTooltip(e.trigger, "Copied!"); - }); - - clipboardSnippets.on('error', function (e) { - showTooltip(e.trigger, "Clipboard error!"); - }); -})(); - -(function scrollToTop () { - var menuTitle = document.querySelector('.menu-title'); - - menuTitle.addEventListener('click', function () { - document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); - }); -})(); - -(function autoHideMenu() { - var menu = document.getElementById('menu-bar'); - - var previousScrollTop = document.scrollingElement.scrollTop; - - document.addEventListener('scroll', function () { - if (menu.classList.contains('folded') && document.scrollingElement.scrollTop < previousScrollTop) { - menu.classList.remove('folded'); - } else if (!menu.classList.contains('folded') && document.scrollingElement.scrollTop > previousScrollTop) { - menu.classList.add('folded'); - } - - if (!menu.classList.contains('bordered') && document.scrollingElement.scrollTop > 0) { - menu.classList.add('bordered'); - } - - if (menu.classList.contains('bordered') && document.scrollingElement.scrollTop === 0) { - menu.classList.remove('bordered'); - } - - previousScrollTop = document.scrollingElement.scrollTop; - }, { passive: true }); -})(); From 1d43a39c61e9748a5c33da7f0f49498ea1424ada Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 3 Mar 2020 14:37:11 +0100 Subject: [PATCH 23/85] add `dyn` --- examples/example.rs | 4 ++-- examples/tutorial1.rs | 8 ++++---- examples/tutorial10.rs | 6 +++--- examples/tutorial11.rs | 6 +++--- examples/tutorial12.rs | 6 +++--- examples/tutorial13.rs | 8 ++++---- examples/tutorial14.rs | 4 ++-- examples/tutorial15.rs | 13 ++++++------- examples/tutorial2.rs | 8 ++++---- examples/tutorial3.rs | 8 ++++---- examples/tutorial4.rs | 8 ++++---- examples/tutorial5.rs | 8 ++++---- examples/tutorial6.rs | 10 +++++----- examples/tutorial7.rs | 8 ++++---- examples/tutorial8.rs | 8 ++++---- examples/tutorial9.rs | 8 ++++---- src/lib.rs | 40 ++++++++++++++++++++-------------------- tests/test_iter.rs | 6 +++--- 18 files changed, 83 insertions(+), 84 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index c383000..99f965e 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -4,12 +4,12 @@ use std::result::Result; use chainerror::*; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func3() -> Result<(), Box> { +fn func3() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) diff --git a/examples/tutorial1.rs b/examples/tutorial1.rs index d296a92..4ac1eb3 100644 --- a/examples/tutorial1.rs +++ b/examples/tutorial1.rs @@ -2,25 +2,25 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { if let Err(_) = do_some_io() { Err("func2 error")?; } Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { if let Err(_) = func2() { Err("func1 error")?; } Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { func1() } diff --git a/examples/tutorial10.rs b/examples/tutorial10.rs index 15eae31..cf13909 100644 --- a/examples/tutorial10.rs +++ b/examples/tutorial10.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -39,7 +39,7 @@ fn func1() -> ChainResult<(), Func1ErrorKind> { Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { match e.kind() { Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), diff --git a/examples/tutorial11.rs b/examples/tutorial11.rs index 00e3d74..5f85c23 100644 --- a/examples/tutorial11.rs +++ b/examples/tutorial11.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -45,7 +45,7 @@ fn func1() -> ChainResult<(), Func1ErrorKind> { Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { match e.kind() { Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), diff --git a/examples/tutorial12.rs b/examples/tutorial12.rs index 8cdc8aa..f5e38de 100644 --- a/examples/tutorial12.rs +++ b/examples/tutorial12.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -54,7 +54,7 @@ fn handle_func1errorkind(e: &Func1ErrorKind) { } } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { match *e { Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 78dee31..c345157 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -2,14 +2,14 @@ pub mod mycrate { use chainerror::*; use std::io; - fn do_some_io() -> std::result::Result<(), Box> { + fn do_some_io() -> std::result::Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); - fn func2() -> std::result::Result<(), Box> { + fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -42,7 +42,7 @@ pub mod mycrate { } } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { use mycrate::func1; use mycrate::ErrorKind; use std::error::Error; @@ -57,7 +57,7 @@ fn main() -> Result<(), Box> { } eprintln!(); - let mut s: &Error = &e; + let mut s: &dyn Error = &e; while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::() { eprintln!("caused by: std::io::Error: {}", ioerror); diff --git a/examples/tutorial14.rs b/examples/tutorial14.rs index e73d747..84c3491 100644 --- a/examples/tutorial14.rs +++ b/examples/tutorial14.rs @@ -182,7 +182,7 @@ pub mod mycrate { } } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { use mycrate::func1; use mycrate::ErrorKind; use std::error::Error; @@ -197,7 +197,7 @@ fn main() -> Result<(), Box> { } eprintln!(); - let mut s: &Error = &e; + let mut s: &dyn Error = &e; while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::() { eprintln!("caused by: std::io::Error: {}", ioerror); diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs index c0f5fe7..581840c 100644 --- a/examples/tutorial15.rs +++ b/examples/tutorial15.rs @@ -10,7 +10,7 @@ pub mod mycrate { derive_str_cherr!(Func2Error); - fn func2() -> std::result::Result<(), Box> { + fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; do_some_io(filename) .map_err(|e| cherr!(e, Func2Error(format!("Error reading '{}'", filename))))?; @@ -52,7 +52,7 @@ pub mod mycrate { } impl From<&(dyn std::error::Error + 'static + Send + Sync)> for ErrorKind { - fn from(e: &(dyn std::error::Error + 'static + Send + Sync)) -> Self { + fn from(e: &(dyn std::error::Error + 'static + Send + Sync)) -> Self { if let Some(_) = e.downcast_ref::() { ErrorKind::IO(String::from("Unknown filename")) } else if let Some(_) = e.downcast_inner_ref::() { @@ -63,8 +63,8 @@ pub mod mycrate { } } - impl From<&std::boxed::Box> for ErrorKind { - fn from(e: &std::boxed::Box) -> Self { + impl From<&std::boxed::Box> for ErrorKind { + fn from(e: &std::boxed::Box) -> Self { Self::from(&**e) } } @@ -99,10 +99,9 @@ pub mod mycrate { func1().map_err(minto_cherr!(ErrorKind))?; Ok(()) } - } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { use mycrate::super_func1; use mycrate::ErrorKind; use std::error::Error; @@ -119,7 +118,7 @@ fn main() -> Result<(), Box> { } eprintln!(); - let mut s: &Error = &e; + let mut s: &dyn Error = &e; while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::() { eprintln!("caused by: std::io::Error: {}", ioerror); diff --git a/examples/tutorial2.rs b/examples/tutorial2.rs index 349c5cd..b13f0cc 100644 --- a/examples/tutorial2.rs +++ b/examples/tutorial2.rs @@ -4,25 +4,25 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { if let Err(e) = do_some_io() { Err(cherr!(e, "func2 error"))?; } Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { if let Err(e) = func2() { Err(cherr!(e, "func1 error"))?; } Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { func1() } diff --git a/examples/tutorial3.rs b/examples/tutorial3.rs index cdbffed..6da067c 100644 --- a/examples/tutorial3.rs +++ b/examples/tutorial3.rs @@ -4,22 +4,22 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { do_some_io().map_err(|e| cherr!(e, "func2 error"))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(|e| cherr!(e, "func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{:?}", e); } diff --git a/examples/tutorial4.rs b/examples/tutorial4.rs index d037215..e1225c0 100644 --- a/examples/tutorial4.rs +++ b/examples/tutorial4.rs @@ -3,23 +3,23 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{:?}", e); } diff --git a/examples/tutorial5.rs b/examples/tutorial5.rs index 4d673e3..cb80569 100644 --- a/examples/tutorial5.rs +++ b/examples/tutorial5.rs @@ -3,18 +3,18 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { if let Err(e) = func2() { if let Some(s) = e.source() { eprintln!("func2 failed because of '{}'", s); @@ -24,7 +24,7 @@ fn func1() -> Result<(), Box> { Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{}", e); } diff --git a/examples/tutorial6.rs b/examples/tutorial6.rs index 22e0e78..996540c 100644 --- a/examples/tutorial6.rs +++ b/examples/tutorial6.rs @@ -3,26 +3,26 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("Error: {}", e); - let mut s : &(dyn Error) = e.as_ref(); + let mut s: &(dyn Error) = e.as_ref(); while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::() { eprintln!("caused by: std::io::Error: {}", ioerror); diff --git a/examples/tutorial7.rs b/examples/tutorial7.rs index bf09449..b36fa8b 100644 --- a/examples/tutorial7.rs +++ b/examples/tutorial7.rs @@ -3,23 +3,23 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("Error: {}", e); if let Some(s) = e.downcast_chain_ref::() { diff --git a/examples/tutorial8.rs b/examples/tutorial8.rs index be92df7..1644a32 100644 --- a/examples/tutorial8.rs +++ b/examples/tutorial8.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -18,12 +18,12 @@ fn func2() -> Result<(), Box> { derive_str_cherr!(Func1Error); -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!(Func1Error, "func1 error"))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { if let Some(f1err) = e.downcast_chain_ref::() { eprintln!("Func1Error: {}", f1err); diff --git a/examples/tutorial9.rs b/examples/tutorial9.rs index d9436f4..99b0f23 100644 --- a/examples/tutorial9.rs +++ b/examples/tutorial9.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -19,14 +19,14 @@ fn func2() -> Result<(), Box> { derive_str_cherr!(Func1ErrorFunc2); derive_str_cherr!(Func1ErrorIO); -fn func1() -> Result<(), Box> { +fn func1() -> Result<(), Box> { func2().map_err(mstrerr!(Func1ErrorFunc2, "func1 error calling func2"))?; let filename = "bar.txt"; do_some_io().map_err(mstrerr!(Func1ErrorIO, "Error reading '{}'", filename))?; Ok(()) } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { if let Err(e) = func1() { if let Some(s) = e.downcast_ref::>() { eprintln!("Func1ErrorIO:\n{:?}", s); diff --git a/src/lib.rs b/src/lib.rs index f378585..d68a1b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,18 +32,18 @@ //! use std::io; //! use std::result::Result; //! -//! fn do_some_io() -> Result<(), Box> { +//! fn do_some_io() -> Result<(), Box> { //! Err(io::Error::from(io::ErrorKind::NotFound))?; //! Ok(()) //! } //! -//! fn func2() -> Result<(), Box> { +//! fn func2() -> Result<(), Box> { //! let filename = "foo.txt"; //! do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; //! Ok(()) //! } //! -//! fn func1() -> Result<(), Box> { +//! fn func1() -> Result<(), Box> { //! func2().map_err(mstrerr!("func1 error"))?; //! Ok(()) //! } @@ -74,12 +74,12 @@ //! use std::io; //! use std::result::Result; //! -//! fn do_some_io() -> Result<(), Box> { +//! fn do_some_io() -> Result<(), Box> { //! Err(io::Error::from(io::ErrorKind::NotFound))?; //! Ok(()) //! } //! -//! fn func3() -> Result<(), Box> { +//! fn func3() -> Result<(), Box> { //! let filename = "foo.txt"; //! do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; //! Ok(()) @@ -246,14 +246,14 @@ impl ChainError { /// use std::error::Error; /// use std::io; /// - /// fn do_some_io() -> Result<(), Box> { + /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) /// } /// /// derive_str_cherr!(Func2Error); /// - /// fn func2() -> Result<(), Box> { + /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; /// Ok(()) @@ -261,7 +261,7 @@ impl ChainError { /// /// derive_str_cherr!(Func1Error); /// - /// fn func1() -> Result<(), Box> { + /// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -351,14 +351,14 @@ impl ChainError { /// use std::error::Error; /// use std::io; /// - /// fn do_some_io() -> Result<(), Box> { + /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) /// } /// /// derive_str_cherr!(Func2Error); /// - /// fn func2() -> Result<(), Box> { + /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; /// Ok(()) @@ -801,7 +801,7 @@ macro_rules! minto_cherr { /// # } /// # } /// # } -/// fn do_some_stuff() -> Result<(), Box> { +/// fn do_some_stuff() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) /// } @@ -850,17 +850,17 @@ macro_rules! cherr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # fn do_some_io() -> Result<(), Box> { +/// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// fn func2() -> Result<(), Box> { +/// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; /// Ok(()) /// } /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!("func1 error"))?; /// Ok(()) /// } @@ -891,13 +891,13 @@ macro_rules! cherr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # fn do_some_io() -> Result<(), Box> { +/// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } /// derive_str_cherr!(Func2Error); /// -/// fn func2() -> Result<(), Box> { +/// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; /// Ok(()) @@ -905,7 +905,7 @@ macro_rules! cherr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -961,7 +961,7 @@ macro_rules! mstrerr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -1009,7 +1009,7 @@ macro_rules! strerr { /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; -/// # fn do_some_io() -> Result<(), Box> { +/// # fn do_some_io() -> Result<(), Box> { /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } @@ -1023,7 +1023,7 @@ macro_rules! strerr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } diff --git a/tests/test_iter.rs b/tests/test_iter.rs index c451f7f..488ed0b 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -4,7 +4,7 @@ use std::fmt::Write; use std::io; #[test] -fn test_iter() -> Result<(), Box> { +fn test_iter() -> Result<(), Box> { let err = io::Error::from(io::ErrorKind::NotFound); let err = cherr!(err, "1"); let err = cherr!(err, "2"); @@ -31,7 +31,7 @@ fn test_iter() -> Result<(), Box> { } #[test] -fn test_find_cause() -> Result<(), Box> { +fn test_find_cause() -> Result<(), Box> { let err = io::Error::from(io::ErrorKind::NotFound); let err = cherr!(err, "1"); let err = cherr!(err, "2"); @@ -48,7 +48,7 @@ fn test_find_cause() -> Result<(), Box> { } #[test] -fn test_root_cause() -> Result<(), Box> { +fn test_root_cause() -> Result<(), Box> { let err = io::Error::from(io::ErrorKind::NotFound); let err = cherr!(err, "1"); let err = cherr!(err, "2"); From db7683f90fc1c68851dc53a2c2702494978b8726 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 3 Mar 2020 15:44:01 +0100 Subject: [PATCH 24/85] .github/workflows/gh-pages.yml: cache mdbook build --- .github/workflows/gh-pages.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 8661869..da9f0e5 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -2,18 +2,30 @@ name: github pages on: push: - branches: - - master + tags: + - '*' jobs: deploy: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 + + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - name: Build mdbook - run: cargo install mdbook - + run: cargo install mdbook + - name: Build run: mdbook build From def8d8c148c3c3db0d33f834dd00d6608f9d6234 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 3 Mar 2020 15:49:24 +0100 Subject: [PATCH 25/85] remove cache keys --- .github/workflows/gh-pages.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index da9f0e5..0ef7bb1 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -15,13 +15,11 @@ jobs: uses: actions/cache@v1 with: path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - name: Cache cargo index uses: actions/cache@v1 with: path: ~/.cargo/git - key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - name: Build mdbook run: cargo install mdbook From 24900e47d90a2d64e366b6a948c32d42087931c7 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 3 Mar 2020 15:57:26 +0100 Subject: [PATCH 26/85] .github/workflows/gh-pages.yml: cache bi-weekly --- .github/workflows/gh-pages.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 0ef7bb1..36952c2 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -11,15 +11,26 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Set CURRENT_TWO_WEEKS for use in cache keys + run: echo "::set-env name=CURRENT_TWO_WEEKS::$(($(date +%V) / 2))" + - name: Cache cargo registry uses: actions/cache@v1 with: path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ env.CURRENT_TWO_WEEKS }} - name: Cache cargo index uses: actions/cache@v1 with: path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ env.CURRENT_TWO_WEEKS }} + + - name: Cache mdbook binary + uses: actions/cache@v1 + with: + path: ~/.cargo/bin/mdbook + key: ${{ runner.os }}-cargo-mdbook-${{ env.CURRENT_TWO_WEEKS }} - name: Build mdbook run: cargo install mdbook From 174d81c8d95fa1afd727946dfd6164f20a9423fe Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 3 Jun 2020 14:01:44 +0200 Subject: [PATCH 27/85] add code coverage via cargo tarpaulin and coveralls --- .github/workflows/coverage.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..f4b77d5 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,22 @@ +name: coverage + +on: [ "push" , "pull_request" ] +jobs: + test: + name: coverage + runs-on: ubuntu-latest + container: + image: xd009642/tarpaulin + options: --security-opt seccomp=unconfined + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Generate code coverage + run: | + cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Lcov --output-dir coverage + + - name: Upload to coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} From 241502de75f10d962d4879a189b226b22351a16e Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 3 Jun 2020 14:08:24 +0200 Subject: [PATCH 28/85] remove `main` in doc tests and `cargo fmt` the doc tests --- src/lib.rs | 138 ++++++++++++++++++++++------------------------------- 1 file changed, 56 insertions(+), 82 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d68a1b6..e0bd397 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,23 +48,22 @@ //! Ok(()) //! } //! -//! fn main() { -//! if let Err(e) = func1() { -//! #[cfg(not(windows))] -//! assert_eq!( -//! format!("\n{:?}\n", e), r#" -//! src/lib.rs:20: func1 error +//! if let Err(e) = func1() { +//! #[cfg(not(windows))] +//! assert_eq!( +//! format!("\n{:?}\n", e), +//! r#" +//! src/lib.rs:21: func1 error //! Caused by: -//! src/lib.rs:15: Error reading 'foo.txt' +//! src/lib.rs:16: Error reading 'foo.txt' //! Caused by: //! Kind(NotFound) //! "# -//! ); -//! } +//! ); +//! } //! # else { //! # unreachable!(); //! # } -//! } //! ``` //! //! @@ -119,52 +118,48 @@ //! Ok(()) //! } //! -//! fn main() { -//! if let Err(e) = func1() { -//! assert!( -//! match e.kind() { -//! Func1Error::Func2 => { -//! eprintln!("Main Error Report: func1 error calling func2"); -//! true -//! } -//! Func1Error::IO(filename) => { -//! eprintln!("Main Error Report: func1 error reading '{}'", filename); -//! false -//! } -//! } -//! ); -//! -//! assert!(e.find_chain_cause::().is_some()); -//! -//! if let Some(e) = e.find_chain_cause::() { -//! eprintln!("\nError reported by Func2Error: {}", e) +//! if let Err(e) = func1() { +//! assert!(match e.kind() { +//! Func1Error::Func2 => { +//! eprintln!("Main Error Report: func1 error calling func2"); +//! true //! } -//! -//! -//! assert!(e.root_cause().is_some()); -//! -//! if let Some(e) = e.root_cause() { -//! let io_error = e.downcast_ref::().unwrap(); -//! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); +//! Func1Error::IO(filename) => { +//! eprintln!("Main Error Report: func1 error reading '{}'", filename); +//! false //! } +//! }); //! -//! #[cfg(not(windows))] -//! assert_eq!( -//! format!("\n{:?}\n", e), r#" -//! src/lib.rs:47: func1 error calling func2 +//! assert!(e.find_chain_cause::().is_some()); +//! +//! if let Some(e) = e.find_chain_cause::() { +//! eprintln!("\nError reported by Func2Error: {}", e) +//! } +//! +//! assert!(e.root_cause().is_some()); +//! +//! if let Some(e) = e.root_cause() { +//! let io_error = e.downcast_ref::().unwrap(); +//! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); +//! } +//! +//! #[cfg(not(windows))] +//! assert_eq!( +//! format!("\n{:?}\n", e), +//! r#" +//! src/lib.rs:48: func1 error calling func2 //! Caused by: -//! src/lib.rs:22: Func2Error(func2 error: calling func3) +//! src/lib.rs:23: Func2Error(func2 error: calling func3) //! Caused by: -//! src/lib.rs:15: Error reading 'foo.txt' +//! src/lib.rs:16: Error reading 'foo.txt' //! Caused by: //! Kind(NotFound) //! "# -//! ); -//! } +//! ); +//! } //! # else { //! # unreachable!(); //! # } -//! } //! ``` #![deny( @@ -266,22 +261,19 @@ impl ChainError { /// Ok(()) /// } /// - /// fn main() { - /// if let Err(e) = func1() { - /// if let Some(f1err) = e.downcast_chain_ref::() { + /// if let Err(e) = func1() { + /// if let Some(f1err) = e.downcast_chain_ref::() { + /// assert!(f1err.find_cause::().is_some()); /// - /// assert!(f1err.find_cause::().is_some()); - /// - /// assert!(f1err.find_chain_cause::().is_some()); - /// } + /// assert!(f1err.find_chain_cause::().is_some()); + /// } /// # else { /// # panic!(); /// # } - /// } + /// } /// # else { /// # unreachable!(); /// # } - /// } /// ``` #[inline] pub fn find_cause(&self) -> Option<&U> { @@ -386,17 +378,15 @@ impl ChainError { /// Ok(()) /// } /// - /// fn main() { - /// if let Err(e) = func1() { - /// match e.kind() { - /// Func1ErrorKind::Func2 => {} - /// Func1ErrorKind::IO(filename) => panic!(), - /// } + /// if let Err(e) = func1() { + /// match e.kind() { + /// Func1ErrorKind::Func2 => {} + /// Func1ErrorKind::IO(filename) => panic!(), /// } + /// } /// # else { /// # unreachable!(); /// # } - /// } /// ``` #[inline] pub fn kind(&self) -> &T { @@ -406,8 +396,6 @@ impl ChainError { /// Returns an Iterator over all error causes/sources /// /// # Example - /// - /// #[inline] pub fn iter(&self) -> impl Iterator { ErrorIter { @@ -769,17 +757,15 @@ macro_rules! minto_cherr { /// } /// /// fn func() -> ChainResult<(), FooError> { -/// if ! do_some_stuff() { +/// if !do_some_stuff() { /// Err(cherr!(FooError::Baz("Error")))?; /// } /// Ok(()) /// } -/// # pub fn main() { /// # match func().unwrap_err().kind() { /// # FooError::Baz(s) if s == &"Error" => {} /// # _ => panic!(), /// # } -/// # } /// ``` /// /// Additionally an error cause can be added. @@ -807,17 +793,13 @@ macro_rules! minto_cherr { /// } /// /// fn func() -> ChainResult<(), FooError> { -/// do_some_stuff().map_err( -/// |e| cherr!(e, FooError::Baz("Error")) -/// )?; +/// do_some_stuff().map_err(|e| cherr!(e, FooError::Baz("Error")))?; /// Ok(()) /// } -/// # pub fn main() { /// # match func().unwrap_err().kind() { /// # FooError::Baz(s) if s == &"Error" => {} /// # _ => panic!(), /// # } -/// # } /// ``` #[macro_export] macro_rules! cherr { @@ -865,14 +847,13 @@ macro_rules! cherr { /// Ok(()) /// } /// -/// # fn main() { /// # if let Err(e) = func1() { /// # #[cfg(not(windows))] /// # assert_eq!( /// # format!("\n{:?}\n", e), r#" -/// # src/lib.rs:18: func1 error +/// # src/lib.rs:19: func1 error /// # Caused by: -/// # src/lib.rs:13: Error reading 'foo.txt' +/// # src/lib.rs:14: Error reading 'foo.txt' /// # Caused by: /// # Kind(NotFound) /// # "# @@ -880,7 +861,6 @@ macro_rules! cherr { /// # } else { /// # unreachable!(); /// # } -/// # } /// ``` /// /// `mstrerr!()` can also be used to map a new `ChainError`, where T was defined with @@ -909,7 +889,6 @@ macro_rules! cherr { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } -/// # fn main() { /// # if let Err(e) = func1() { /// # if let Some(f1err) = e.downcast_chain_ref::() { /// # assert!(f1err.find_cause::>().is_some()); @@ -920,7 +899,6 @@ macro_rules! cherr { /// # } else { /// # unreachable!(); /// # } -/// # } /// ``` #[macro_export] macro_rules! mstrerr { @@ -965,7 +943,6 @@ macro_rules! mstrerr { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } -/// # fn main() { /// # if let Err(e) = func1() { /// # if let Some(f1err) = e.downcast_chain_ref::() { /// # assert!(f1err.find_cause::>().is_some()); @@ -976,7 +953,6 @@ macro_rules! mstrerr { /// # } else { /// # unreachable!(); /// # } -/// # } /// ``` #[macro_export] macro_rules! strerr { @@ -1027,7 +1003,6 @@ macro_rules! strerr { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } -/// # fn main() { /// # if let Err(e) = func1() { /// # if let Some(f1err) = e.downcast_chain_ref::() { /// # assert!(f1err.find_cause::>().is_some()); @@ -1038,7 +1013,6 @@ macro_rules! strerr { /// # } else { /// # unreachable!(); /// # } -/// # } /// ``` #[macro_export] macro_rules! derive_str_cherr { @@ -1070,8 +1044,8 @@ macro_rules! derive_str_cherr { /// # Examples /// /// ```rust -/// use std::io; /// use chainerror::*; +/// use std::io; /// /// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { /// return Err(io::Error::from(io::ErrorKind::NotFound)); From 935eb658cf33ca4509e0618f27bebee61b199d30 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 3 Jun 2020 14:41:42 +0200 Subject: [PATCH 29/85] make test pass with --all-features --- src/lib.rs | 3 +++ tests/test_iter.rs | 28 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e0bd397..817dde2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,7 @@ //! } //! //! if let Err(e) = func1() { +//! # #[cfg(not(feature = "no-debug-cause"))] //! #[cfg(not(windows))] //! assert_eq!( //! format!("\n{:?}\n", e), @@ -143,6 +144,7 @@ //! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); //! } //! +//! # #[cfg(not(feature = "no-debug-cause"))] //! #[cfg(not(windows))] //! assert_eq!( //! format!("\n{:?}\n", e), @@ -848,6 +850,7 @@ macro_rules! cherr { /// } /// /// # if let Err(e) = func1() { +/// # #[cfg(not(feature = "no-debug-cause"))] /// # #[cfg(not(windows))] /// # assert_eq!( /// # format!("\n{:?}\n", e), r#" diff --git a/tests/test_iter.rs b/tests/test_iter.rs index 488ed0b..ce100d2 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -1,10 +1,11 @@ use chainerror::*; use std::error::Error; -use std::fmt::Write; use std::io; +#[cfg(not(feature = "display-cause"))] #[test] fn test_iter() -> Result<(), Box> { + use std::fmt::Write; let err = io::Error::from(io::ErrorKind::NotFound); let err = cherr!(err, "1"); let err = cherr!(err, "2"); @@ -30,6 +31,31 @@ fn test_iter() -> Result<(), Box> { Ok(()) } +#[cfg(feature = "display-cause")] +#[test] +fn test_iter() -> Result<(), Box> { + let err = io::Error::from(io::ErrorKind::NotFound); + let err = cherr!(err, "1"); + let err = cherr!(err, "2"); + let err = cherr!(err, "3"); + let err = cherr!(err, "4"); + let err = cherr!(err, "5"); + let err = cherr!(err, "6"); + + let res = err.to_string(); + + assert_eq!(res, "6\nCaused by:\n5\nCaused by:\n4\nCaused by:\n3\nCaused by:\n2\nCaused by:\n1\nCaused by:\nentity not found"); + + let io_error: Option<&io::Error> = err + .iter() + .filter_map(Error::downcast_ref::) + .next(); + + assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound); + + Ok(()) +} + #[test] fn test_find_cause() -> Result<(), Box> { let err = io::Error::from(io::ErrorKind::NotFound); From fb99427f197bf06b6d73c043847558bea18720ac Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 3 Jun 2020 14:30:52 +0200 Subject: [PATCH 30/85] coverage: don't use all features --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index f4b77d5..1925bb5 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -14,7 +14,7 @@ jobs: - name: Generate code coverage run: | - cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Lcov --output-dir coverage + cargo tarpaulin --verbose --workspace --timeout 120 --out Lcov --output-dir coverage - name: Upload to coveralls uses: coverallsapp/github-action@master From e2815321bcabc43a3ff17d56cba7a7dc1cdfbd28 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 3 Jun 2020 14:30:30 +0200 Subject: [PATCH 31/85] add github CI and coveralls badges to README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9252c1d..78837ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # chainerror - +[![Rust](https://github.com/haraldh/chainerror/workflows/Rust/badge.svg)](https://github.com/haraldh/chainerror/actions?query=workflow%3ARust) [![Build Status](https://travis-ci.org/haraldh/chainerror.svg?branch=master)](https://travis-ci.org/haraldh/chainerror) +[![Coverage Status](https://coveralls.io/repos/github/haraldh/chainerror/badge.svg?branch=master)](https://coveralls.io/github/haraldh/chainerror?branch=master) [![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/) From 356335e80740effe162c6d419186be2cec73825d Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Aug 2020 16:45:24 +0200 Subject: [PATCH 32/85] use `#[track_caller]` and `Location` --- .github/workflows/gh-pages.yml | 6 + .github/workflows/rust.yml | 87 ++++- .travis.yml | 22 -- Cargo.toml | 14 +- README.md | 193 ++++++++--- examples/example.rs | 12 +- examples/tutorial10.rs | 8 +- examples/tutorial11.rs | 8 +- examples/tutorial12.rs | 8 +- examples/tutorial13.rs | 8 +- examples/tutorial15.rs | 17 +- examples/tutorial2.rs | 6 +- examples/tutorial3.rs | 6 +- examples/tutorial4.rs | 6 +- examples/tutorial5.rs | 6 +- examples/tutorial6.rs | 6 +- examples/tutorial7.rs | 6 +- examples/tutorial8.rs | 6 +- examples/tutorial9.rs | 8 +- src/lib.rs | 568 +++++++++++---------------------- tests/test_iter.rs | 62 ++-- 21 files changed, 515 insertions(+), 548 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 36952c2..b8d27bd 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -35,6 +35,12 @@ jobs: - name: Build mdbook run: cargo install mdbook + - name: Build cargo-readme + run: cargo install cargo-readme + + - name: Build README.md + run: cargo readme > README.md + - name: Build run: mdbook build diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 312cbfd..9e148b2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,15 +1,86 @@ name: Rust -on: [push] +on: + # Trigger the workflow on push or pull request, + # but only for the master branch + push: + branches: + - master + pull_request: + branches: + - master + release: + types: + - created jobs: build: - runs-on: ubuntu-latest - + strategy: + matrix: + version: + - 1.46.0 + - stable + - beta + - nightly steps: - - uses: actions/checkout@v2 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose + - uses: actions/checkout@v1 + - name: Install toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.version }} + default: true + profile: minimal + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose + - name: Build --all-features + run: cargo build --verbose --all-features + - name: Run tests --all-features + run: cargo test --verbose --all-features + + fmt: + name: cargo fmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + components: rustfmt + toolchain: stable + profile: minimal + override: true + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + clippy: + name: cargo clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + components: clippy + toolchain: stable + profile: minimal + override: true + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings + + readme: + name: cargo readme + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + - run: cargo install cargo-readme + - run: cargo readme > README.md && git diff --exit-code diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b39f048..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: rust - -branches: - except: - - gh-pages - -rust: -- stable -- nightly - -os: -- linux -- windows - -script: -- cargo build --all -- cargo test --all - -matrix: - allow_failures: - - rust: nightly - fast_finish: true diff --git a/Cargo.toml b/Cargo.toml index 327757b..6224d65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.5.0" +version = "0.6.0-alpha.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" @@ -16,10 +16,14 @@ exclude = [ ".gitignore", "examples/*", "booksrc/*", "book.toml", "theme/*", "git-deploy-branch.sh", ".travis.yml" ] [badges] -travis-ci = { repository = "haraldh/chainerror" } +# See https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section +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 = [ ] -no-fileline = [] +default = [ "location", "debug-cause" ] +location = [] display-cause = [] -no-debug-cause = [] +debug-cause = [] diff --git a/README.md b/README.md index 78837ee..9b17c55 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,148 @@ -# chainerror -[![Rust](https://github.com/haraldh/chainerror/workflows/Rust/badge.svg)](https://github.com/haraldh/chainerror/actions?query=workflow%3ARust) -[![Build Status](https://travis-ci.org/haraldh/chainerror.svg?branch=master)](https://travis-ci.org/haraldh/chainerror) -[![Coverage Status](https://coveralls.io/repos/github/haraldh/chainerror/badge.svg?branch=master)](https://coveralls.io/github/haraldh/chainerror?branch=master) -[![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/) +[![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") +![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg) -`chainerror` provides an error backtrace like `failure` without doing a real backtrace, so even after you `strip` your +# chainerror + +`chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your binaries, you still have the error backtrace. `chainerror` has no dependencies! -`chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace. +`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. Debug information is worth it! -Now continue reading the -[Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) +### Features -## Example: -Output: +`default = [ "location", "debug-cause" ]` -~~~ +`location` +: store the error location + +`display-cause` +: turn on printing a backtrace of the errors in `Display` + +`debug-cause` +: print a backtrace of the errors in `Debug` + + +## Tutorial + +Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) + +## Examples + +```console $ cargo run -q --example example Main Error Report: func1 error calling func2 Error reported by Func2Error: func2 error: calling func3 - The root cause was: std::io::Error: Kind( NotFound ) Debug Error: -examples/example.rs:45: func1 error calling func2 +examples/example.rs:46:13: func1 error calling func2 Caused by: -examples/example.rs:20: Func2Error(func2 error: calling func3) +examples/example.rs:21:13: Func2Error(func2 error: calling func3) Caused by: -examples/example.rs:13: Error reading 'foo.txt' +examples/example.rs:14:18: Error reading 'foo.txt' Caused by: Kind(NotFound) -~~~ +Alternative Debug Error: +ChainError { + occurrence: Some( + "examples/example.rs:46:13", + ), + kind: func1 error calling func2, + source: Some( + ChainError { + occurrence: Some( + "examples/example.rs:21:13", + ), + kind: Func2Error(func2 error: calling func3), + source: Some( + ChainError { + occurrence: Some( + "examples/example.rs:14:18", + ), + kind: "Error reading \'foo.txt\'", + source: Some( + Kind( + NotFound, + ), + ), + }, + ), + }, + ), +} +``` -~~~rust,ignore -use chainerror::*; +```rust +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box> { +fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func3() -> Result<(), Box> { +fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; + do_some_io().cherr(format!("Error reading '{}'", filename))?; + Ok(()) +} + +fn func1() -> Result<(), Box> { + func2().cherr("func1 error")?; + Ok(()) +} + +if let Err(e) = func1() { + #[cfg(not(windows))] + assert_eq!( + format!("\n{:?}\n", e), + r#" +src/lib.rs:21:13: func1 error +Caused by: +src/lib.rs:16:18: Error reading 'foo.txt' +Caused by: +Kind(NotFound) +"# + ); +} +``` + + +```rust +use chainerror::prelude::v1::*; +use std::error::Error; +use std::io; +use std::result::Result; + +fn do_some_io() -> Result<(), Box> { + Err(io::Error::from(io::ErrorKind::NotFound))?; + Ok(()) +} + +fn func3() -> Result<(), Box> { + let filename = "foo.txt"; + do_some_io().cherr(format!("Error reading '{}'", filename))?; Ok(()) } derive_str_cherr!(Func2Error); fn func2() -> ChainResult<(), Func2Error> { - func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; + func3().cherr(Func2Error("func2 error: calling func3".into()))?; Ok(()) } @@ -88,43 +167,51 @@ impl ::std::fmt::Debug for Func1Error { } fn func1() -> ChainResult<(), Func1Error> { - func2().map_err(|e| cherr!(e, Func1Error::Func2))?; + func2().cherr(Func1Error::Func2)?; let filename = String::from("bar.txt"); - do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; + do_some_io().cherr(Func1Error::IO(filename))?; Ok(()) } -fn main() { - if let Err(e) = func1() { - match e.kind() { - Func1Error::Func2 => eprintln!("Main Error Report: func1 error calling func2"), - Func1Error::IO(filename) => { - eprintln!("Main Error Report: func1 error reading '{}'", filename) - } +if let Err(e) = func1() { + assert!(match e.kind() { + Func1Error::Func2 => { + eprintln!("Main Error Report: func1 error calling func2"); + true } - - if let Some(e) = e.find_chain_cause::() { - eprintln!("\nError reported by Func2Error: {}", e) + Func1Error::IO(filename) => { + eprintln!("Main Error Report: func1 error reading '{}'", filename); + false } + }); - if let Some(e) = e.root_cause() { - let ioerror = e.downcast_ref::().unwrap(); - eprintln!("\nThe root cause was: std::io::Error: {:#?}", ioerror); - } + assert!(e.find_chain_cause::().is_some()); - eprintln!("\nDebug Error:\n{:?}", e); + if let Some(e) = e.find_chain_cause::() { + eprintln!("\nError reported by Func2Error: {}", e) } + + assert!(e.root_cause().is_some()); + + if let Some(e) = e.root_cause() { + let io_error = e.downcast_ref::().unwrap(); + eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); + } + + #[cfg(not(windows))] + assert_eq!( + format!("\n{:?}\n", e), + r#" +src/lib.rs:48:13: func1 error calling func2 +Caused by: +src/lib.rs:23:13: Func2Error(func2 error: calling func3) +Caused by: +src/lib.rs:16:18: Error reading 'foo.txt' +Caused by: +Kind(NotFound) +"# + ); } +``` -~~~ - -## Features - -`no-fileline` -: completely turn off storing filename and line - -`display-cause` -: turn on printing a backtrace of the errors in `Display` - -`no-debug-cause` -: turn off printing a backtrace of the errors in `Debug` +License: MIT/Apache-2.0 diff --git a/examples/example.rs b/examples/example.rs index 99f965e..4044155 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::io; use std::result::Result; -use chainerror::*; +use chainerror::prelude::v1::*; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; @@ -11,14 +11,14 @@ fn do_some_io() -> Result<(), Box> { fn func3() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; + do_some_io().cherr(format!("Error reading '{}'", filename))?; Ok(()) } derive_str_cherr!(Func2Error); fn func2() -> ChainResult<(), Func2Error> { - func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; + func3().cherr(Func2Error(format!("func2 error: calling func3")))?; Ok(()) } @@ -43,9 +43,9 @@ impl ::std::fmt::Debug for Func1Error { } fn func1() -> ChainResult<(), Func1Error> { - func2().map_err(|e| cherr!(e, Func1Error::Func2))?; + func2().cherr(Func1Error::Func2)?; let filename = String::from("bar.txt"); - do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; + do_some_io().cherr(Func1Error::IO(filename))?; Ok(()) } @@ -68,5 +68,7 @@ fn main() { } eprintln!("\nDebug Error:\n{:?}", e); + + eprintln!("\nAlternative Debug Error:\n{:#?}", e); } } diff --git a/examples/tutorial10.rs b/examples/tutorial10.rs index cf13909..4ee8f44 100644 --- a/examples/tutorial10.rs +++ b/examples/tutorial10.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -12,7 +12,7 @@ derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -33,9 +33,9 @@ impl ::std::fmt::Display for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} fn func1() -> ChainResult<(), Func1ErrorKind> { - func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + func2().cherr(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); - do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO(filename)))?; + do_some_io().cherr(Func1ErrorKind::IO(filename))?; Ok(()) } diff --git a/examples/tutorial11.rs b/examples/tutorial11.rs index 5f85c23..42e5de8 100644 --- a/examples/tutorial11.rs +++ b/examples/tutorial11.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -12,7 +12,7 @@ derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -39,9 +39,9 @@ impl ::std::fmt::Debug for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} fn func1() -> ChainResult<(), Func1ErrorKind> { - func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + func2().cherr(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); - do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO(filename)))?; + do_some_io().cherr(Func1ErrorKind::IO(filename))?; Ok(()) } diff --git a/examples/tutorial12.rs b/examples/tutorial12.rs index f5e38de..ce3cea6 100644 --- a/examples/tutorial12.rs +++ b/examples/tutorial12.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -12,7 +12,7 @@ derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -39,9 +39,9 @@ impl ::std::fmt::Debug for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} fn func1() -> ChainResult<(), Func1ErrorKind> { - func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + func2().cherr(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); - do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO(filename)))?; + do_some_io().cherr(Func1ErrorKind::IO(filename))?; Ok(()) } diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index c345157..29a0374 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -1,5 +1,5 @@ pub mod mycrate { - use chainerror::*; + use chainerror::prelude::v1::*; use std::io; fn do_some_io() -> std::result::Result<(), Box> { @@ -11,7 +11,7 @@ pub mod mycrate { fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -35,9 +35,9 @@ pub mod mycrate { } pub fn func1() -> Result<()> { - func2().map_err(|e| cherr!(e, ErrorKind::Func2))?; + func2().cherr(ErrorKind::Func2)?; let filename = String::from("bar.txt"); - do_some_io().map_err(|e| cherr!(e, ErrorKind::IO(filename)))?; + do_some_io().cherr(ErrorKind::IO(filename))?; Ok(()) } } diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs index 581840c..5130bfc 100644 --- a/examples/tutorial15.rs +++ b/examples/tutorial15.rs @@ -1,7 +1,7 @@ pub mod mycrate { use std::io; - use chainerror::*; + use chainerror::prelude::v1::*; fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { Err(io::Error::from(io::ErrorKind::NotFound))?; @@ -12,8 +12,7 @@ pub mod mycrate { fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; - do_some_io(filename) - .map_err(|e| cherr!(e, Func2Error(format!("Error reading '{}'", filename))))?; + do_some_io(filename).cherr(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -82,21 +81,19 @@ pub mod mycrate { } pub fn func1() -> Result<()> { - func2().map_err(|e| cherr!(e, ErrorKind::from(&e)))?; + func2().map_err(|e| ErrorKind::from(&e))?; let filename = "bar.txt"; - do_some_io(filename) - .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; - do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; - do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; - do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; + do_some_io(filename).map_cherr(|e| ErrorKind::from_io_error(&e, filename.into()))?; + do_some_io(filename).map_cherr(|_| ErrorKind::IO(filename.into()))?; + do_some_io(filename).map_cherr(|e| ErrorKind::from(e))?; Ok(()) } pub fn super_func1() -> Result<()> { - func1().map_err(minto_cherr!(ErrorKind))?; + func1().map_cherr(|e| ErrorKind::from(e))?; Ok(()) } } diff --git a/examples/tutorial2.rs b/examples/tutorial2.rs index b13f0cc..140862f 100644 --- a/examples/tutorial2.rs +++ b/examples/tutorial2.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; @@ -11,14 +11,14 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { if let Err(e) = do_some_io() { - Err(cherr!(e, "func2 error"))?; + Err(e).cherr("func2 error")?; } Ok(()) } fn func1() -> Result<(), Box> { if let Err(e) = func2() { - Err(cherr!(e, "func1 error"))?; + Err(e).cherr("func1 error")?; } Ok(()) } diff --git a/examples/tutorial3.rs b/examples/tutorial3.rs index 6da067c..9096e07 100644 --- a/examples/tutorial3.rs +++ b/examples/tutorial3.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; @@ -10,12 +10,12 @@ fn do_some_io() -> Result<(), Box> { } fn func2() -> Result<(), Box> { - do_some_io().map_err(|e| cherr!(e, "func2 error"))?; + do_some_io().cherr("func2 error")?; Ok(()) } fn func1() -> Result<(), Box> { - func2().map_err(|e| cherr!(e, "func1 error"))?; + func2().cherr("func1 error")?; Ok(()) } diff --git a/examples/tutorial4.rs b/examples/tutorial4.rs index e1225c0..3b5fc15 100644 --- a/examples/tutorial4.rs +++ b/examples/tutorial4.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -10,12 +10,12 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; + do_some_io().cherr(format!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!("func1 error"))?; + func2().cherr("func1 error")?; Ok(()) } diff --git a/examples/tutorial5.rs b/examples/tutorial5.rs index cb80569..edabbbf 100644 --- a/examples/tutorial5.rs +++ b/examples/tutorial5.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -10,7 +10,7 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; + do_some_io().cherr(format!("Error reading '{}'", filename))?; Ok(()) } @@ -18,7 +18,7 @@ fn func1() -> Result<(), Box> { if let Err(e) = func2() { if let Some(s) = e.source() { eprintln!("func2 failed because of '{}'", s); - Err(e).map_err(mstrerr!("func1 error"))?; + Err(e).cherr("func1 error")?; } } Ok(()) diff --git a/examples/tutorial6.rs b/examples/tutorial6.rs index 996540c..f8c6549 100644 --- a/examples/tutorial6.rs +++ b/examples/tutorial6.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -10,12 +10,12 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; + do_some_io().cherr(format!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!("func1 error"))?; + func2().cherr("func1 error")?; Ok(()) } diff --git a/examples/tutorial7.rs b/examples/tutorial7.rs index b36fa8b..11658f2 100644 --- a/examples/tutorial7.rs +++ b/examples/tutorial7.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -10,12 +10,12 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; + do_some_io().cherr(format!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!("func1 error"))?; + func2().cherr(format!("func1 error"))?; Ok(()) } diff --git a/examples/tutorial8.rs b/examples/tutorial8.rs index 1644a32..f55b83b 100644 --- a/examples/tutorial8.rs +++ b/examples/tutorial8.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -12,14 +12,14 @@ derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } derive_str_cherr!(Func1Error); fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!(Func1Error, "func1 error"))?; + func2().cherr(Func1Error(format!("func1 error")))?; Ok(()) } diff --git a/examples/tutorial9.rs b/examples/tutorial9.rs index 99b0f23..374cb77 100644 --- a/examples/tutorial9.rs +++ b/examples/tutorial9.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; use std::result::Result; @@ -12,7 +12,7 @@ derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -20,9 +20,9 @@ derive_str_cherr!(Func1ErrorFunc2); derive_str_cherr!(Func1ErrorIO); fn func1() -> Result<(), Box> { - func2().map_err(mstrerr!(Func1ErrorFunc2, "func1 error calling func2"))?; + func2().cherr(Func1ErrorFunc2(format!("func1 error calling func2")))?; let filename = "bar.txt"; - do_some_io().map_err(mstrerr!(Func1ErrorIO, "Error reading '{}'", filename))?; + do_some_io().cherr(Func1ErrorIO(format!("Error reading '{}'", filename)))?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 817dde2..71fbf3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,21 +3,25 @@ //! //! `chainerror` has no dependencies! //! -//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace. +//! `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. //! +//! Debug information is worth it! +//! //! ## Features //! -//! `no-fileline` -//! : completely turn off storing filename and line +//! `default = [ "location", "debug-cause" ]` +//! +//! `location` +//! : store the error location //! //! `display-cause` //! : turn on printing a backtrace of the errors in `Display` //! -//! `no-debug-cause` -//! : turn off printing a backtrace of the errors in `Debug` +//! `debug-cause` +//! : print a backtrace of the errors in `Debug` //! //! //! # Tutorial @@ -26,8 +30,55 @@ //! //! # Examples //! +//! ```console +//! $ cargo run -q --example example +//! Main Error Report: func1 error calling func2 +//! +//! Error reported by Func2Error: func2 error: calling func3 +//! The root cause was: std::io::Error: Kind( +//! NotFound +//! ) +//! +//! Debug Error: +//! examples/example.rs:46:13: func1 error calling func2 +//! Caused by: +//! examples/example.rs:21:13: Func2Error(func2 error: calling func3) +//! Caused by: +//! examples/example.rs:14:18: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! Alternative Debug Error: +//! ChainError { +//! occurrence: Some( +//! "examples/example.rs:46:13", +//! ), +//! kind: func1 error calling func2, +//! source: Some( +//! ChainError { +//! occurrence: Some( +//! "examples/example.rs:21:13", +//! ), +//! kind: Func2Error(func2 error: calling func3), +//! source: Some( +//! ChainError { +//! occurrence: Some( +//! "examples/example.rs:14:18", +//! ), +//! kind: "Error reading \'foo.txt\'", +//! source: Some( +//! Kind( +//! NotFound, +//! ), +//! ), +//! }, +//! ), +//! }, +//! ), +//! } +//! ``` +//! //! ```rust -//! use chainerror::*; +//! use chainerror::prelude::v1::*; //! use std::error::Error; //! use std::io; //! use std::result::Result; @@ -39,24 +90,24 @@ //! //! fn func2() -> Result<(), Box> { //! let filename = "foo.txt"; -//! do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; +//! do_some_io().cherr(format!("Error reading '{}'", filename))?; //! Ok(()) //! } //! //! fn func1() -> Result<(), Box> { -//! func2().map_err(mstrerr!("func1 error"))?; +//! func2().cherr("func1 error")?; //! Ok(()) //! } //! //! if let Err(e) = func1() { -//! # #[cfg(not(feature = "no-debug-cause"))] +//! # #[cfg(feature = "debug-cause")] //! #[cfg(not(windows))] //! assert_eq!( //! format!("\n{:?}\n", e), //! r#" -//! src/lib.rs:21: func1 error +//! src/lib.rs:21:13: func1 error //! Caused by: -//! src/lib.rs:16: Error reading 'foo.txt' +//! src/lib.rs:16:18: Error reading 'foo.txt' //! Caused by: //! Kind(NotFound) //! "# @@ -69,7 +120,7 @@ //! //! //! ```rust -//! use chainerror::*; +//! use chainerror::prelude::v1::*; //! use std::error::Error; //! use std::io; //! use std::result::Result; @@ -81,14 +132,14 @@ //! //! fn func3() -> Result<(), Box> { //! let filename = "foo.txt"; -//! do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; +//! do_some_io().cherr(format!("Error reading '{}'", filename))?; //! Ok(()) //! } //! //! derive_str_cherr!(Func2Error); //! //! fn func2() -> ChainResult<(), Func2Error> { -//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; +//! func3().cherr(Func2Error("func2 error: calling func3".into()))?; //! Ok(()) //! } //! @@ -113,9 +164,9 @@ //! } //! //! fn func1() -> ChainResult<(), Func1Error> { -//! func2().map_err(|e| cherr!(e, Func1Error::Func2))?; +//! func2().cherr(Func1Error::Func2)?; //! let filename = String::from("bar.txt"); -//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; +//! do_some_io().cherr(Func1Error::IO(filename))?; //! Ok(()) //! } //! @@ -144,16 +195,16 @@ //! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); //! } //! -//! # #[cfg(not(feature = "no-debug-cause"))] +//! # #[cfg(feature = "no-debug-cause")] //! #[cfg(not(windows))] //! assert_eq!( //! format!("\n{:?}\n", e), //! r#" -//! src/lib.rs:48: func1 error calling func2 +//! src/lib.rs:48:13: func1 error calling func2 //! Caused by: -//! src/lib.rs:23: Func2Error(func2 error: calling func3) +//! src/lib.rs:23:13: Func2Error(func2 error: calling func3) //! Caused by: -//! src/lib.rs:16: Error reading 'foo.txt' +//! src/lib.rs:16:18: Error reading 'foo.txt' //! Caused by: //! Kind(NotFound) //! "# @@ -164,37 +215,29 @@ //! # } //! ``` -#![deny( - warnings, - absolute_paths_not_starting_with_crate, - deprecated_in_future, - keyword_idents, - macro_use_extern_crate, - missing_debug_implementations, - trivial_numeric_casts, - unused_extern_crates, - unused_import_braces, - unused_qualifications, - unused_results, - unused_labels, - unused_lifetimes, - unstable_features, - unreachable_pub, - future_incompatible, - missing_copy_implementations, - missing_doc_code_examples, - rust_2018_idioms, - rust_2018_compatibility -)] +#![deny(clippy::all)] +#![deny(clippy::integer_arithmetic)] +#![deny(missing_docs)] use std::any::TypeId; use std::error::Error; use std::fmt::{Debug, Display, Formatter, Result}; +use std::panic::Location; + +pub mod prelude { + //! convenience prelude + pub mod v1 { + //! convenience prelude + pub use crate::ChainErrorDown as _; + pub use crate::ResultTrait as _; + pub use crate::{derive_err_kind, derive_str_cherr, ChainError, ChainResult}; + } +} /// chains an inner error kind `T` with a causing error pub struct ChainError { - #[cfg(not(feature = "no-fileline"))] - occurrence: Option<&'static str>, + #[cfg(feature = "location")] + occurrence: Option, kind: T, error_cause: Option>, } @@ -203,13 +246,13 @@ pub struct ChainError { pub type ChainResult = std::result::Result>; impl ChainError { - #[cfg(not(feature = "no-fileline"))] - /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[cfg(feature = "location")] + /// Use the `cherr()` or `map_cherr()` Result methods instead of calling this directly #[inline] pub fn new( kind: T, error_cause: Option>, - occurrence: Option<&'static str>, + occurrence: Option, ) -> Self { Self { occurrence, @@ -218,13 +261,13 @@ impl ChainError { } } - #[cfg(feature = "no-fileline")] - /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[cfg(not(feature = "location"))] + /// Use the `cherr()` or `map_cherr()` Result methods instead of calling this directly #[inline] pub fn new( kind: T, error_cause: Option>, - _occurrence: Option<&'static str>, + _occurrence: Option, ) -> Self { Self { kind, error_cause } } @@ -239,7 +282,7 @@ impl ChainError { /// # Examples /// /// ```rust - /// use chainerror::*; + /// use chainerror::prelude::v1::*; /// use std::error::Error; /// use std::io; /// @@ -252,14 +295,14 @@ impl ChainError { /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; - /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; /// Ok(()) /// } /// /// derive_str_cherr!(Func1Error); /// /// fn func1() -> Result<(), Box> { - /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; + /// func2().cherr(Func1Error("func1 error".into()))?; /// Ok(()) /// } /// @@ -289,7 +332,7 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use chainerror::*; + /// # use chainerror::prelude::v1::*; /// # derive_str_cherr!(FooError); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing @@ -312,7 +355,7 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use chainerror::*; + /// # use chainerror::prelude::v1::*; /// # derive_str_cherr!(FooErrorKind); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing @@ -341,7 +384,7 @@ impl ChainError { /// # Examples /// /// ```rust - /// use chainerror::*; + /// use chainerror::prelude::v1::*; /// use std::error::Error; /// use std::io; /// @@ -354,7 +397,7 @@ impl ChainError { /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; - /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; /// Ok(()) /// } /// @@ -375,8 +418,8 @@ impl ChainError { /// # } /// /// fn func1() -> ChainResult<(), Func1ErrorKind> { - /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; - /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?; + /// func2().cherr(Func1ErrorKind::Func2)?; + /// do_some_io().cherr(Func1ErrorKind::IO("bar.txt".into()))?; /// Ok(()) /// } /// @@ -406,7 +449,54 @@ impl ChainError { } } -struct ErrorIter<'a> { +/// Convenience methods for `Result<>` to turn the error into a decorated ChainError +pub trait ResultTrait>> { + /// Decorate the error with a `kind` of type `T` and the source `Location` + fn cherr(self, kind: T) -> std::result::Result>; + + /// Decorate the error with a `kind` of type `T` produced with a `FnOnce` and the source `Location` + fn map_cherr T>( + self, + op: F, + ) -> std::result::Result>; +} + +impl>> ResultTrait + for std::result::Result +{ + #[track_caller] + fn cherr(self, kind: T) -> std::result::Result> { + match self { + Ok(t) => Ok(t), + Err(error_cause) => Err(ChainError::new( + kind, + Some(error_cause.into()), + Some(Location::caller().to_string()), + )), + } + } + + #[track_caller] + fn map_cherr T>( + self, + op: F, + ) -> std::result::Result> { + match self { + Ok(t) => Ok(t), + Err(error_cause) => { + let kind = op(&error_cause); + Err(ChainError::new( + kind, + Some(error_cause.into()), + Some(Location::caller().to_string()), + )) + } + } + } +} + +/// An iterator over all error causes/sources +pub struct ErrorIter<'a> { current: Option<&'a (dyn Error + 'static)>, } @@ -647,40 +737,56 @@ impl Display for ChainError { impl Debug for ChainError { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { - #[cfg(not(feature = "no-fileline"))] - { - if let Some(ref o) = self.occurrence { - Display::fmt(o, f)?; - } - } + if f.alternate() { + let mut f = f.debug_struct(&format!("ChainError<{}>", std::any::type_name::())); - if self.is_chain::() { - Display::fmt(&self.kind, f)?; + #[cfg(feature = "location")] + let f = f.field("occurrence", &self.occurrence); + + let f = f.field("kind", &self.kind); + + #[cfg(feature = "debug-cause")] + let f = f.field("source", &self.source()); + + f.finish() } else { - Debug::fmt(&self.kind, f)?; - } - - #[cfg(not(feature = "no-debug-cause"))] - { - if let Some(e) = self.source() { - writeln!(f, "\nCaused by:")?; - Debug::fmt(&e, f)?; + #[cfg(feature = "location")] + { + if let Some(ref o) = self.occurrence { + write!(f, "{}: ", o)?; + } } + + if TypeId::of::() == TypeId::of::() + || TypeId::of::<&str>() == TypeId::of::() + { + Display::fmt(&self.kind, f)?; + } else { + Debug::fmt(&self.kind, f)?; + } + + #[cfg(feature = "debug-cause")] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Debug::fmt(&e, f)?; + } + } + Ok(()) } - Ok(()) } } /// `ChainErrorFrom` is similar to `From` pub trait ChainErrorFrom: Sized { /// similar to From::from() - fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError; + 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<&'static str>) -> ChainError; + fn into_chain_error(self, line_filename: Option) -> ChainError; } impl IntoChainError for T @@ -688,7 +794,7 @@ where U: ChainErrorFrom, { #[inline] - fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError { + fn into_chain_error(self, line_filename: Option) -> ChainError { U::chain_error_from(self, line_filename) } } @@ -699,286 +805,12 @@ where U: 'static + Display + Debug, { #[inline] - fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError { + fn chain_error_from(t: T, line_filename: Option) -> ChainError { let e: U = t.into(); ChainError::new(e, None, line_filename) } } -/* -impl ChainErrorFrom for U - where - T: 'static + Error + Into> + Clone, - U: 'static + Display + Debug + From, -{ - #[inline] - fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError { - ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename) - } -} -*/ - -/// map into `ChainError` with `T::from(err)` -/// -/// adds `line!()` and `file!()` information -#[macro_export] -macro_rules! minto_cherr { - ( $k:ident ) => ( - |e| $crate::cherr!(e, $k::from(&e)) - ); - ( $enum:ident $(:: $enum_path:ident)* ) => ( - |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e)) - ); -} - -/// Creates a new `ChainError` -/// -/// # Examples -/// -/// Create a new ChainError, where `FooError` must implement `Display` and `Debug`. -/// ```rust -/// # use chainerror::*; -/// # #[derive(Debug)] -/// enum FooError { -/// Bar, -/// Baz(&'static str), -/// } -/// # impl ::std::fmt::Display for FooError { -/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { -/// # match self { -/// # FooError::Bar => write!(f, "Bar Error"), -/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), -/// # } -/// # } -/// # } -/// -/// // impl ::std::fmt::Display for FooError -/// -/// fn do_some_stuff() -> bool { -/// false -/// } -/// -/// fn func() -> ChainResult<(), FooError> { -/// if !do_some_stuff() { -/// Err(cherr!(FooError::Baz("Error")))?; -/// } -/// Ok(()) -/// } -/// # match func().unwrap_err().kind() { -/// # FooError::Baz(s) if s == &"Error" => {} -/// # _ => panic!(), -/// # } -/// ``` -/// -/// Additionally an error cause can be added. -/// -/// ```rust -/// # use chainerror::*; -/// # use std::io; -/// # use std::error::Error; -/// # #[derive(Debug)] -/// # enum FooError { -/// # Bar, -/// # Baz(&'static str), -/// # } -/// # impl ::std::fmt::Display for FooError { -/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { -/// # match self { -/// # FooError::Bar => write!(f, "Bar Error"), -/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), -/// # } -/// # } -/// # } -/// fn do_some_stuff() -> Result<(), Box> { -/// Err(io::Error::from(io::ErrorKind::NotFound))?; -/// Ok(()) -/// } -/// -/// fn func() -> ChainResult<(), FooError> { -/// do_some_stuff().map_err(|e| cherr!(e, FooError::Baz("Error")))?; -/// Ok(()) -/// } -/// # match func().unwrap_err().kind() { -/// # FooError::Baz(s) if s == &"Error" => {} -/// # _ => panic!(), -/// # } -/// ``` -#[macro_export] -macro_rules! cherr { - ( $k:expr ) => ({ - $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) - }); - ( None, $k:expr ) => ({ - $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) - }); - ( None, $fmt:expr, $($arg:tt)+ ) => ({ - $crate::cherr!(None, format!($fmt, $($arg)+ )) - }); - ( None, $fmt:expr, $($arg:tt)+ ) => ({ - $crate::cherr!(None, format!($fmt, $($arg)+ )) - }); - ( $e:path, $k:expr ) => ({ - $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) - }); - ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ - $crate::cherr!($e, format!($fmt, $($arg)+ )) - }); -} - -/// Convenience macro for `|e| cherr!(e, format!(…))` -/// -/// # Examples -/// -/// ```rust -/// # use crate::chainerror::*; -/// # use std::error::Error; -/// # use std::io; -/// # use std::result::Result; -/// # fn do_some_io() -> Result<(), Box> { -/// # Err(io::Error::from(io::ErrorKind::NotFound))?; -/// # Ok(()) -/// # } -/// fn func2() -> Result<(), Box> { -/// let filename = "foo.txt"; -/// do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; -/// Ok(()) -/// } -/// -/// fn func1() -> Result<(), Box> { -/// func2().map_err(mstrerr!("func1 error"))?; -/// Ok(()) -/// } -/// -/// # if let Err(e) = func1() { -/// # #[cfg(not(feature = "no-debug-cause"))] -/// # #[cfg(not(windows))] -/// # assert_eq!( -/// # format!("\n{:?}\n", e), r#" -/// # src/lib.rs:19: func1 error -/// # Caused by: -/// # src/lib.rs:14: Error reading 'foo.txt' -/// # Caused by: -/// # Kind(NotFound) -/// # "# -/// # ); -/// # } else { -/// # unreachable!(); -/// # } -/// ``` -/// -/// `mstrerr!()` can also be used to map a new `ChainError`, where T was defined with -/// `derive_str_cherr!(T)` -/// -/// ```rust -/// # use crate::chainerror::*; -/// # use std::error::Error; -/// # use std::io; -/// # use std::result::Result; -/// # fn do_some_io() -> Result<(), Box> { -/// # Err(io::Error::from(io::ErrorKind::NotFound))?; -/// # Ok(()) -/// # } -/// derive_str_cherr!(Func2Error); -/// -/// fn func2() -> Result<(), Box> { -/// let filename = "foo.txt"; -/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; -/// Ok(()) -/// } -/// -/// derive_str_cherr!(Func1Error); -/// -/// fn func1() -> Result<(), Box> { -/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; -/// Ok(()) -/// } -/// # if let Err(e) = func1() { -/// # if let Some(f1err) = e.downcast_chain_ref::() { -/// # assert!(f1err.find_cause::>().is_some()); -/// # assert!(f1err.find_chain_cause::().is_some()); -/// # } else { -/// # panic!(); -/// # } -/// # } else { -/// # unreachable!(); -/// # } -/// ``` -#[macro_export] -macro_rules! mstrerr { - ( $t:path, $msg:expr ) => ({ - |e| $crate::cherr!(e, $t ($msg.to_string())) - }); - ( $t:path, $msg:expr, ) => ({ - |e| $crate::cherr!(e, $t ($msg.to_string())) - }); - ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ - |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ ))) - }); - ($msg:expr) => ({ - |e| $crate::cherr!(e, $msg.to_string()) - }); - ($msg:expr, ) => ({ - |e| $crate::cherr!(e, $msg.to_string()) - }); - ($fmt:expr, $($arg:tt)+) => ({ - |e| $crate::cherr!(e, format!($fmt, $($arg)+ )) - }); -} - -/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)` -/// -/// # Examples -/// -/// ```rust -/// # use crate::chainerror::*; -/// # use std::error::Error; -/// # use std::result::Result; -/// derive_str_cherr!(Func2Error); -/// -/// fn func2() -> ChainResult<(), Func2Error> { -/// let filename = "foo.txt"; -/// Err(strerr!(Func2Error, "Error reading '{}'", filename)) -/// } -/// -/// derive_str_cherr!(Func1Error); -/// -/// fn func1() -> Result<(), Box> { -/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; -/// Ok(()) -/// } -/// # if let Err(e) = func1() { -/// # if let Some(f1err) = e.downcast_chain_ref::() { -/// # assert!(f1err.find_cause::>().is_some()); -/// # assert!(f1err.find_chain_cause::().is_some()); -/// # } else { -/// # panic!(); -/// # } -/// # } else { -/// # unreachable!(); -/// # } -/// ``` -#[macro_export] -macro_rules! strerr { - ( $t:path, $msg:expr ) => ({ - $crate::cherr!($t ($msg.to_string())) - }); - ( $t:path, $msg:expr, ) => ({ - $crate::cherr!($t ($msg.to_string())) - }); - ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ - $crate::cherr!($t (format!($fmt, $($arg)+ ))) - }); - ($msg:expr) => ({ - $crate::cherr!($msg.to_string()) - }); - ($msg:expr, ) => ({ - $crate::cherr!($msg.to_string()) - }); - ($fmt:expr, $($arg:tt)+) => ({ - $crate::cherr!(format!($fmt, $($arg)+ )) - }); -} - /// Convenience macro to create a "new type" T(String) and implement Display + Debug for T /// /// # Examples @@ -996,14 +828,14 @@ macro_rules! strerr { /// /// fn func2() -> ChainResult<(), Func2Error> { /// let filename = "foo.txt"; -/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; /// Ok(()) /// } /// /// derive_str_cherr!(Func1Error); /// /// fn func1() -> Result<(), Box> { -/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// func2().cherr(Func1Error("func1 error".into()))?; /// Ok(()) /// } /// # if let Err(e) = func1() { @@ -1047,7 +879,7 @@ macro_rules! derive_str_cherr { /// # Examples /// /// ```rust -/// use chainerror::*; +/// use chainerror::prelude::v1::*; /// use std::io; /// /// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { @@ -1095,10 +927,9 @@ macro_rules! derive_str_cherr { /// let filename = "bar.txt"; /// /// do_some_io(filename) -/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; -/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; -/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; -/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; +/// .map_cherr(|e| ErrorKind::from_io_error(e, filename.into()))?; +/// do_some_io(filename).map_cherr(|e| ErrorKind::IO(filename.into()))?; +/// do_some_io(filename).map_cherr(|e| ErrorKind::from(e))?; /// Ok(()) /// } /// ``` @@ -1134,19 +965,6 @@ macro_rules! derive_err_kind { } } - impl $crate::ChainErrorFrom<$e> for $k - where - $k: Clone, - { - #[inline] - fn chain_error_from( - t: $e, - line_filename: Option<&'static str>, - ) -> $crate::ChainError<$k> { - $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename) - } - } - impl std::error::Error for $e { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.0.source() diff --git a/tests/test_iter.rs b/tests/test_iter.rs index ce100d2..b567544 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -1,4 +1,4 @@ -use chainerror::*; +use chainerror::prelude::v1::*; use std::error::Error; use std::io; @@ -6,13 +6,14 @@ use std::io; #[test] fn test_iter() -> Result<(), Box> { use std::fmt::Write; - let err = io::Error::from(io::ErrorKind::NotFound); - let err = cherr!(err, "1"); - let err = cherr!(err, "2"); - let err = cherr!(err, "3"); - let err = cherr!(err, "4"); - let err = cherr!(err, "5"); - let err = cherr!(err, "6"); + let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); + let err = err.cherr("1"); + let err = err.cherr("2"); + let err = err.cherr("3"); + let err = err.cherr("4"); + let err = err.cherr("5"); + let err = err.cherr("6"); + let err = err.err().unwrap(); let mut res = String::new(); @@ -34,13 +35,14 @@ fn test_iter() -> Result<(), Box> { #[cfg(feature = "display-cause")] #[test] fn test_iter() -> Result<(), Box> { - let err = io::Error::from(io::ErrorKind::NotFound); - let err = cherr!(err, "1"); - let err = cherr!(err, "2"); - let err = cherr!(err, "3"); - let err = cherr!(err, "4"); - let err = cherr!(err, "5"); - let err = cherr!(err, "6"); + let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); + let err = err.cherr("1"); + let err = err.cherr("2"); + let err = err.cherr("3"); + let err = err.cherr("4"); + let err = err.cherr("5"); + let err = err.cherr("6"); + let err = err.err().unwrap(); let res = err.to_string(); @@ -58,13 +60,14 @@ fn test_iter() -> Result<(), Box> { #[test] fn test_find_cause() -> Result<(), Box> { - let err = io::Error::from(io::ErrorKind::NotFound); - let err = cherr!(err, "1"); - let err = cherr!(err, "2"); - let err = cherr!(err, "3"); - let err = cherr!(err, "4"); - let err = cherr!(err, "5"); - let err = cherr!(err, "6"); + let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); + let err = err.cherr("1"); + let err = err.cherr("2"); + let err = err.cherr("3"); + let err = err.cherr("4"); + let err = err.cherr("5"); + let err = err.cherr("6"); + let err = err.err().unwrap(); let io_error: Option<&io::Error> = err.find_cause::(); @@ -75,13 +78,14 @@ fn test_find_cause() -> Result<(), Box> { #[test] fn test_root_cause() -> Result<(), Box> { - let err = io::Error::from(io::ErrorKind::NotFound); - let err = cherr!(err, "1"); - let err = cherr!(err, "2"); - let err = cherr!(err, "3"); - let err = cherr!(err, "4"); - let err = cherr!(err, "5"); - let err = cherr!(err, "6"); + let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); + let err = err.cherr("1"); + let err = err.cherr("2"); + let err = err.cherr("3"); + let err = err.cherr("4"); + let err = err.cherr("5"); + let err = err.cherr("6"); + let err = err.err().unwrap(); let err: Option<&(dyn std::error::Error + 'static)> = err.root_cause(); let io_error: Option<&io::Error> = err.and_then(Error::downcast_ref::); From ebbdddf971954bd799aa09bad2700fd280c7bdf5 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Aug 2020 17:01:50 +0200 Subject: [PATCH 33/85] Cargo.toml: fixed github workflow badge --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6224d65..d52a6ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ exclude = [ ".gitignore", "examples/*", "booksrc/*", "book.toml", [badges] # See https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section -github = { repository = "haraldh/chainerror", workflow = "rust" } +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" } From 9d8f316db2b7175c9274a249159d8cea1d03b918 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Aug 2020 17:07:19 +0200 Subject: [PATCH 34/85] add README.tpl template --- README.md | 21 +++++++++++++++++++-- README.tpl | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 README.tpl diff --git a/README.md b/README.md index 9b17c55..c5b6f60 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -[![Workflow Status](https://github.com/haraldh/chainerror/workflows/rust/badge.svg)](https://github.com/haraldh/chainerror/actions?query=workflow%3A%22rust%22) +[![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") ![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg) @@ -214,4 +217,18 @@ Kind(NotFound) } ``` -License: MIT/Apache-2.0 +## License + +Licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/README.tpl b/README.tpl new file mode 100644 index 0000000..01e969a --- /dev/null +++ b/README.tpl @@ -0,0 +1,24 @@ +[![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) +{{badges}} + +# {{crate}} + +{{readme}} + +## License + +Licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. From 2af5fb7ad62c7aae0445738dc2e2b73dc2a550ec Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Aug 2020 17:11:56 +0200 Subject: [PATCH 35/85] doc fix --- README.md | 1 + src/lib.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index c5b6f60..6ee78fe 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Caused by: examples/example.rs:14:18: Error reading 'foo.txt' Caused by: Kind(NotFound) + Alternative Debug Error: ChainError { occurrence: Some( diff --git a/src/lib.rs b/src/lib.rs index 71fbf3f..4c81ee2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ //! examples/example.rs:14:18: Error reading 'foo.txt' //! Caused by: //! Kind(NotFound) +//! //! Alternative Debug Error: //! ChainError { //! occurrence: Some( From ed710fada3c1ad0edd525cae512d65065da26ef3 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 21:03:01 +0200 Subject: [PATCH 36/85] rename `cherr()` to `context()` --- Cargo.toml | 4 +- README.md | 23 +++----- booksrc/tutorial1.md | 2 +- booksrc/tutorial10.md | 6 +-- booksrc/tutorial2.md | 15 +++--- booksrc/tutorial3.md | 4 +- booksrc/tutorial4.md | 13 ++--- booksrc/tutorial8.md | 6 +-- examples/example.rs | 10 ++-- examples/tutorial10.rs | 10 ++-- examples/tutorial11.rs | 10 ++-- examples/tutorial12.rs | 10 ++-- examples/tutorial13.rs | 10 ++-- examples/tutorial14.rs | 12 +++-- examples/tutorial15.rs | 14 ++--- examples/tutorial2.rs | 4 +- examples/tutorial3.rs | 5 +- examples/tutorial4.rs | 5 +- examples/tutorial5.rs | 5 +- examples/tutorial6.rs | 5 +- examples/tutorial7.rs | 5 +- examples/tutorial8.rs | 9 ++-- examples/tutorial9.rs | 13 ++--- src/lib.rs | 120 +++++++++++++++-------------------------- tests/test_iter.rs | 48 ++++++++--------- 25 files changed, 169 insertions(+), 199 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d52a6ad..1341e0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,5 @@ is-it-maintained-issue-resolution = { repository = "haraldh/chainerror" } is-it-maintained-open-issues = { repository = "haraldh/chainerror" } [features] -default = [ "location", "debug-cause" ] -location = [] +default = [] display-cause = [] -debug-cause = [] diff --git a/README.md b/README.md index 6ee78fe..ef73cae 100644 --- a/README.md +++ b/README.md @@ -22,18 +22,9 @@ Debug information is worth it! ### Features -`default = [ "location", "debug-cause" ]` - -`location` -: store the error location - `display-cause` : turn on printing a backtrace of the errors in `Display` -`debug-cause` -: print a backtrace of the errors in `Debug` - - ## Tutorial Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) @@ -101,12 +92,12 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(format!("Error reading '{}'", filename))?; + do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box> { - func2().cherr("func1 error")?; + func2().context("func1 error")?; Ok(()) } @@ -139,14 +130,14 @@ fn do_some_io() -> Result<(), Box> { fn func3() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(format!("Error reading '{}'", filename))?; + do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } -derive_str_cherr!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> ChainResult<(), Func2Error> { - func3().cherr(Func2Error("func2 error: calling func3".into()))?; + func3().context(Func2Error("func2 error: calling func3".into()))?; Ok(()) } @@ -171,9 +162,9 @@ impl ::std::fmt::Debug for Func1Error { } fn func1() -> ChainResult<(), Func1Error> { - func2().cherr(Func1Error::Func2)?; + func2().context(Func1Error::Func2)?; let filename = String::from("bar.txt"); - do_some_io().cherr(Func1Error::IO(filename))?; + do_some_io().context(Func1Error::IO(filename))?; Ok(()) } diff --git a/booksrc/tutorial1.md b/booksrc/tutorial1.md index ae73400..57a502f 100644 --- a/booksrc/tutorial1.md +++ b/booksrc/tutorial1.md @@ -9,7 +9,7 @@ this only prints out the last `Error`. ~~~ -Error: StringError("func1 error") +Error: "func1 error" ~~~ The next chapters of this tutorial show how `chainerror` adds more information diff --git a/booksrc/tutorial10.md b/booksrc/tutorial10.md index e7f2ee4..17c6b63 100644 --- a/booksrc/tutorial10.md +++ b/booksrc/tutorial10.md @@ -5,8 +5,8 @@ To cope with different kind of errors, we introduce the kind of an error `Func1E Because we derive `Debug` and implement `Display` our `Func1ErrorKind` enum, this enum can be used as a `std::error::Error`. -Not using `String` errors anymore, the `cherr!()` macro seen in the beginning of -the tutorial has to be used again. +Not using `String` errors anymore, the `context()` function seen in the beginning of +the tutorial can be used again. Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box>` and we can use `ChainResult<(), Func1ErrorKind>`. @@ -22,4 +22,4 @@ Also a nice `match` on `ChainError.kind()` is now possible, which returns `&T # mod chainerror { {{#rustdoc_include ../src/lib.rs:-1}} # } -~~~ \ No newline at end of file +~~~ diff --git a/booksrc/tutorial2.md b/booksrc/tutorial2.md index e7c5e6d..27e1fbb 100644 --- a/booksrc/tutorial2.md +++ b/booksrc/tutorial2.md @@ -1,7 +1,7 @@ # Simple Chained String Errors -With relatively small changes and the help of the `cherr!` macro of the `chainerror` crate -the `String` errors are now chained together. +With relatively small changes and the help of the `context()` method of the `chainerror` crate +the `&str` errors are now chained together. Press the play button in the upper right corner and see the nice debug output. @@ -19,14 +19,13 @@ Press the play button in the upper right corner and see the nice debug output. {{#include ../examples/tutorial2.rs:13:15}} ~~~ -The macro `cherr!(olderror, newerror)` stores `olderror` as the source/cause of `newerror` -along with the filename (`file!()`) and line number (`line!()`) -and returns `newerror`. +The function `context(newerror)` stores `olderror` as the source/cause of `newerror` +along with the `Location` of the `context()` call and returns `Err(newerror)`. -`Err()?` then returns the inner error applying `.into()`, so that we +`?` then returns the inner error applying `.into()`, so that we again have a `Err(Box)` as a result. -The `Debug` implementation of `ChainError` (which is returned by `cherr!()`) +The `Debug` implementation of `ChainError` (which is returned by `context()`) prints the `Debug` of `T` prefixed with the stored filename and line number. -`ChainError` in our case is `ChainError`. +`ChainError` in our case is `ChainError<&str>`. diff --git a/booksrc/tutorial3.md b/booksrc/tutorial3.md index cc832a9..e44cc7d 100644 --- a/booksrc/tutorial3.md +++ b/booksrc/tutorial3.md @@ -1,6 +1,6 @@ # Mapping Errors -Now let's get more rust idiomatic by using `.map_err()`. +Now let's get more rust idiomatic by using `.context()` directly on the previous `Result`. ~~~rust {{#include ../examples/tutorial3.rs}} @@ -26,4 +26,4 @@ src/main.rs:16: "func1 error" This is, because we caught the error of `func1()` in `main()` and print it out ourselves. We can now control, whether to output in `Debug` or `Display` mode. -Maybe depending on `--debug` as a CLI argument. \ No newline at end of file +Maybe depending on `--debug` as a CLI argument. diff --git a/booksrc/tutorial4.md b/booksrc/tutorial4.md index 68cc16d..f717706 100644 --- a/booksrc/tutorial4.md +++ b/booksrc/tutorial4.md @@ -1,12 +1,7 @@ -# Saving coding chars +# More information -Because decorating an error with more information should not -let you jump through hoops, `chainerror` has a quick macro for that. - -`mstrerror!()` fits right into `.map_err()` letting you quickly add -more debug strings. - -`mstrerror!()` even understands `format!()` syntax like `println!()`. +To give more context to the error, you want to use `format!` +to extend the information in the context string. ~~~rust {{#include ../examples/tutorial4.rs}} @@ -14,4 +9,4 @@ more debug strings. # mod chainerror { {{#rustdoc_include ../src/lib.rs:-1}} # } -~~~ \ No newline at end of file +~~~ diff --git a/booksrc/tutorial8.md b/booksrc/tutorial8.md index 8060788..da0c405 100644 --- a/booksrc/tutorial8.md +++ b/booksrc/tutorial8.md @@ -4,8 +4,8 @@ To distinguish the errors occuring in various places, we can define named string "new type" pattern. ~~~rust,ignore -derive_str_cherr!(Func2Error); -derive_str_cherr!(Func1Error); +derive_str_context!(Func2Error); +derive_str_context!(Func1Error); ~~~ Instead of `ChainError` we now have `struct Func1Error(String)` and `ChainError`. @@ -28,4 +28,4 @@ hiding the `ChainError` implementation detail. # mod chainerror { {{#rustdoc_include ../src/lib.rs:-1}} # } -~~~ \ No newline at end of file +~~~ diff --git a/examples/example.rs b/examples/example.rs index 4044155..cbed147 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -11,14 +11,14 @@ fn do_some_io() -> Result<(), Box> { fn func3() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(format!("Error reading '{}'", filename))?; + do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } -derive_str_cherr!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> ChainResult<(), Func2Error> { - func3().cherr(Func2Error(format!("func2 error: calling func3")))?; + func3().context(Func2Error(format!("func2 error: calling func3")))?; Ok(()) } @@ -43,9 +43,9 @@ impl ::std::fmt::Debug for Func1Error { } fn func1() -> ChainResult<(), Func1Error> { - func2().cherr(Func1Error::Func2)?; + func2().context(Func1Error::Func2)?; let filename = String::from("bar.txt"); - do_some_io().cherr(Func1Error::IO(filename))?; + do_some_io().context(Func1Error::IO(filename))?; Ok(()) } diff --git a/examples/tutorial10.rs b/examples/tutorial10.rs index 4ee8f44..5118c63 100644 --- a/examples/tutorial10.rs +++ b/examples/tutorial10.rs @@ -8,11 +8,11 @@ fn do_some_io() -> Result<(), Box> { Ok(()) } -derive_str_cherr!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; + do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -33,9 +33,9 @@ impl ::std::fmt::Display for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} fn func1() -> ChainResult<(), Func1ErrorKind> { - func2().cherr(Func1ErrorKind::Func2)?; + func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); - do_some_io().cherr(Func1ErrorKind::IO(filename))?; + do_some_io().context(Func1ErrorKind::IO(filename))?; Ok(()) } @@ -53,6 +53,8 @@ fn main() -> Result<(), Box> { } eprintln!("\nDebug Error:\n{:?}", e); + + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial11.rs b/examples/tutorial11.rs index 42e5de8..a943b97 100644 --- a/examples/tutorial11.rs +++ b/examples/tutorial11.rs @@ -8,11 +8,11 @@ fn do_some_io() -> Result<(), Box> { Ok(()) } -derive_str_cherr!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; + do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -39,9 +39,9 @@ impl ::std::fmt::Debug for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} fn func1() -> ChainResult<(), Func1ErrorKind> { - func2().cherr(Func1ErrorKind::Func2)?; + func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); - do_some_io().cherr(Func1ErrorKind::IO(filename))?; + do_some_io().context(Func1ErrorKind::IO(filename))?; Ok(()) } @@ -59,6 +59,8 @@ fn main() -> Result<(), Box> { } eprintln!("\nDebug Error:\n{:?}", e); + + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial12.rs b/examples/tutorial12.rs index ce3cea6..e2bf644 100644 --- a/examples/tutorial12.rs +++ b/examples/tutorial12.rs @@ -8,11 +8,11 @@ fn do_some_io() -> Result<(), Box> { Ok(()) } -derive_str_cherr!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; + do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -39,9 +39,9 @@ impl ::std::fmt::Debug for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} fn func1() -> ChainResult<(), Func1ErrorKind> { - func2().cherr(Func1ErrorKind::Func2)?; + func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); - do_some_io().cherr(Func1ErrorKind::IO(filename))?; + do_some_io().context(Func1ErrorKind::IO(filename))?; Ok(()) } @@ -70,6 +70,8 @@ fn main() -> Result<(), Box> { } eprintln!("\nDebug Error:\n{:?}", e); + + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 29a0374..19bc713 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -7,11 +7,11 @@ pub mod mycrate { Ok(()) } - derive_str_cherr!(Func2Error); + derive_str_context!(Func2Error); fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; + do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -35,9 +35,9 @@ pub mod mycrate { } pub fn func1() -> Result<()> { - func2().cherr(ErrorKind::Func2)?; + func2().context(ErrorKind::Func2)?; let filename = String::from("bar.txt"); - do_some_io().cherr(ErrorKind::IO(filename))?; + do_some_io().context(ErrorKind::IO(filename))?; Ok(()) } } @@ -72,6 +72,8 @@ fn main() -> Result<(), Box> { } eprintln!("\nDebug Error:\n{:?}", e); + + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial14.rs b/examples/tutorial14.rs index 84c3491..8daeceb 100644 --- a/examples/tutorial14.rs +++ b/examples/tutorial14.rs @@ -27,7 +27,7 @@ pub mod mycrate { } } - macro_rules! mcherr { + macro_rules! mcontext { ( $k:expr ) => {{ |e| { Error( @@ -92,7 +92,7 @@ pub mod mycrate { pub fn func2() -> std::result::Result<(), Error> { let filename = "foo.txt"; - do_some_io().map_err(mcherr!(ErrorKind::IO(format!( + do_some_io().map_err(mcontext!(ErrorKind::IO(format!( "Error reading '{}'", filename ))))?; @@ -115,7 +115,7 @@ pub mod mycrate { } } - macro_rules! mcherr { + macro_rules! mcontext { ( $k:expr ) => {{ |e| { Error( @@ -175,9 +175,9 @@ pub mod mycrate { pub type Result = std::result::Result; pub fn func1() -> Result<()> { - func2().map_err(mcherr!(ErrorKind::Func2))?; + func2().map_err(mcontext!(ErrorKind::Func2))?; let filename = String::from("bar.txt"); - do_some_io().map_err(mcherr!(ErrorKind::IO(filename)))?; + do_some_io().map_err(mcontext!(ErrorKind::IO(filename)))?; Ok(()) } } @@ -212,6 +212,8 @@ fn main() -> Result<(), Box> { } eprintln!("\nDebug Error:\n{:?}", e); + + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs index 5130bfc..c045c85 100644 --- a/examples/tutorial15.rs +++ b/examples/tutorial15.rs @@ -8,11 +8,11 @@ pub mod mycrate { Ok(()) } - derive_str_cherr!(Func2Error); + derive_str_context!(Func2Error); fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; - do_some_io(filename).cherr(Func2Error(format!("Error reading '{}'", filename)))?; + do_some_io(filename).context(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -85,15 +85,15 @@ pub mod mycrate { let filename = "bar.txt"; - do_some_io(filename).map_cherr(|e| ErrorKind::from_io_error(&e, filename.into()))?; - do_some_io(filename).map_cherr(|_| ErrorKind::IO(filename.into()))?; - do_some_io(filename).map_cherr(|e| ErrorKind::from(e))?; + do_some_io(filename).map_context(|e| ErrorKind::from_io_error(&e, filename.into()))?; + do_some_io(filename).map_context(|_| ErrorKind::IO(filename.into()))?; + do_some_io(filename).map_context(|e| ErrorKind::from(e))?; Ok(()) } pub fn super_func1() -> Result<()> { - func1().map_cherr(|e| ErrorKind::from(e))?; + func1().map_context(|e| ErrorKind::from(e))?; Ok(()) } } @@ -130,6 +130,8 @@ fn main() -> Result<(), Box> { } eprintln!("\nDebug Error:\n{:?}", e); + + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial2.rs b/examples/tutorial2.rs index 140862f..8c68dc0 100644 --- a/examples/tutorial2.rs +++ b/examples/tutorial2.rs @@ -11,14 +11,14 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { if let Err(e) = do_some_io() { - Err(e).cherr("func2 error")?; + Err(e).context("func2 error")?; } Ok(()) } fn func1() -> Result<(), Box> { if let Err(e) = func2() { - Err(e).cherr("func1 error")?; + Err(e).context("func1 error")?; } Ok(()) } diff --git a/examples/tutorial3.rs b/examples/tutorial3.rs index 9096e07..8475d6f 100644 --- a/examples/tutorial3.rs +++ b/examples/tutorial3.rs @@ -10,18 +10,19 @@ fn do_some_io() -> Result<(), Box> { } fn func2() -> Result<(), Box> { - do_some_io().cherr("func2 error")?; + do_some_io().context("func2 error")?; Ok(()) } fn func1() -> Result<(), Box> { - func2().cherr("func1 error")?; + func2().context("func1 error")?; Ok(()) } fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{:?}", e); + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial4.rs b/examples/tutorial4.rs index 3b5fc15..d1b247b 100644 --- a/examples/tutorial4.rs +++ b/examples/tutorial4.rs @@ -10,18 +10,19 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(format!("Error reading '{}'", filename))?; + do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box> { - func2().cherr("func1 error")?; + func2().context("func1 error")?; Ok(()) } fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{:?}", e); + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial5.rs b/examples/tutorial5.rs index edabbbf..333208c 100644 --- a/examples/tutorial5.rs +++ b/examples/tutorial5.rs @@ -10,7 +10,7 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(format!("Error reading '{}'", filename))?; + do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } @@ -18,7 +18,7 @@ fn func1() -> Result<(), Box> { if let Err(e) = func2() { if let Some(s) = e.source() { eprintln!("func2 failed because of '{}'", s); - Err(e).cherr("func1 error")?; + Err(e).context("func1 error")?; } } Ok(()) @@ -27,6 +27,7 @@ fn func1() -> Result<(), Box> { fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!("{}", e); + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial6.rs b/examples/tutorial6.rs index f8c6549..9310c8c 100644 --- a/examples/tutorial6.rs +++ b/examples/tutorial6.rs @@ -10,12 +10,12 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(format!("Error reading '{}'", filename))?; + do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box> { - func2().cherr("func1 error")?; + func2().context("func1 error")?; Ok(()) } @@ -35,6 +35,7 @@ fn main() -> Result<(), Box> { } s = c; } + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial7.rs b/examples/tutorial7.rs index 11658f2..feb7cf8 100644 --- a/examples/tutorial7.rs +++ b/examples/tutorial7.rs @@ -10,12 +10,12 @@ fn do_some_io() -> Result<(), Box> { fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(format!("Error reading '{}'", filename))?; + do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box> { - func2().cherr(format!("func1 error"))?; + func2().context(format!("func1 error"))?; Ok(()) } @@ -36,6 +36,7 @@ fn main() -> Result<(), Box> { eprintln!("The root cause was: std::io::Error: {:#?}", ioerror); } } + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial8.rs b/examples/tutorial8.rs index f55b83b..2093782 100644 --- a/examples/tutorial8.rs +++ b/examples/tutorial8.rs @@ -8,18 +8,18 @@ fn do_some_io() -> Result<(), Box> { Ok(()) } -derive_str_cherr!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; + do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } -derive_str_cherr!(Func1Error); +derive_str_context!(Func1Error); fn func1() -> Result<(), Box> { - func2().cherr(Func1Error(format!("func1 error")))?; + func2().context(Func1Error(format!("func1 error")))?; Ok(()) } @@ -36,6 +36,7 @@ fn main() -> Result<(), Box> { eprintln!("Debug Func2Error:\n{:?}", f2err); } } + std::process::exit(1); } Ok(()) } diff --git a/examples/tutorial9.rs b/examples/tutorial9.rs index 374cb77..1561535 100644 --- a/examples/tutorial9.rs +++ b/examples/tutorial9.rs @@ -8,21 +8,21 @@ fn do_some_io() -> Result<(), Box> { Ok(()) } -derive_str_cherr!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; - do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; + do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; Ok(()) } -derive_str_cherr!(Func1ErrorFunc2); -derive_str_cherr!(Func1ErrorIO); +derive_str_context!(Func1ErrorFunc2); +derive_str_context!(Func1ErrorIO); fn func1() -> Result<(), Box> { - func2().cherr(Func1ErrorFunc2(format!("func1 error calling func2")))?; + func2().context(Func1ErrorFunc2(format!("func1 error calling func2")))?; let filename = "bar.txt"; - do_some_io().cherr(Func1ErrorIO(format!("Error reading '{}'", filename)))?; + do_some_io().context(Func1ErrorIO(format!("Error reading '{}'", filename)))?; Ok(()) } @@ -35,6 +35,7 @@ fn main() -> Result<(), Box> { if let Some(s) = e.downcast_chain_ref::() { eprintln!("Func1ErrorFunc2:\n{:?}", s); } + std::process::exit(1); } Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 4c81ee2..0837a64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,18 +12,9 @@ //! //! ## Features //! -//! `default = [ "location", "debug-cause" ]` -//! -//! `location` -//! : store the error location -//! //! `display-cause` //! : turn on printing a backtrace of the errors in `Display` //! -//! `debug-cause` -//! : print a backtrace of the errors in `Debug` -//! -//! //! # Tutorial //! //! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) @@ -91,17 +82,16 @@ //! //! fn func2() -> Result<(), Box> { //! let filename = "foo.txt"; -//! do_some_io().cherr(format!("Error reading '{}'", filename))?; +//! do_some_io().context(format!("Error reading '{}'", filename))?; //! Ok(()) //! } //! //! fn func1() -> Result<(), Box> { -//! func2().cherr("func1 error")?; +//! func2().context("func1 error")?; //! Ok(()) //! } //! //! if let Err(e) = func1() { -//! # #[cfg(feature = "debug-cause")] //! #[cfg(not(windows))] //! assert_eq!( //! format!("\n{:?}\n", e), @@ -133,14 +123,14 @@ //! //! fn func3() -> Result<(), Box> { //! let filename = "foo.txt"; -//! do_some_io().cherr(format!("Error reading '{}'", filename))?; +//! do_some_io().context(format!("Error reading '{}'", filename))?; //! Ok(()) //! } //! -//! derive_str_cherr!(Func2Error); +//! derive_str_context!(Func2Error); //! //! fn func2() -> ChainResult<(), Func2Error> { -//! func3().cherr(Func2Error("func2 error: calling func3".into()))?; +//! func3().context(Func2Error("func2 error: calling func3".into()))?; //! Ok(()) //! } //! @@ -165,9 +155,9 @@ //! } //! //! fn func1() -> ChainResult<(), Func1Error> { -//! func2().cherr(Func1Error::Func2)?; +//! func2().context(Func1Error::Func2)?; //! let filename = String::from("bar.txt"); -//! do_some_io().cherr(Func1Error::IO(filename))?; +//! do_some_io().context(Func1Error::IO(filename))?; //! Ok(()) //! } //! @@ -196,7 +186,6 @@ //! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); //! } //! -//! # #[cfg(feature = "no-debug-cause")] //! #[cfg(not(windows))] //! assert_eq!( //! format!("\n{:?}\n", e), @@ -229,15 +218,15 @@ pub mod prelude { //! convenience prelude pub mod v1 { //! convenience prelude - pub use crate::ChainErrorDown as _; - pub use crate::ResultTrait as _; - pub use crate::{derive_err_kind, derive_str_cherr, ChainError, ChainResult}; + pub use super::super::ChainErrorDown as _; + 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 { - #[cfg(feature = "location")] occurrence: Option, kind: T, error_cause: Option>, @@ -247,8 +236,7 @@ pub struct ChainError { pub type ChainResult = std::result::Result>; impl ChainError { - #[cfg(feature = "location")] - /// Use the `cherr()` or `map_cherr()` Result methods instead of calling this directly + /// Use the `context()` or `map_context()` Result methods instead of calling this directly #[inline] pub fn new( kind: T, @@ -262,17 +250,6 @@ impl ChainError { } } - #[cfg(not(feature = "location"))] - /// Use the `cherr()` or `map_cherr()` Result methods instead of calling this directly - #[inline] - pub fn new( - kind: T, - error_cause: Option>, - _occurrence: Option, - ) -> Self { - Self { kind, error_cause } - } - /// return the root cause of the error chain, if any exists pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { self.iter().last() @@ -292,18 +269,18 @@ impl ChainError { /// Ok(()) /// } /// - /// derive_str_cherr!(Func2Error); + /// derive_str_context!(Func2Error); /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; - /// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; + /// do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; /// Ok(()) /// } /// - /// derive_str_cherr!(Func1Error); + /// derive_str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { - /// func2().cherr(Func1Error("func1 error".into()))?; + /// func2().context(Func1Error("func1 error".into()))?; /// Ok(()) /// } /// @@ -334,7 +311,7 @@ impl ChainError { /// /// ```rust /// # use chainerror::prelude::v1::*; - /// # derive_str_cherr!(FooError); + /// # derive_str_context!(FooError); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing /// err.find_cause::>(); @@ -357,7 +334,7 @@ impl ChainError { /// /// ```rust /// # use chainerror::prelude::v1::*; - /// # derive_str_cherr!(FooErrorKind); + /// # derive_str_context!(FooErrorKind); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing /// err.find_cause::>(); @@ -394,11 +371,11 @@ impl ChainError { /// Ok(()) /// } /// - /// derive_str_cherr!(Func2Error); + /// derive_str_context!(Func2Error); /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; - /// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; + /// do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; /// Ok(()) /// } /// @@ -419,8 +396,8 @@ impl ChainError { /// # } /// /// fn func1() -> ChainResult<(), Func1ErrorKind> { - /// func2().cherr(Func1ErrorKind::Func2)?; - /// do_some_io().cherr(Func1ErrorKind::IO("bar.txt".into()))?; + /// func2().context(Func1ErrorKind::Func2)?; + /// do_some_io().context(Func1ErrorKind::IO("bar.txt".into()))?; /// Ok(()) /// } /// @@ -453,10 +430,10 @@ impl ChainError { /// Convenience methods for `Result<>` to turn the error into a decorated ChainError pub trait ResultTrait>> { /// Decorate the error with a `kind` of type `T` and the source `Location` - fn cherr(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` and the source `Location` - fn map_cherr T>( + /// 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>; @@ -466,7 +443,7 @@ impl>> ResultTrait for std::result::Result { #[track_caller] - fn cherr(self, kind: T) -> std::result::Result> { + fn context(self, kind: T) -> std::result::Result> { match self { Ok(t) => Ok(t), Err(error_cause) => Err(ChainError::new( @@ -478,7 +455,7 @@ impl>> ResultTrait } #[track_caller] - fn map_cherr T>( + fn map_context T>( self, op: F, ) -> std::result::Result> { @@ -741,21 +718,15 @@ impl Debug for ChainError { if f.alternate() { let mut f = f.debug_struct(&format!("ChainError<{}>", std::any::type_name::())); - #[cfg(feature = "location")] - let f = f.field("occurrence", &self.occurrence); - - let f = f.field("kind", &self.kind); - - #[cfg(feature = "debug-cause")] - let f = f.field("source", &self.source()); + let f = f + .field("occurrence", &self.occurrence) + .field("kind", &self.kind) + .field("source", &self.source()); f.finish() } else { - #[cfg(feature = "location")] - { - if let Some(ref o) = self.occurrence { - write!(f, "{}: ", o)?; - } + if let Some(ref o) = self.occurrence { + write!(f, "{}: ", o)?; } if TypeId::of::() == TypeId::of::() @@ -766,12 +737,9 @@ impl Debug for ChainError { Debug::fmt(&self.kind, f)?; } - #[cfg(feature = "debug-cause")] - { - if let Some(e) = self.source() { - writeln!(f, "\nCaused by:")?; - Debug::fmt(&e, f)?; - } + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Debug::fmt(&e, f)?; } Ok(()) } @@ -825,18 +793,18 @@ where /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// derive_str_cherr!(Func2Error); +/// derive_str_context!(Func2Error); /// /// fn func2() -> ChainResult<(), Func2Error> { /// let filename = "foo.txt"; -/// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; +/// do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; /// Ok(()) /// } /// -/// derive_str_cherr!(Func1Error); +/// derive_str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { -/// func2().cherr(Func1Error("func1 error".into()))?; +/// func2().context(Func1Error("func1 error".into()))?; /// Ok(()) /// } /// # if let Err(e) = func1() { @@ -851,7 +819,7 @@ where /// # } /// ``` #[macro_export] -macro_rules! derive_str_cherr { +macro_rules! derive_str_context { ($e:ident) => { #[derive(Clone)] pub struct $e(pub String); @@ -928,9 +896,9 @@ macro_rules! derive_str_cherr { /// let filename = "bar.txt"; /// /// do_some_io(filename) -/// .map_cherr(|e| ErrorKind::from_io_error(e, filename.into()))?; -/// do_some_io(filename).map_cherr(|e| ErrorKind::IO(filename.into()))?; -/// do_some_io(filename).map_cherr(|e| ErrorKind::from(e))?; +/// .map_context(|e| ErrorKind::from_io_error(e, filename.into()))?; +/// do_some_io(filename).map_context(|e| ErrorKind::IO(filename.into()))?; +/// do_some_io(filename).map_context(|e| ErrorKind::from(e))?; /// Ok(()) /// } /// ``` diff --git a/tests/test_iter.rs b/tests/test_iter.rs index b567544..318c7a7 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -7,12 +7,12 @@ use std::io; fn test_iter() -> Result<(), Box> { use std::fmt::Write; let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); - let err = err.cherr("1"); - let err = err.cherr("2"); - let err = err.cherr("3"); - let err = err.cherr("4"); - let err = err.cherr("5"); - let err = err.cherr("6"); + let err = err.context("1"); + let err = err.context("2"); + let err = err.context("3"); + let err = err.context("4"); + let err = err.context("5"); + let err = err.context("6"); let err = err.err().unwrap(); let mut res = String::new(); @@ -36,12 +36,12 @@ fn test_iter() -> Result<(), Box> { #[test] fn test_iter() -> Result<(), Box> { let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); - let err = err.cherr("1"); - let err = err.cherr("2"); - let err = err.cherr("3"); - let err = err.cherr("4"); - let err = err.cherr("5"); - let err = err.cherr("6"); + let err = err.context("1"); + let err = err.context("2"); + let err = err.context("3"); + let err = err.context("4"); + let err = err.context("5"); + let err = err.context("6"); let err = err.err().unwrap(); let res = err.to_string(); @@ -61,12 +61,12 @@ fn test_iter() -> Result<(), Box> { #[test] fn test_find_cause() -> Result<(), Box> { let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); - let err = err.cherr("1"); - let err = err.cherr("2"); - let err = err.cherr("3"); - let err = err.cherr("4"); - let err = err.cherr("5"); - let err = err.cherr("6"); + let err = err.context("1"); + let err = err.context("2"); + let err = err.context("3"); + let err = err.context("4"); + let err = err.context("5"); + let err = err.context("6"); let err = err.err().unwrap(); let io_error: Option<&io::Error> = err.find_cause::(); @@ -79,12 +79,12 @@ fn test_find_cause() -> Result<(), Box> { #[test] fn test_root_cause() -> Result<(), Box> { let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); - let err = err.cherr("1"); - let err = err.cherr("2"); - let err = err.cherr("3"); - let err = err.cherr("4"); - let err = err.cherr("5"); - let err = err.cherr("6"); + let err = err.context("1"); + let err = err.context("2"); + let err = err.context("3"); + let err = err.context("4"); + let err = err.context("5"); + let err = err.context("6"); let err = err.err().unwrap(); let err: Option<&(dyn std::error::Error + 'static)> = err.root_cause(); From 1654624c080699def1050e4b6653ee7d73b975eb Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 21:17:56 +0200 Subject: [PATCH 37/85] more documentation and formatting --- README.md | 27 ++++++++++++++++----------- booksrc/SUMMARY.md | 4 ++-- booksrc/tutorial10.md | 3 --- booksrc/tutorial11.md | 2 +- examples/example.rs | 8 ++++---- examples/tutorial14.rs | 2 +- examples/tutorial15.rs | 3 +-- src/lib.rs | 40 +++++++++++++++++++++++++--------------- 8 files changed, 50 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index ef73cae..0021587 100644 --- a/README.md +++ b/README.md @@ -31,16 +31,21 @@ Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) ## Examples +examples/examples.rs: +```rust +// […] +fn main() { + if let Err(e) = func1() { + eprintln!("\nDebug Error {{:?}}:\n{:?}", e); + eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", e); + // […] + } +} +``` + ```console $ cargo run -q --example example -Main Error Report: func1 error calling func2 - -Error reported by Func2Error: func2 error: calling func3 -The root cause was: std::io::Error: Kind( - NotFound -) - -Debug Error: +Debug Error {:?}: examples/example.rs:46:13: func1 error calling func2 Caused by: examples/example.rs:21:13: Func2Error(func2 error: calling func3) @@ -49,7 +54,7 @@ examples/example.rs:14:18: Error reading 'foo.txt' Caused by: Kind(NotFound) -Alternative Debug Error: +Alternative Debug Error {:#?}: ChainError { occurrence: Some( "examples/example.rs:46:13", @@ -65,8 +70,8 @@ ChainError { ChainError { occurrence: Some( "examples/example.rs:14:18", - ), - kind: "Error reading \'foo.txt\'", + ), + kind: "Error reading \'foo.txt\'", source: Some( Kind( NotFound, diff --git a/booksrc/SUMMARY.md b/booksrc/SUMMARY.md index 91ef467..6bbc3b4 100644 --- a/booksrc/SUMMARY.md +++ b/booksrc/SUMMARY.md @@ -5,7 +5,7 @@ - [Simple String Errors](tutorial1.md) - [Simple Chained String Errors](tutorial2.md) - [Mapping Errors](tutorial3.md) -- [Saving coding chars](tutorial4.md) +- [More Information](tutorial4.md) - [The source() of Errors](tutorial5.md) - [Downcast the Errors](tutorial6.md) - [The root cause of all Errors](tutorial7.md) @@ -17,4 +17,4 @@ - [Writing a library](tutorial13.md) - [Going back to std](tutorial14.md) -[The End](end.md) \ No newline at end of file +[The End](end.md) diff --git a/booksrc/tutorial10.md b/booksrc/tutorial10.md index 17c6b63..07d0100 100644 --- a/booksrc/tutorial10.md +++ b/booksrc/tutorial10.md @@ -5,9 +5,6 @@ To cope with different kind of errors, we introduce the kind of an error `Func1E Because we derive `Debug` and implement `Display` our `Func1ErrorKind` enum, this enum can be used as a `std::error::Error`. -Not using `String` errors anymore, the `context()` function seen in the beginning of -the tutorial can be used again. - Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box>` and we can use `ChainResult<(), Func1ErrorKind>`. diff --git a/booksrc/tutorial11.md b/booksrc/tutorial11.md index 0d2c0cc..197b9d3 100644 --- a/booksrc/tutorial11.md +++ b/booksrc/tutorial11.md @@ -1,6 +1,6 @@ # Debug for the ErrorKind -One small improvement at the end of the tutorial is to fix the debug output of +One small improvement is to fix the debug output of `Func1ErrorKind`. As you probably noticed, the output doesn't say much of the enum. ~~~ diff --git a/examples/example.rs b/examples/example.rs index cbed147..89b0d77 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!("\nDebug Error {{:?}}:\n{:?}", e); + + eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", e); + match e.kind() { Func1Error::Func2 => eprintln!("Main Error Report: func1 error calling func2"), Func1Error::IO(filename) => { @@ -66,9 +70,5 @@ fn main() { let ioerror = e.downcast_ref::().unwrap(); eprintln!("\nThe root cause was: std::io::Error: {:#?}", ioerror); } - - eprintln!("\nDebug Error:\n{:?}", e); - - eprintln!("\nAlternative Debug Error:\n{:#?}", e); } } diff --git a/examples/tutorial14.rs b/examples/tutorial14.rs index 8daeceb..9c624dc 100644 --- a/examples/tutorial14.rs +++ b/examples/tutorial14.rs @@ -1,7 +1,7 @@ pub mod mycrate { use std::error::Error as StdError; - use func2mod::{do_some_io, func2}; + use self::func2mod::{do_some_io, func2}; pub mod func2mod { use std::error::Error as StdError; diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs index c045c85..ef35003 100644 --- a/examples/tutorial15.rs +++ b/examples/tutorial15.rs @@ -1,7 +1,6 @@ pub mod mycrate { - use std::io; - use chainerror::prelude::v1::*; + use std::io; fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/src/lib.rs b/src/lib.rs index 0837a64..f5d8472 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,16 +21,21 @@ //! //! # Examples //! +//! examples/examples.rs: +//! ```rust,no_run +//! // […] +//! fn main() { +//! if let Err(e) = func1() { +//! eprintln!("\nDebug Error {{:?}}:\n{:?}", e); +//! eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", e); +//! // […] +//! } +//! } +//! ``` +//! //! ```console //! $ cargo run -q --example example -//! Main Error Report: func1 error calling func2 -//! -//! Error reported by Func2Error: func2 error: calling func3 -//! The root cause was: std::io::Error: Kind( -//! NotFound -//! ) -//! -//! Debug Error: +//! Debug Error {:?}: //! examples/example.rs:46:13: func1 error calling func2 //! Caused by: //! examples/example.rs:21:13: Func2Error(func2 error: calling func3) @@ -39,7 +44,7 @@ //! Caused by: //! Kind(NotFound) //! -//! Alternative Debug Error: +//! Alternative Debug Error {:#?}: //! ChainError { //! occurrence: Some( //! "examples/example.rs:46:13", @@ -55,8 +60,8 @@ //! ChainError { //! occurrence: Some( //! "examples/example.rs:14:18", -//! ), -//! kind: "Error reading \'foo.txt\'", +//! ), +//! kind: "Error reading \'foo.txt\'", //! source: Some( //! Kind( //! NotFound, @@ -430,7 +435,10 @@ impl ChainError { /// Convenience methods for `Result<>` to turn the error into a decorated ChainError 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>( @@ -443,7 +451,10 @@ impl>> ResultTrait for std::result::Result { #[track_caller] - fn context(self, kind: T) -> std::result::Result> { + fn context( + self, + kind: T, + ) -> std::result::Result> { match self { Ok(t) => Ok(t), Err(error_cause) => Err(ChainError::new( @@ -895,8 +906,7 @@ macro_rules! derive_str_context { /// pub fn func1() -> std::result::Result<(), Error> { /// let filename = "bar.txt"; /// -/// do_some_io(filename) -/// .map_context(|e| ErrorKind::from_io_error(e, filename.into()))?; +/// do_some_io(filename).map_context(|e| ErrorKind::from_io_error(e, filename.into()))?; /// do_some_io(filename).map_context(|e| ErrorKind::IO(filename.into()))?; /// do_some_io(filename).map_context(|e| ErrorKind::from(e))?; /// Ok(()) From 14c67e190312da4dc7b4e2035c1d733a77513eab Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 21:42:17 +0200 Subject: [PATCH 38/85] ignore first example --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f5d8472..bd4edd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,8 +21,8 @@ //! //! # Examples //! -//! examples/examples.rs: -//! ```rust,no_run +//! examples/example.rs: +//! ```rust,ignore //! // […] //! fn main() { //! if let Err(e) = func1() { From 56c13d0be581286d7f7a38d17b00d48de1d5e1ed Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 21:43:29 +0200 Subject: [PATCH 39/85] update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0021587..86405f0 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) ## Examples -examples/examples.rs: +examples/example.rs: ```rust // […] fn main() { From a78b9a22f7c3c86320562eaf15a2050a967532bd Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 21:47:29 +0200 Subject: [PATCH 40/85] (cargo-release) version 0.6.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1341e0c..c7e19c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.6.0-alpha.0" +version = "0.6.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" From 455b01d3fb2c761ef88af79ba2656b0f71c6285e Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 21:47:49 +0200 Subject: [PATCH 41/85] (cargo-release) start next development iteration 0.6.1-alpha.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c7e19c6..ea0e924 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.6.0" +version = "0.6.1-alpha.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" From f586b52675cf5d13a337a8220d562a152ef0df41 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 22:47:15 +0200 Subject: [PATCH 42/85] Better introduction examples. --- README.md | 260 ++++++++++++++----------------------------------- src/lib.rs | 277 ++++++++++++++++------------------------------------- 2 files changed, 159 insertions(+), 378 deletions(-) diff --git a/README.md b/README.md index 86405f0..2441848 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,86 @@ `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your binaries, you still have the error backtrace. -`chainerror` has no dependencies! +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); + } +} +``` + +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 @@ -29,191 +102,6 @@ Debug information is worth it! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) -## Examples - -examples/example.rs: -```rust -// […] -fn main() { - if let Err(e) = func1() { - eprintln!("\nDebug Error {{:?}}:\n{:?}", e); - eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", e); - // […] - } -} -``` - -```console -$ cargo run -q --example example -Debug Error {:?}: -examples/example.rs:46:13: func1 error calling func2 -Caused by: -examples/example.rs:21:13: Func2Error(func2 error: calling func3) -Caused by: -examples/example.rs:14:18: Error reading 'foo.txt' -Caused by: -Kind(NotFound) - -Alternative Debug Error {:#?}: -ChainError { - occurrence: Some( - "examples/example.rs:46:13", - ), - kind: func1 error calling func2, - source: Some( - ChainError { - occurrence: Some( - "examples/example.rs:21:13", - ), - kind: Func2Error(func2 error: calling func3), - source: Some( - ChainError { - occurrence: Some( - "examples/example.rs:14:18", - ), - kind: "Error reading \'foo.txt\'", - source: Some( - Kind( - NotFound, - ), - ), - }, - ), - }, - ), -} -``` - -```rust -use chainerror::prelude::v1::*; -use std::error::Error; -use std::io; -use std::result::Result; - -fn do_some_io() -> Result<(), Box> { - Err(io::Error::from(io::ErrorKind::NotFound))?; - Ok(()) -} - -fn func2() -> Result<(), Box> { - let filename = "foo.txt"; - do_some_io().context(format!("Error reading '{}'", filename))?; - Ok(()) -} - -fn func1() -> Result<(), Box> { - func2().context("func1 error")?; - Ok(()) -} - -if let Err(e) = func1() { - #[cfg(not(windows))] - assert_eq!( - format!("\n{:?}\n", e), - r#" -src/lib.rs:21:13: func1 error -Caused by: -src/lib.rs:16:18: Error reading 'foo.txt' -Caused by: -Kind(NotFound) -"# - ); -} -``` - - -```rust -use chainerror::prelude::v1::*; -use std::error::Error; -use std::io; -use std::result::Result; - -fn do_some_io() -> Result<(), Box> { - Err(io::Error::from(io::ErrorKind::NotFound))?; - Ok(()) -} - -fn func3() -> Result<(), Box> { - let filename = "foo.txt"; - do_some_io().context(format!("Error reading '{}'", filename))?; - Ok(()) -} - -derive_str_context!(Func2Error); - -fn func2() -> ChainResult<(), Func2Error> { - func3().context(Func2Error("func2 error: calling func3".into()))?; - Ok(()) -} - -enum Func1Error { - Func2, - IO(String), -} - -impl ::std::fmt::Display for Func1Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match self { - Func1Error::Func2 => write!(f, "func1 error calling func2"), - Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename), - } - } -} - -impl ::std::fmt::Debug for Func1Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}", self) - } -} - -fn func1() -> ChainResult<(), Func1Error> { - func2().context(Func1Error::Func2)?; - let filename = String::from("bar.txt"); - do_some_io().context(Func1Error::IO(filename))?; - Ok(()) -} - -if let Err(e) = func1() { - assert!(match e.kind() { - Func1Error::Func2 => { - eprintln!("Main Error Report: func1 error calling func2"); - true - } - Func1Error::IO(filename) => { - eprintln!("Main Error Report: func1 error reading '{}'", filename); - false - } - }); - - assert!(e.find_chain_cause::().is_some()); - - if let Some(e) = e.find_chain_cause::() { - eprintln!("\nError reported by Func2Error: {}", e) - } - - assert!(e.root_cause().is_some()); - - if let Some(e) = e.root_cause() { - let io_error = e.downcast_ref::().unwrap(); - eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); - } - - #[cfg(not(windows))] - assert_eq!( - format!("\n{:?}\n", e), - r#" -src/lib.rs:48:13: func1 error calling func2 -Caused by: -src/lib.rs:23:13: Func2Error(func2 error: calling func3) -Caused by: -src/lib.rs:16:18: Error reading 'foo.txt' -Caused by: -Kind(NotFound) -"# - ); -} -``` - ## License Licensed under either of diff --git a/src/lib.rs b/src/lib.rs index bd4edd6..48a0f3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,96 @@ //! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your //! binaries, you still have the error backtrace. //! -//! `chainerror` has no dependencies! +//! 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); +//! # assert_eq!( +//! # format!("{:?}\n", e), +//! # "\ +//! # src/lib.rs:16:51: read the config file\n\ +//! # Caused by:\n\ +//! # src/lib.rs:9:47: Reading file: \"foo.txt\"\n\ +//! # Caused by:\n\ +//! # Os { code: 2, kind: NotFound, message: \"No such file or directory\" }\n\ +//! # ", +//! # ); +//! } +//! } +//! ``` +//! +//! 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 @@ -18,200 +101,10 @@ //! # Tutorial //! //! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) -//! -//! # Examples -//! -//! examples/example.rs: -//! ```rust,ignore -//! // […] -//! fn main() { -//! if let Err(e) = func1() { -//! eprintln!("\nDebug Error {{:?}}:\n{:?}", e); -//! eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", e); -//! // […] -//! } -//! } -//! ``` -//! -//! ```console -//! $ cargo run -q --example example -//! Debug Error {:?}: -//! examples/example.rs:46:13: func1 error calling func2 -//! Caused by: -//! examples/example.rs:21:13: Func2Error(func2 error: calling func3) -//! Caused by: -//! examples/example.rs:14:18: Error reading 'foo.txt' -//! Caused by: -//! Kind(NotFound) -//! -//! Alternative Debug Error {:#?}: -//! ChainError { -//! occurrence: Some( -//! "examples/example.rs:46:13", -//! ), -//! kind: func1 error calling func2, -//! source: Some( -//! ChainError { -//! occurrence: Some( -//! "examples/example.rs:21:13", -//! ), -//! kind: Func2Error(func2 error: calling func3), -//! source: Some( -//! ChainError { -//! occurrence: Some( -//! "examples/example.rs:14:18", -//! ), -//! kind: "Error reading \'foo.txt\'", -//! source: Some( -//! Kind( -//! NotFound, -//! ), -//! ), -//! }, -//! ), -//! }, -//! ), -//! } -//! ``` -//! -//! ```rust -//! use chainerror::prelude::v1::*; -//! use std::error::Error; -//! use std::io; -//! use std::result::Result; -//! -//! fn do_some_io() -> Result<(), Box> { -//! Err(io::Error::from(io::ErrorKind::NotFound))?; -//! Ok(()) -//! } -//! -//! fn func2() -> Result<(), Box> { -//! let filename = "foo.txt"; -//! do_some_io().context(format!("Error reading '{}'", filename))?; -//! Ok(()) -//! } -//! -//! fn func1() -> Result<(), Box> { -//! func2().context("func1 error")?; -//! Ok(()) -//! } -//! -//! if let Err(e) = func1() { -//! #[cfg(not(windows))] -//! assert_eq!( -//! format!("\n{:?}\n", e), -//! r#" -//! src/lib.rs:21:13: func1 error -//! Caused by: -//! src/lib.rs:16:18: Error reading 'foo.txt' -//! Caused by: -//! Kind(NotFound) -//! "# -//! ); -//! } -//! # else { -//! # unreachable!(); -//! # } -//! ``` -//! -//! -//! ```rust -//! use chainerror::prelude::v1::*; -//! use std::error::Error; -//! use std::io; -//! use std::result::Result; -//! -//! fn do_some_io() -> Result<(), Box> { -//! Err(io::Error::from(io::ErrorKind::NotFound))?; -//! Ok(()) -//! } -//! -//! fn func3() -> Result<(), Box> { -//! let filename = "foo.txt"; -//! do_some_io().context(format!("Error reading '{}'", filename))?; -//! Ok(()) -//! } -//! -//! derive_str_context!(Func2Error); -//! -//! fn func2() -> ChainResult<(), Func2Error> { -//! func3().context(Func2Error("func2 error: calling func3".into()))?; -//! Ok(()) -//! } -//! -//! enum Func1Error { -//! Func2, -//! IO(String), -//! } -//! -//! impl ::std::fmt::Display for Func1Error { -//! fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { -//! match self { -//! Func1Error::Func2 => write!(f, "func1 error calling func2"), -//! Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename), -//! } -//! } -//! } -//! -//! impl ::std::fmt::Debug for Func1Error { -//! fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { -//! write!(f, "{}", self) -//! } -//! } -//! -//! fn func1() -> ChainResult<(), Func1Error> { -//! func2().context(Func1Error::Func2)?; -//! let filename = String::from("bar.txt"); -//! do_some_io().context(Func1Error::IO(filename))?; -//! Ok(()) -//! } -//! -//! if let Err(e) = func1() { -//! assert!(match e.kind() { -//! Func1Error::Func2 => { -//! eprintln!("Main Error Report: func1 error calling func2"); -//! true -//! } -//! Func1Error::IO(filename) => { -//! eprintln!("Main Error Report: func1 error reading '{}'", filename); -//! false -//! } -//! }); -//! -//! assert!(e.find_chain_cause::().is_some()); -//! -//! if let Some(e) = e.find_chain_cause::() { -//! eprintln!("\nError reported by Func2Error: {}", e) -//! } -//! -//! assert!(e.root_cause().is_some()); -//! -//! if let Some(e) = e.root_cause() { -//! let io_error = e.downcast_ref::().unwrap(); -//! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); -//! } -//! -//! #[cfg(not(windows))] -//! assert_eq!( -//! format!("\n{:?}\n", e), -//! r#" -//! src/lib.rs:48:13: func1 error calling func2 -//! Caused by: -//! src/lib.rs:23:13: Func2Error(func2 error: calling func3) -//! Caused by: -//! src/lib.rs:16:18: Error reading 'foo.txt' -//! Caused by: -//! Kind(NotFound) -//! "# -//! ); -//! } -//! # else { -//! # unreachable!(); -//! # } -//! ``` #![deny(clippy::all)] #![deny(clippy::integer_arithmetic)] +#![allow(clippy::needless_doctest_main)] #![deny(missing_docs)] use std::any::TypeId; From 1003671be34d0ffd73e0742838eeddddd4b4aab5 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 22:59:44 +0200 Subject: [PATCH 43/85] (cargo-release) version 0.6.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ea0e924..49cf712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.6.1-alpha.0" +version = "0.6.1" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" From cd0fc471cb4ebbbd6ef98091fa1001dfe207e11d Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 1 Sep 2020 22:59:58 +0200 Subject: [PATCH 44/85] (cargo-release) start next development iteration 0.6.2-alpha.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 49cf712..86e0e24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.6.1" +version = "0.6.2-alpha.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" From 523b8633fb2328baa0120ebce70448b1ec88c621 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 19:31:40 +0000 Subject: [PATCH 45/85] Create Dependabot config file --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5cde165 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 From 259712931f45f362318a2b3ab6076d67c762e002 Mon Sep 17 00:00:00 2001 From: Jakub Duchniewicz Date: Mon, 11 Jan 2021 19:38:01 +0100 Subject: [PATCH 46/85] Rust PR 75180 fix. --- src/lib.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 48a0f3d..5febac3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -582,15 +582,6 @@ impl Error for ChainError { } } -impl Error for &ChainError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause - .as_ref() - .map(|e| e.as_ref() as &(dyn Error + 'static)) - } -} - impl Error for &mut ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { From ee385c1fe03a68eb9a96874e91673ce1b205b934 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 2 Feb 2021 09:11:42 +0100 Subject: [PATCH 47/85] (cargo-release) version 0.7.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 86e0e24..3afdf67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.6.2-alpha.0" +version = "0.7.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" From d577243d8e965ca8d2d27b994e11d731bc27df2e Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 2 Feb 2021 09:12:07 +0100 Subject: [PATCH 48/85] (cargo-release) start next development iteration 0.7.1-alpha.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3afdf67..e5089e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.7.0" +version = "0.7.1-alpha.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" From 05085229be60482ee25dc2a00a3ce7e28d5afc3f Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 2 Feb 2021 09:45:23 +0100 Subject: [PATCH 49/85] chore: fix github pages deploy set-env is deprecated ... remove the caching stuff --- .github/workflows/gh-pages.yml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index b8d27bd..bcc15ae 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -11,27 +11,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set CURRENT_TWO_WEEKS for use in cache keys - run: echo "::set-env name=CURRENT_TWO_WEEKS::$(($(date +%V) / 2))" - - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ env.CURRENT_TWO_WEEKS }} - - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ${{ runner.os }}-cargo-index-${{ env.CURRENT_TWO_WEEKS }} - - - name: Cache mdbook binary - uses: actions/cache@v1 - with: - path: ~/.cargo/bin/mdbook - key: ${{ runner.os }}-cargo-mdbook-${{ env.CURRENT_TWO_WEEKS }} - - name: Build mdbook run: cargo install mdbook From d60cdf9cdb6953f0211cd03204b72cc355500b2f Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 12:39:31 +0200 Subject: [PATCH 50/85] fix: clippy Signed-off-by: Harald Hoyer --- examples/example.rs | 2 +- examples/tutorial15.rs | 2 +- examples/tutorial7.rs | 2 +- examples/tutorial8.rs | 2 +- examples/tutorial9.rs | 2 +- src/lib.rs | 9 +++++---- tests/test_iter.rs | 8 ++++---- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index 89b0d77..370631e 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -18,7 +18,7 @@ fn func3() -> Result<(), Box> { derive_str_context!(Func2Error); fn func2() -> ChainResult<(), Func2Error> { - func3().context(Func2Error(format!("func2 error: calling func3")))?; + func3().context(Func2Error("func2 error: calling func3".to_string()))?; Ok(()) } diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs index ef35003..3b0eef3 100644 --- a/examples/tutorial15.rs +++ b/examples/tutorial15.rs @@ -84,7 +84,7 @@ pub mod mycrate { let filename = "bar.txt"; - do_some_io(filename).map_context(|e| ErrorKind::from_io_error(&e, filename.into()))?; + do_some_io(filename).map_context(|e| ErrorKind::from_io_error(e, filename.into()))?; do_some_io(filename).map_context(|_| ErrorKind::IO(filename.into()))?; do_some_io(filename).map_context(|e| ErrorKind::from(e))?; diff --git a/examples/tutorial7.rs b/examples/tutorial7.rs index feb7cf8..969588d 100644 --- a/examples/tutorial7.rs +++ b/examples/tutorial7.rs @@ -15,7 +15,7 @@ fn func2() -> Result<(), Box> { } fn func1() -> Result<(), Box> { - func2().context(format!("func1 error"))?; + func2().context("func1 error")?; Ok(()) } diff --git a/examples/tutorial8.rs b/examples/tutorial8.rs index 2093782..cf5e654 100644 --- a/examples/tutorial8.rs +++ b/examples/tutorial8.rs @@ -19,7 +19,7 @@ fn func2() -> Result<(), Box> { derive_str_context!(Func1Error); fn func1() -> Result<(), Box> { - func2().context(Func1Error(format!("func1 error")))?; + func2().context(Func1Error("func1 error".to_string()))?; Ok(()) } diff --git a/examples/tutorial9.rs b/examples/tutorial9.rs index 1561535..754c621 100644 --- a/examples/tutorial9.rs +++ b/examples/tutorial9.rs @@ -20,7 +20,7 @@ derive_str_context!(Func1ErrorFunc2); derive_str_context!(Func1ErrorIO); fn func1() -> Result<(), Box> { - func2().context(Func1ErrorFunc2(format!("func1 error calling func2")))?; + func2().context(Func1ErrorFunc2("func1 error calling func2".to_string()))?; let filename = "bar.txt"; do_some_io().context(Func1ErrorIO(format!("Error reading '{}'", filename)))?; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 5febac3..9ba0f28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,7 +103,6 @@ //! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) #![deny(clippy::all)] -#![deny(clippy::integer_arithmetic)] #![allow(clippy::needless_doctest_main)] #![deny(missing_docs)] @@ -198,7 +197,9 @@ impl ChainError { /// ``` #[inline] pub fn find_cause(&self) -> Option<&U> { - self.iter().filter_map(Error::downcast_ref::).next() + self.iter() + .filter_map(::downcast_ref::) + .next() } /// Find the first error cause of type `ChainError`, if any exists @@ -220,7 +221,7 @@ impl ChainError { #[inline] pub fn find_chain_cause(&self) -> Option<&ChainError> { self.iter() - .filter_map(Error::downcast_ref::>) + .filter_map(::downcast_ref::>) .next() } @@ -428,7 +429,7 @@ impl ChainErrorDown for ChainError { #[allow(clippy::cast_ptr_alignment)] unsafe { #[allow(trivial_casts)] - Some(&*(self as *const dyn Error as *const &ChainError)) + Some(*(self as *const dyn Error as *const &ChainError)) } } else { None diff --git a/tests/test_iter.rs b/tests/test_iter.rs index 318c7a7..ad4f9f1 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -18,13 +18,13 @@ fn test_iter() -> Result<(), Box> { let mut res = String::new(); for e in err.iter() { - write!(res, "{}", e.to_string())?; + write!(res, "{}", e)?; } assert_eq!(res, "654321entity not found"); let io_error: Option<&io::Error> = err .iter() - .filter_map(Error::downcast_ref::) + .filter_map(::downcast_ref::) .next(); assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound); @@ -50,7 +50,7 @@ fn test_iter() -> Result<(), Box> { let io_error: Option<&io::Error> = err .iter() - .filter_map(Error::downcast_ref::) + .filter_map(::downcast_ref::) .next(); assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound); @@ -88,7 +88,7 @@ fn test_root_cause() -> Result<(), Box> { let err = err.err().unwrap(); let err: Option<&(dyn std::error::Error + 'static)> = err.root_cause(); - let io_error: Option<&io::Error> = err.and_then(Error::downcast_ref::); + let io_error: Option<&io::Error> = err.and_then(::downcast_ref::); assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound); From bb5f372a92d6cca496d97e1dbf9a42ae3910c724 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 12:27:28 +0200 Subject: [PATCH 51/85] fix: use `dtolnay/rust-toolchain` instead of `actions-rs/toolchain` Signed-off-by: Harald Hoyer --- .github/workflows/rust.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9e148b2..2f81b29 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,10 +26,9 @@ jobs: steps: - uses: actions/checkout@v1 - name: Install toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.version }} - default: true profile: minimal - name: Build run: cargo build --verbose @@ -45,7 +44,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: components: rustfmt toolchain: stable @@ -61,7 +60,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: components: clippy toolchain: stable @@ -77,7 +76,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: toolchain: stable profile: minimal From 376e133836b3f1c2a22d598a0ca61509c8ee2128 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 12:32:37 +0200 Subject: [PATCH 52/85] ci: use codecov for coverage Signed-off-by: Harald Hoyer --- .github/workflows/coverage.yml | 55 +++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 1925bb5..21025a8 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,22 +1,43 @@ -name: coverage +name: coverage + +on: + # Trigger the workflow on push or pull request, + # but only for the master branch + push: + branches: + - master + pull_request: + branches: + - master + release: + types: + - created -on: [ "push" , "pull_request" ] jobs: test: - name: coverage - runs-on: ubuntu-latest - container: - image: xd009642/tarpaulin - options: --security-opt seccomp=unconfined + name: coverage + runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Generate code coverage - run: | - cargo tarpaulin --verbose --workspace --timeout 120 --out Lcov --output-dir coverage - - - name: Upload to coveralls - uses: coverallsapp/github-action@master + - uses: actions/checkout@v1 + - uses: dtolnay/rust-toolchain@master with: - github-token: ${{ secrets.GITHUB_TOKEN }} + target: x86_64-unknown-linux-gnu + toolchain: nightly + components: llvm-tools-preview + + - name: Install cargo-llvm-cov + run: > + curl -LsSf 'https://github.com/taiki-e/cargo-llvm-cov/releases/download/v0.5.23/cargo-llvm-cov-x86_64-unknown-linux-musl.tar.gz' + | tar xzf - + && mv cargo-llvm-cov $HOME/.cargo/bin + + - name: Run cargo-llvm-cov + run: cargo llvm-cov --doctests --all --all-features --lcov --output-path lcov.info + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + directory: ./ + fail_ci_if_error: false + files: ./lcov.info + verbose: true From 95c5a02d507a418c68cadf1c4a4a9db73111c1f8 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 13:28:15 +0200 Subject: [PATCH 53/85] test: rewrite doc test Line numbering has changed in Rust 1.71 in the doc tests, due to an extra inserted `#[allow(unused_extern_crates)]` line. Don't test for the exact line number anymore. Signed-off-by: Harald Hoyer --- src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9ba0f28..2c58ec4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,17 +60,16 @@ //! fn main() { //! if let Err(e) = process_config_file() { //! eprintln!("Error:\n{:?}", e); -//! # assert_eq!( -//! # format!("{:?}\n", e), -//! # "\ -//! # src/lib.rs:16:51: read the config file\n\ -//! # Caused by:\n\ -//! # src/lib.rs:9:47: Reading file: \"foo.txt\"\n\ -//! # Caused by:\n\ -//! # Os { code: 2, kind: NotFound, message: \"No such file or directory\" }\n\ -//! # ", -//! # ); +//! # 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!(); } //! } //! ``` //! From a34929600e383edea3842eef1875157f2ba98d57 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 14:13:34 +0200 Subject: [PATCH 54/85] chore: fix License to SPDX syntax Signed-off-by: Harald Hoyer --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e5089e9..4093bde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "chainerror" version = "0.7.1-alpha.0" authors = ["Harald Hoyer "] edition = "2018" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" documentation = "https://docs.rs/chainerror" homepage = "https://haraldh.github.io/chainerror/" repository = "https://github.com/haraldh/chainerror" From e90072f079c69d3561eb57d9c96810deb375b7ed Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 14:24:20 +0200 Subject: [PATCH 55/85] chore: Release chainerror version 0.7.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4093bde..266c241 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.7.1-alpha.0" +version = "0.7.1" authors = ["Harald Hoyer "] edition = "2018" license = "MIT OR Apache-2.0" From b2a62b2f550e5dc2e775edf0580f4f0f6422568f Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 14:29:29 +0200 Subject: [PATCH 56/85] chore: Release chainerror version 0.7.2-alpha.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 266c241..6c4e589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.7.1" +version = "0.7.2-alpha.1" authors = ["Harald Hoyer "] edition = "2018" license = "MIT OR Apache-2.0" From 4eae3da3c165134b86081d91c893a89ccc4fe843 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 2 Feb 2021 11:16:48 +0100 Subject: [PATCH 57/85] feat: removed feature `display-cause` `display-cause` can be turned on with the `{:#}` format specifier --- Cargo.toml | 4 ---- examples/example.rs | 4 ++++ src/lib.rs | 15 ++++----------- tests/test_iter.rs | 8 +++----- 4 files changed, 11 insertions(+), 20 deletions(-) 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/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/src/lib.rs b/src/lib.rs index 2c58ec4..480a763 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,11 +92,6 @@ //! //! 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) @@ -596,13 +591,12 @@ impl Display for ChainError { fn fmt(&self, f: &mut Formatter<'_>) -> 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(()) } } @@ -633,8 +627,7 @@ 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(()) } 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() From bdfec082286d38a3eec768713991ea6d8908480d Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 2 Feb 2021 15:35:50 +0100 Subject: [PATCH 58/85] feat: add inline to context and map_context methods --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 480a763..910e8aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -339,6 +339,7 @@ impl>> ResultTrait for std::result::Result { #[track_caller] + #[inline] fn context( self, kind: T, @@ -354,6 +355,7 @@ impl>> ResultTrait } #[track_caller] + #[inline] fn map_context T>( self, op: F, From f5c8afce0d025de5bc0684e1853bf504316b2b74 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Tue, 2 Feb 2021 15:36:43 +0100 Subject: [PATCH 59/85] feat: remove ChainErrorFrom and IntoChainError, add From ChainErrorFrom and IntoChainError are not needed anymore with the `#[track_caller]` feature. Now, a proper `From for ChainError` can be implemented. --- src/lib.rs | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 910e8aa..7dfa5a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -636,40 +636,16 @@ impl Debug for ChainError { } } -/// `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 ChainError 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) -> ChainError { + ChainError::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 From 1327575aa99bccd2a7355dd23d3934694fcbc2bf Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 14:36:10 +0200 Subject: [PATCH 60/85] feat!: remove `Chain` prefix from `Error` and `Result` like `anyhow` Signed-off-by: Harald Hoyer --- src/lib.rs | 183 ++++++++++++++++++++++++++--------------------------- 1 file changed, 89 insertions(+), 94 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7dfa5a0..0ea1dbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,8 +101,8 @@ #![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 { @@ -110,28 +110,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 { @@ -142,7 +143,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() } @@ -190,9 +191,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() } @@ -213,9 +214,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() } @@ -240,10 +241,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::()) }) @@ -313,7 +314,7 @@ impl ChainError { /// /// # Example #[inline] - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { ErrorIter { current: Some(self), } @@ -321,32 +322,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] #[inline] - fn context( - self, - kind: T, - ) -> std::result::Result> { + 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,12 +354,12 @@ impl>> ResultTrait 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()), @@ -376,21 +371,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] @@ -404,28 +399,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 @@ -433,24 +428,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 @@ -458,12 +453,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 @@ -471,126 +466,126 @@ 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)?; if f.alternate() { @@ -603,9 +598,9 @@ impl Display for ChainError { } } -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::())); @@ -636,14 +631,14 @@ impl Debug for ChainError { } } -impl From for ChainError +impl From for Error where T: 'static + Display + Debug, { #[track_caller] #[inline] - fn from(e: T) -> ChainError { - ChainError::new(e, None, Some(Location::caller().to_string())) + fn from(e: T) -> Error { + Error::new(e, None, Some(Location::caller().to_string())) } } /// Convenience macro to create a "new type" T(String) and implement Display + Debug for T @@ -651,7 +646,7 @@ where /// # Examples /// /// ```rust -/// # use crate::chainerror::*; +/// # use chainerror::prelude::v1::*; /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; @@ -770,7 +765,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 { @@ -780,12 +775,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) } } From 165c1b939c9289dc47782345d81c2b0617b1d07d Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 14:37:19 +0200 Subject: [PATCH 61/85] chore: suppress clippy errors in tutorial code is in this state by purpose Signed-off-by: Harald Hoyer --- examples/tutorial1.rs | 3 +++ examples/tutorial13.rs | 3 +++ examples/tutorial14.rs | 3 +++ examples/tutorial15.rs | 3 +++ examples/tutorial6.rs | 3 +++ examples/tutorial7.rs | 3 +++ 6 files changed, 18 insertions(+) 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; From cf62d1a9f979ee9df9a615fbf8f369f2d7daef94 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 14:54:57 +0200 Subject: [PATCH 62/85] chore: remove need for `cargo readme` Just use `#![doc = include_str!("../README.md")]` Signed-off-by: Harald Hoyer --- .github/workflows/rust.yml | 15 +----- README.md | 10 +--- src/lib.rs | 99 +------------------------------------- 3 files changed, 3 insertions(+), 121 deletions(-) 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/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/src/lib.rs b/src/lib.rs index 0ea1dbe..50dbd51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,101 +1,4 @@ -//! `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! -//! -//! # 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)] From 87bac108d29ca664452f314bdebca840a705eb87 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 15:28:18 +0200 Subject: [PATCH 63/85] chore: Release chainerror version 0.8.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dae493e..9c1824f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.7.2-alpha.1" +version = "0.8.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT OR Apache-2.0" From 9e7492f67f0036722ea0e2289561de13eff12c24 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 27 Jul 2023 15:29:06 +0200 Subject: [PATCH 64/85] chore: Release chainerror version 0.8.1-alpha.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9c1824f..3bac3ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.8.0" +version = "0.8.1-alpha.1" authors = ["Harald Hoyer "] edition = "2018" license = "MIT OR Apache-2.0" From 968c83983aebe60f4c2931262186641a59bb6434 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 15:04:29 +0200 Subject: [PATCH 65/85] fix: re-add the basic doc test It got lost with the `README.md` conversion. Signed-off-by: Harald Hoyer --- tests/test_basic.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/test_basic.rs diff --git a/tests/test_basic.rs b/tests/test_basic.rs new file mode 100644 index 0000000..ac5412c --- /dev/null +++ b/tests/test_basic.rs @@ -0,0 +1,34 @@ +use chainerror::prelude::v1::*; + +#[test] +fn test_basic() { + 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 + read_config_file("_non_existent.txt".into()).context("read the config file")?; + // do stuff, return other errors + Ok(()) + } + + if let Err(e) = process_config_file() { + let os_notfound_error = std::io::Error::from_raw_os_error(2); + eprintln!("Error:\n{:?}", e); + let s = format!("{:?}", e); + let lines = s.lines().collect::>(); + assert_eq!(lines.len(), 5); + assert!(lines[0].starts_with("tests/test_basic.rs:")); + assert_eq!(lines[1], "Caused by:"); + assert!(lines[2].starts_with("tests/test_basic.rs:")); + assert_eq!(lines[3], "Caused by:"); + assert_eq!(lines[4], format!("{:?}", os_notfound_error)); + } else { + panic!(); + } +} From a116310c4d20aea4ec0a2c444b1c18161a68a590 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 15:05:49 +0200 Subject: [PATCH 66/85] doc: improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - remove `Chain…` mentions in the docs - add doc links - add rustdoc feature to scrape the examples for code Signed-off-by: Harald Hoyer --- Cargo.toml | 3 +++ README.md | 6 +++--- src/lib.rs | 32 ++++++++++++++++---------------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3bac3ee..668f5a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,6 @@ 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" } + +[package.metadata.docs.rs] +cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/README.md b/README.md index 7a7b3a9..7df21e4 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ 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. +Along with the `Error` struct, `chainerror` comes with some useful helper macros to save a lot of typing. `chainerror` has no dependencies! @@ -98,8 +98,8 @@ Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) Licensed under either of -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. diff --git a/src/lib.rs b/src/lib.rs index 50dbd51..1b6039c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,9 +100,9 @@ impl Error { .next() } - /// Find the first error cause of type `ChainError`, if any exists + /// Find the first error cause of type [`Error`](Error), if any exists /// - /// Same as `find_cause`, but hides the `ChainError` implementation internals + /// Same as `find_cause`, but hides the [`Error`](Error) implementation internals /// /// # Examples /// @@ -123,9 +123,9 @@ impl Error { .next() } - /// Find the first error cause of type `ChainError` or `U`, if any exists and return `U` + /// Find the first error cause of type [`Error`](Error) or `U`, if any exists and return `U` /// - /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError` implementation internals + /// Same as `find_cause` and `find_chain_cause`, but hides the [`Error`](Error) implementation internals /// /// # Examples /// @@ -154,7 +154,7 @@ impl Error { .next() } - /// Return a reference to T of `ChainError` + /// Return a reference to T of [`Error`](Error) /// /// # Examples /// @@ -224,7 +224,7 @@ impl Error { } } -/// Convenience methods for `Result<>` to turn the error into a decorated ChainError +/// Convenience methods for `Result<>` to turn the error into a decorated [`Error`](Error) pub trait ResultTrait>> { /// Decorate the error with a `kind` of type `T` and the source `Location` fn context(self, kind: T) -> std::result::Result>; @@ -297,17 +297,17 @@ impl std::ops::Deref for Error { } } -/// Convenience trait to hide the `ChainError` implementation internals +/// Convenience trait to hide the [`Error`](Error) implementation internals pub trait ChainErrorDown { - /// Test if of type `ChainError` + /// Test if of type `Error` fn is_chain(&self) -> bool; - /// Downcast to a reference of `ChainError` + /// Downcast to a reference of `Error` fn downcast_chain_ref(&self) -> Option<&Error>; - /// Downcast to a mutable reference of `ChainError` + /// Downcast to a mutable reference of `Error` fn downcast_chain_mut(&mut self) -> Option<&mut Error>; - /// Downcast to T of `ChainError` + /// Downcast to T of `Error` fn downcast_inner_ref(&self) -> Option<&T>; - /// Downcast to T mutable reference of `ChainError` + /// Downcast to T mutable reference of `Error` fn downcast_inner_mut(&mut self) -> Option<&mut T>; } @@ -505,7 +505,7 @@ impl Debug for Error { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if f.alternate() { - let mut f = f.debug_struct(&format!("ChainError<{}>", std::any::type_name::())); + let mut f = f.debug_struct(&format!("Error<{}>", std::any::type_name::())); let f = f .field("occurrence", &self.occurrence) @@ -601,9 +601,9 @@ macro_rules! derive_str_context { }; } -/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method +/// Derive an Error for an ErrorKind, which wraps a [`Error`](Error) and implements a `kind()` method /// -/// It basically hides `ChainError` to the outside and only exposes the `kind()` +/// It basically hides [`Error`](Error) to the outside and only exposes the [`kind()`](Error::kind) /// method. /// /// Error::kind() returns the ErrorKind @@ -682,7 +682,7 @@ macro_rules! derive_err_kind { } } - impl From> for $e { + impl From<$crate::Error<$k>> for $e { fn from(e: $crate::Error<$k>) -> Self { $e(e) } From 11aeeb17c3ac1dfc4971ba6de0b25f720ae88876 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 15:13:50 +0200 Subject: [PATCH 67/85] ci: use ubuntu-latest The ubuntu-18.04 environment is deprecated. Signed-off-by: Harald Hoyer --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index bcc15ae..7062679 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -7,7 +7,7 @@ on: jobs: deploy: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From cacfb37d4469280c8c62bcb78c317692f2c90266 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 15:18:14 +0200 Subject: [PATCH 68/85] chore: Release chainerror version 0.8.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 668f5a0..c4f3b17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.8.1-alpha.1" +version = "0.8.2" authors = ["Harald Hoyer "] edition = "2018" license = "MIT OR Apache-2.0" From cb2e1509e590805018bb8d1633d0f4bdfb92bb72 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 15:28:27 +0200 Subject: [PATCH 69/85] chore: remove more `Chain..` occurences Signed-off-by: Harald Hoyer --- src/lib.rs | 12 ++++++------ tests/test_basic.rs | 3 +-- tests/test_iter.rs | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1b6039c..8aedf66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,9 +111,9 @@ impl Error { /// # derive_str_context!(FooError); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing - /// err.find_cause::>(); + /// err.find_cause::>(); /// - /// // leave out the ChainError implementation detail + /// // leave out the chainerror::Error implementation detail /// err.find_chain_cause::(); /// ``` #[inline] @@ -134,13 +134,13 @@ impl Error { /// # derive_str_context!(FooErrorKind); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing - /// err.find_cause::>(); + /// err.find_cause::>(); /// // and/or /// err.find_chain_cause::(); /// // and/or /// err.find_cause::(); /// - /// // leave out the ChainError implementation detail + /// // leave out the chainerror::Error implementation detail /// err.find_kind_or_cause::(); /// ``` #[inline] @@ -192,7 +192,7 @@ impl Error { /// # } /// # } /// - /// fn func1() -> ChainResult<(), Func1ErrorKind> { + /// fn func1() -> chainerror::Result<(), Func1ErrorKind> { /// func2().context(Func1ErrorKind::Func2)?; /// do_some_io().context(Func1ErrorKind::IO("bar.txt".into()))?; /// Ok(()) @@ -559,7 +559,7 @@ where /// # } /// derive_str_context!(Func2Error); /// -/// fn func2() -> ChainResult<(), Func2Error> { +/// fn func2() -> chainerror::Result<(), Func2Error> { /// let filename = "foo.txt"; /// do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; /// Ok(()) diff --git a/tests/test_basic.rs b/tests/test_basic.rs index ac5412c..dfdc5f8 100644 --- a/tests/test_basic.rs +++ b/tests/test_basic.rs @@ -1,4 +1,4 @@ -use chainerror::prelude::v1::*; +use chainerror::ResultTrait; #[test] fn test_basic() { @@ -19,7 +19,6 @@ fn test_basic() { if let Err(e) = process_config_file() { let os_notfound_error = std::io::Error::from_raw_os_error(2); - eprintln!("Error:\n{:?}", e); let s = format!("{:?}", e); let lines = s.lines().collect::>(); assert_eq!(lines.len(), 5); diff --git a/tests/test_iter.rs b/tests/test_iter.rs index f5f0e58..3a464b1 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -1,4 +1,4 @@ -use chainerror::prelude::v1::*; +use chainerror::ResultTrait; use std::error::Error; use std::io; From 3ab04cca257bd05d35e18cd4306a190573fd6cd2 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 16:18:54 +0200 Subject: [PATCH 70/85] feat: cleanup names - ResultTrait -> Context - ChainErrorDown -> ErrorDown - derive_err_kind -> err_kind - derive_str_context -> str_context - add `prelude::v2` with only the traits Signed-off-by: Harald Hoyer --- src/lib.rs | 66 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8aedf66..a4c82c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,11 +12,19 @@ pub mod prelude { //! convenience prelude pub mod v1 { //! convenience prelude - pub use super::super::ChainErrorDown as _; + pub use super::super::Context as _; + pub use super::super::Context as ResultTrait; pub use super::super::Error as ChainError; + pub use super::super::ErrorDown as _; + pub use super::super::ErrorDown as ChainErrorDown; pub use super::super::Result as ChainResult; - pub use super::super::ResultTrait as _; - pub use crate::{derive_err_kind, derive_str_context}; + pub use crate::err_kind as derive_err_kind; + pub use crate::str_context as derive_str_context; + } + pub mod v2 { + //! convenience prelude + pub use super::super::Context as _; + pub use super::super::ErrorDown as _; } } @@ -55,7 +63,8 @@ impl Error { /// # Examples /// /// ```rust - /// use chainerror::prelude::v1::*; + /// use chainerror::Context as _; + /// use chainerror::ErrorDown as _; /// use std::error::Error; /// use std::io; /// @@ -64,7 +73,7 @@ impl Error { /// Ok(()) /// } /// - /// derive_str_context!(Func2Error); + /// chainerror::str_context!(Func2Error); /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; @@ -72,7 +81,7 @@ impl Error { /// Ok(()) /// } /// - /// derive_str_context!(Func1Error); + /// chainerror::str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { /// func2().context(Func1Error("func1 error".into()))?; @@ -107,9 +116,8 @@ impl Error { /// # Examples /// /// ```rust - /// # use chainerror::prelude::v1::*; - /// # derive_str_context!(FooError); - /// # let err = ChainError::new(String::new(), None, None); + /// # chainerror::str_context!(FooError); + /// # let err = chainerror::Error::new(String::new(), None, None); /// // Instead of writing /// err.find_cause::>(); /// @@ -130,9 +138,8 @@ impl Error { /// # Examples /// /// ```rust - /// # use chainerror::prelude::v1::*; - /// # derive_str_context!(FooErrorKind); - /// # let err = ChainError::new(String::new(), None, None); + /// # chainerror::str_context!(FooErrorKind); + /// # let err = chainerror::Error::new(String::new(), None, None); /// // Instead of writing /// err.find_cause::>(); /// // and/or @@ -159,7 +166,7 @@ impl Error { /// # Examples /// /// ```rust - /// use chainerror::prelude::v1::*; + /// use chainerror::Context as _; /// use std::error::Error; /// use std::io; /// @@ -168,7 +175,7 @@ impl Error { /// Ok(()) /// } /// - /// derive_str_context!(Func2Error); + /// chainerror::str_context!(Func2Error); /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; @@ -225,7 +232,7 @@ impl Error { } /// Convenience methods for `Result<>` to turn the error into a decorated [`Error`](Error) -pub trait ResultTrait>> { +pub trait Context>> { /// Decorate the error with a `kind` of type `T` and the source `Location` fn context(self, kind: T) -> std::result::Result>; @@ -236,7 +243,7 @@ pub trait ResultTrait>> { ) -> std::result::Result>; } -impl>> ResultTrait +impl>> Context for std::result::Result { #[track_caller] @@ -298,7 +305,7 @@ impl std::ops::Deref for Error { } /// Convenience trait to hide the [`Error`](Error) implementation internals -pub trait ChainErrorDown { +pub trait ErrorDown { /// Test if of type `Error` fn is_chain(&self) -> bool; /// Downcast to a reference of `Error` @@ -311,7 +318,7 @@ pub trait ChainErrorDown { fn downcast_inner_mut(&mut self) -> Option<&mut T>; } -impl ChainErrorDown for Error { +impl ErrorDown for Error { #[inline] fn is_chain(&self) -> bool { TypeId::of::() == TypeId::of::() @@ -369,7 +376,7 @@ impl ChainErrorDown for Error { } } -impl ChainErrorDown for dyn StdError + 'static { +impl ErrorDown for dyn StdError + 'static { #[inline] fn is_chain(&self) -> bool { self.is::>() @@ -402,7 +409,7 @@ impl ChainErrorDown for dyn StdError + 'static { } } -impl ChainErrorDown for dyn StdError + 'static + Send { +impl ErrorDown for dyn StdError + 'static + Send { #[inline] fn is_chain(&self) -> bool { self.is::>() @@ -435,7 +442,7 @@ impl ChainErrorDown for dyn StdError + 'static + Send { } } -impl ChainErrorDown for dyn StdError + 'static + Send + Sync { +impl ErrorDown for dyn StdError + 'static + Send + Sync { #[inline] fn is_chain(&self) -> bool { self.is::>() @@ -549,7 +556,8 @@ where /// # Examples /// /// ```rust -/// # use chainerror::prelude::v1::*; +/// # use chainerror::Context as _; +/// # use chainerror::ErrorDown as _; /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; @@ -557,7 +565,7 @@ where /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// derive_str_context!(Func2Error); +/// chainerror::str_context!(Func2Error); /// /// fn func2() -> chainerror::Result<(), Func2Error> { /// let filename = "foo.txt"; @@ -565,7 +573,7 @@ where /// Ok(()) /// } /// -/// derive_str_context!(Func1Error); +/// chainerror::str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { /// func2().context(Func1Error("func1 error".into()))?; @@ -573,7 +581,7 @@ where /// } /// # if let Err(e) = func1() { /// # if let Some(f1err) = e.downcast_chain_ref::() { -/// # assert!(f1err.find_cause::>().is_some()); +/// # assert!(f1err.find_cause::>().is_some()); /// # assert!(f1err.find_chain_cause::().is_some()); /// # } else { /// # panic!(); @@ -583,7 +591,7 @@ where /// # } /// ``` #[macro_export] -macro_rules! derive_str_context { +macro_rules! str_context { ($e:ident) => { #[derive(Clone)] pub struct $e(pub String); @@ -612,7 +620,7 @@ macro_rules! derive_str_context { /// # Examples /// /// ```rust -/// use chainerror::prelude::v1::*; +/// use chainerror::Context as _; /// use std::io; /// /// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { @@ -626,7 +634,7 @@ macro_rules! derive_str_context { /// Unknown, /// } /// -/// derive_err_kind!(Error, ErrorKind); +/// chainerror::err_kind!(Error, ErrorKind); /// /// impl std::fmt::Display for ErrorKind { /// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { @@ -666,7 +674,7 @@ macro_rules! derive_str_context { /// } /// ``` #[macro_export] -macro_rules! derive_err_kind { +macro_rules! err_kind { ($e:ident, $k:ident) => { pub struct $e($crate::Error<$k>); From 55c16d7867f7dd1e2177a846453e4926b24bcaa2 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 16:20:55 +0200 Subject: [PATCH 71/85] tests: use new names Signed-off-by: Harald Hoyer --- tests/test_basic.rs | 2 +- tests/test_iter.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_basic.rs b/tests/test_basic.rs index dfdc5f8..bbc9f16 100644 --- a/tests/test_basic.rs +++ b/tests/test_basic.rs @@ -1,4 +1,4 @@ -use chainerror::ResultTrait; +use chainerror::Context; #[test] fn test_basic() { diff --git a/tests/test_iter.rs b/tests/test_iter.rs index 3a464b1..9023078 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -1,4 +1,4 @@ -use chainerror::ResultTrait; +use chainerror::Context; use std::error::Error; use std::io; From 82257c881a81fc6fb21319fc307ec3ef3c1381f8 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 16:21:22 +0200 Subject: [PATCH 72/85] docs: use new names Signed-off-by: Harald Hoyer --- README.md | 2 +- booksrc/LICENSE-APACHE | 1 + booksrc/LICENSE-MIT | 1 + booksrc/tutorial10.md | 7 +++---- booksrc/tutorial11.md | 2 +- booksrc/tutorial12.md | 2 +- booksrc/tutorial13.md | 2 +- booksrc/tutorial2.md | 4 ++-- booksrc/tutorial3.md | 4 ++-- booksrc/tutorial5.md | 3 +-- booksrc/tutorial7.md | 8 ++++---- booksrc/tutorial8.md | 12 ++++++------ examples/example.rs | 19 +++++++++---------- examples/tutorial1.rs | 1 - examples/tutorial10.rs | 8 ++++---- examples/tutorial11.rs | 8 ++++---- examples/tutorial12.rs | 8 ++++---- examples/tutorial13.rs | 7 ++++--- examples/tutorial15.rs | 7 ++++--- examples/tutorial2.rs | 3 +-- examples/tutorial3.rs | 3 +-- examples/tutorial4.rs | 4 ++-- examples/tutorial5.rs | 4 ++-- examples/tutorial6.rs | 4 ++-- examples/tutorial7.rs | 4 ++-- examples/tutorial8.rs | 10 +++++----- examples/tutorial9.rs | 12 ++++++------ 27 files changed, 74 insertions(+), 76 deletions(-) create mode 120000 booksrc/LICENSE-APACHE create mode 120000 booksrc/LICENSE-MIT diff --git a/README.md b/README.md index 7df21e4..865cb0d 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ 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 chainerror::Context as _; use std::path::PathBuf; type BoxedError = Box; diff --git a/booksrc/LICENSE-APACHE b/booksrc/LICENSE-APACHE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/booksrc/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/booksrc/LICENSE-MIT b/booksrc/LICENSE-MIT new file mode 120000 index 0000000..76219eb --- /dev/null +++ b/booksrc/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/booksrc/tutorial10.md b/booksrc/tutorial10.md index 07d0100..5547954 100644 --- a/booksrc/tutorial10.md +++ b/booksrc/tutorial10.md @@ -1,6 +1,6 @@ # ErrorKind to the rescue -To cope with different kind of errors, we introduce the kind of an error `Func1ErrorKind` with an enum. +To cope with different kind of errors, we introduce the `kind` of an error `Func1ErrorKind` with an enum. Because we derive `Debug` and implement `Display` our `Func1ErrorKind` enum, this enum can be used as a `std::error::Error`. @@ -8,10 +8,9 @@ a `std::error::Error`. Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box>` and we can use `ChainResult<(), Func1ErrorKind>`. -In `main` we can now directly use the methods of `ChainError` without downcasting the error first. +In `main` we can now directly use the methods of `chainerror::Error` without downcasting the error first. -Also a nice `match` on `ChainError.kind()` is now possible, which returns `&T`, meaning -`&Func1ErrorKind` here. +Also, a nice `match` on `chainerror::Error.kind()` is now possible, which returns `&T`, meaning `&Func1ErrorKind` here. ~~~rust {{#include ../examples/tutorial10.rs}} diff --git a/booksrc/tutorial11.md b/booksrc/tutorial11.md index 197b9d3..7d37814 100644 --- a/booksrc/tutorial11.md +++ b/booksrc/tutorial11.md @@ -21,7 +21,7 @@ which gives us a lot more detail. To create your own Errors, you might find [crates](https://crates.io) which create enum `Display+Debug` via derive macros. -Also noteworthy is [custom_error](https://crates.io/crates/custom_error) to define your custom errors, +Also, noteworthy is [custom_error](https://crates.io/crates/custom_error) to define your custom errors, which can then be used with `chainerror`. ~~~rust diff --git a/booksrc/tutorial12.md b/booksrc/tutorial12.md index a8c80e6..037803b 100644 --- a/booksrc/tutorial12.md +++ b/booksrc/tutorial12.md @@ -1,6 +1,6 @@ # Deref for the ErrorKind -Because ChainError implements Deref to &T, we can also match on `*e` instead of `e.kind()` +Because chainerror::Error implements Deref to &T, we can also match on `*e` instead of `e.kind()` or call a function with `&e` ~~~rust {{#include ../examples/tutorial12.rs}} diff --git a/booksrc/tutorial13.md b/booksrc/tutorial13.md index 6d438f4..0ed9db8 100644 --- a/booksrc/tutorial13.md +++ b/booksrc/tutorial13.md @@ -1,6 +1,6 @@ # Writing a library -I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `ChainError` +I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `chainerror::Error` so you can tell your library users to use the `.kind()` method as `std::io::Error` does. If you later decide to make your own `Error` implementation, your library users don't diff --git a/booksrc/tutorial2.md b/booksrc/tutorial2.md index 27e1fbb..a330c1a 100644 --- a/booksrc/tutorial2.md +++ b/booksrc/tutorial2.md @@ -25,7 +25,7 @@ along with the `Location` of the `context()` call and returns `Err(newerror)`. `?` then returns the inner error applying `.into()`, so that we again have a `Err(Box)` as a result. -The `Debug` implementation of `ChainError` (which is returned by `context()`) +The `Debug` implementation of `chainerror::Error` (which is returned by `context()`) prints the `Debug` of `T` prefixed with the stored filename and line number. -`ChainError` in our case is `ChainError<&str>`. +`chainerror::Error` in our case is `chainerror::Error<&str>`. diff --git a/booksrc/tutorial3.md b/booksrc/tutorial3.md index e44cc7d..f6c26ac 100644 --- a/booksrc/tutorial3.md +++ b/booksrc/tutorial3.md @@ -14,13 +14,13 @@ If you compare the output to the previous example, you will see, that: ~~~ -Error: src/main.rs:19: "func1 error" +Error: examples/tutorial2.rs:20:16: func1 error ~~~ changed to just: ~~~ -src/main.rs:16: "func1 error" +examples/tutorial3.rs:17:13: func1 error ~~~ This is, because we caught the error of `func1()` in `main()` and print it out ourselves. diff --git a/booksrc/tutorial5.md b/booksrc/tutorial5.md index 05db784..57932fa 100644 --- a/booksrc/tutorial5.md +++ b/booksrc/tutorial5.md @@ -14,5 +14,4 @@ Sometimes you want to inspect the `source()` of an `Error`. Note, that because we changed the output of the error in `main()` from `Debug` to `Display`, we don't see the error backtrace with filename and line number. -To enable the `Display` backtrace, you have to enable the feature `display-cause` for `chainerror`. - \ No newline at end of file +To use the `Display` backtrace, you have to use the alternative display format output `{:#}`. diff --git a/booksrc/tutorial7.md b/booksrc/tutorial7.md index ccb6ff6..90665f7 100644 --- a/booksrc/tutorial7.md +++ b/booksrc/tutorial7.md @@ -4,15 +4,15 @@ ~~~rust,ignore fn is_chain(&self) -> bool -fn downcast_chain_ref(&self) -> Option<&ChainError> -fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> +fn downcast_chain_ref(&self) -> Option<&chainerror::Error> +fn downcast_chain_mut(&mut self) -> Option<&mut chainerror::Error> fn root_cause(&self) -> Option<&(dyn Error + 'static)> fn find_cause(&self) -> Option<&U> -fn find_chain_cause(&self) -> Option<&ChainError> +fn find_chain_cause(&self) -> Option<&chainerror::Error> fn kind<'a>(&'a self) -> &'a T ~~~ -Using `downcast_chain_ref::()` gives a `ChainError`, which can be used +Using `downcast_chain_ref::()` gives a `chainerror::Error`, which can be used to call `.find_cause::()`. ~~~rust,ignore diff --git a/booksrc/tutorial8.md b/booksrc/tutorial8.md index da0c405..17070fe 100644 --- a/booksrc/tutorial8.md +++ b/booksrc/tutorial8.md @@ -1,14 +1,14 @@ # Finding an Error cause -To distinguish the errors occuring in various places, we can define named string errors with the +To distinguish the errors occurring in various places, we can define named string errors with the "new type" pattern. ~~~rust,ignore -derive_str_context!(Func2Error); -derive_str_context!(Func1Error); +chainerror::str_context!(Func2Error); +chainerror::str_context!(Func1Error); ~~~ -Instead of `ChainError` we now have `struct Func1Error(String)` and `ChainError`. +Instead of `chainerror::Error` we now have `struct Func1Error(String)` and `chainerror::Error`. In the `main` function you can see, how we can match the different errors. @@ -18,9 +18,9 @@ Also see: ~~~ as a shortcut to ~~~rust,ignore - if let Some(f2err) = f1err.find_cause::>() { + if let Some(f2err) = f1err.find_cause::>() { ~~~ -hiding the `ChainError` implementation detail. +hiding the `chainerror::Error` implementation detail. ~~~rust {{#include ../examples/tutorial8.rs}} diff --git a/examples/example.rs b/examples/example.rs index 859a12c..02fcba2 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,8 +1,7 @@ +use chainerror::prelude::v2::*; use std::error::Error; +use std::fmt; use std::io; -use std::result::Result; - -use chainerror::prelude::v1::*; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; @@ -15,9 +14,9 @@ fn func3() -> Result<(), Box> { Ok(()) } -derive_str_context!(Func2Error); +chainerror::str_context!(Func2Error); -fn func2() -> ChainResult<(), Func2Error> { +fn func2() -> chainerror::Result<(), Func2Error> { func3().context(Func2Error("func2 error: calling func3".to_string()))?; Ok(()) } @@ -27,8 +26,8 @@ enum Func1Error { IO(String), } -impl ::std::fmt::Display for Func1Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl fmt::Display for Func1Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Func1Error::Func2 => write!(f, "func1 error calling func2"), Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename), @@ -36,13 +35,13 @@ impl ::std::fmt::Display for Func1Error { } } -impl ::std::fmt::Debug for Func1Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl fmt::Debug for Func1Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self) } } -fn func1() -> ChainResult<(), Func1Error> { +fn func1() -> chainerror::Result<(), Func1Error> { func2().context(Func1Error::Func2)?; let filename = String::from("bar.txt"); do_some_io().context(Func1Error::IO(filename))?; diff --git a/examples/tutorial1.rs b/examples/tutorial1.rs index 939fb73..08ca3e8 100644 --- a/examples/tutorial1.rs +++ b/examples/tutorial1.rs @@ -3,7 +3,6 @@ use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial10.rs b/examples/tutorial10.rs index 5118c63..3e0af4a 100644 --- a/examples/tutorial10.rs +++ b/examples/tutorial10.rs @@ -1,14 +1,14 @@ -use chainerror::prelude::v1::*; +use chainerror::Context as _; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -derive_str_context!(Func2Error); +chainerror::str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -32,7 +32,7 @@ impl ::std::fmt::Display for Func1ErrorKind { } impl ::std::error::Error for Func1ErrorKind {} -fn func1() -> ChainResult<(), Func1ErrorKind> { +fn func1() -> chainerror::Result<(), Func1ErrorKind> { func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); do_some_io().context(Func1ErrorKind::IO(filename))?; diff --git a/examples/tutorial11.rs b/examples/tutorial11.rs index a943b97..abbca94 100644 --- a/examples/tutorial11.rs +++ b/examples/tutorial11.rs @@ -1,14 +1,14 @@ -use chainerror::prelude::v1::*; +use chainerror::Context as _; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -derive_str_context!(Func2Error); +chainerror::str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -38,7 +38,7 @@ impl ::std::fmt::Debug for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} -fn func1() -> ChainResult<(), Func1ErrorKind> { +fn func1() -> chainerror::Result<(), Func1ErrorKind> { func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); do_some_io().context(Func1ErrorKind::IO(filename))?; diff --git a/examples/tutorial12.rs b/examples/tutorial12.rs index e2bf644..caa8fca 100644 --- a/examples/tutorial12.rs +++ b/examples/tutorial12.rs @@ -1,14 +1,14 @@ -use chainerror::prelude::v1::*; +use chainerror::Context as _; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -derive_str_context!(Func2Error); +chainerror::str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -38,7 +38,7 @@ impl ::std::fmt::Debug for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} -fn func1() -> ChainResult<(), Func1ErrorKind> { +fn func1() -> chainerror::Result<(), Func1ErrorKind> { func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); do_some_io().context(Func1ErrorKind::IO(filename))?; diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 35dc832..0d50d68 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -2,7 +2,8 @@ #![allow(clippy::redundant_pattern_matching)] pub mod mycrate { - use chainerror::prelude::v1::*; + use chainerror::Context as _; + use std::io; fn do_some_io() -> std::result::Result<(), Box> { @@ -10,7 +11,7 @@ pub mod mycrate { Ok(()) } - derive_str_context!(Func2Error); + chainerror::str_context!(Func2Error); fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; @@ -24,7 +25,7 @@ pub mod mycrate { IO(String), } - derive_err_kind!(Error, ErrorKind); + chainerror::err_kind!(Error, ErrorKind); pub type Result = std::result::Result; diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs index 39594b4..42355b3 100644 --- a/examples/tutorial15.rs +++ b/examples/tutorial15.rs @@ -2,7 +2,8 @@ #![allow(clippy::redundant_pattern_matching)] pub mod mycrate { - use chainerror::prelude::v1::*; + use chainerror::{Context as _, ErrorDown as _}; + use std::io; fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { @@ -10,7 +11,7 @@ pub mod mycrate { Ok(()) } - derive_str_context!(Func2Error); + chainerror::str_context!(Func2Error); fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; @@ -26,7 +27,7 @@ pub mod mycrate { Unknown, } - derive_err_kind!(Error, ErrorKind); + chainerror::err_kind!(Error, ErrorKind); pub type Result = std::result::Result; impl std::fmt::Display for ErrorKind { diff --git a/examples/tutorial2.rs b/examples/tutorial2.rs index 8c68dc0..96742ad 100644 --- a/examples/tutorial2.rs +++ b/examples/tutorial2.rs @@ -1,8 +1,7 @@ -use chainerror::prelude::v1::*; +use chainerror::Context as _; use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial3.rs b/examples/tutorial3.rs index 8475d6f..62a44eb 100644 --- a/examples/tutorial3.rs +++ b/examples/tutorial3.rs @@ -1,8 +1,7 @@ -use chainerror::prelude::v1::*; +use chainerror::Context as _; use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial4.rs b/examples/tutorial4.rs index d1b247b..3dea51c 100644 --- a/examples/tutorial4.rs +++ b/examples/tutorial4.rs @@ -1,7 +1,7 @@ -use chainerror::prelude::v1::*; +use chainerror::Context as _; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial5.rs b/examples/tutorial5.rs index 333208c..d9fe708 100644 --- a/examples/tutorial5.rs +++ b/examples/tutorial5.rs @@ -1,7 +1,7 @@ -use chainerror::prelude::v1::*; +use chainerror::Context as _; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial6.rs b/examples/tutorial6.rs index de1a0a8..5d81310 100644 --- a/examples/tutorial6.rs +++ b/examples/tutorial6.rs @@ -1,10 +1,10 @@ #![allow(clippy::single_match)] #![allow(clippy::redundant_pattern_matching)] -use chainerror::prelude::v1::*; +use chainerror::Context as _; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial7.rs b/examples/tutorial7.rs index 9a1ca67..214484c 100644 --- a/examples/tutorial7.rs +++ b/examples/tutorial7.rs @@ -1,10 +1,10 @@ #![allow(clippy::single_match)] #![allow(clippy::redundant_pattern_matching)] -use chainerror::prelude::v1::*; +use chainerror::{Context as _, ErrorDown as _}; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial8.rs b/examples/tutorial8.rs index cf5e654..7661bd8 100644 --- a/examples/tutorial8.rs +++ b/examples/tutorial8.rs @@ -1,14 +1,14 @@ -use chainerror::prelude::v1::*; +use chainerror::{Context as _, ErrorDown as _}; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -derive_str_context!(Func2Error); +chainerror::str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -16,7 +16,7 @@ fn func2() -> Result<(), Box> { Ok(()) } -derive_str_context!(Func1Error); +chainerror::str_context!(Func1Error); fn func1() -> Result<(), Box> { func2().context(Func1Error("func1 error".to_string()))?; @@ -28,7 +28,7 @@ fn main() -> Result<(), Box> { if let Some(f1err) = e.downcast_chain_ref::() { eprintln!("Func1Error: {}", f1err); - if let Some(f2err) = f1err.find_cause::>() { + if let Some(f2err) = f1err.find_cause::>() { eprintln!("Func2Error: {}", f2err); } diff --git a/examples/tutorial9.rs b/examples/tutorial9.rs index 754c621..1cf24d3 100644 --- a/examples/tutorial9.rs +++ b/examples/tutorial9.rs @@ -1,14 +1,14 @@ -use chainerror::prelude::v1::*; +use chainerror::{Context as _, ErrorDown}; + use std::error::Error; use std::io; -use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -derive_str_context!(Func2Error); +chainerror::str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -16,8 +16,8 @@ fn func2() -> Result<(), Box> { Ok(()) } -derive_str_context!(Func1ErrorFunc2); -derive_str_context!(Func1ErrorIO); +chainerror::str_context!(Func1ErrorFunc2); +chainerror::str_context!(Func1ErrorIO); fn func1() -> Result<(), Box> { func2().context(Func1ErrorFunc2("func1 error calling func2".to_string()))?; @@ -28,7 +28,7 @@ fn func1() -> Result<(), Box> { fn main() -> Result<(), Box> { if let Err(e) = func1() { - if let Some(s) = e.downcast_ref::>() { + if let Some(s) = e.downcast_ref::>() { eprintln!("Func1ErrorIO:\n{:?}", s); } From cb9465f0dfa85690002c9dfc1276a2360d62a9e0 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 16:22:15 +0200 Subject: [PATCH 73/85] chore: remove obsolete README.tpl Signed-off-by: Harald Hoyer --- README.tpl | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 README.tpl diff --git a/README.tpl b/README.tpl deleted file mode 100644 index 01e969a..0000000 --- a/README.tpl +++ /dev/null @@ -1,24 +0,0 @@ -[![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) -{{badges}} - -# {{crate}} - -{{readme}} - -## License - -Licensed under either of - -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. From aaca6945b0fcb0fe3239a9eac18093183bcacd79 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 16:32:46 +0200 Subject: [PATCH 74/85] feat: add `new(Into)` method for `str_context!` types Signed-off-by: Harald Hoyer --- examples/example.rs | 2 +- examples/tutorial8.rs | 2 +- examples/tutorial9.rs | 2 +- src/lib.rs | 9 +++++++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index 02fcba2..c1d5702 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -17,7 +17,7 @@ fn func3() -> Result<(), Box> { chainerror::str_context!(Func2Error); fn func2() -> chainerror::Result<(), Func2Error> { - func3().context(Func2Error("func2 error: calling func3".to_string()))?; + func3().context(Func2Error::new("func2 error: calling func3"))?; Ok(()) } diff --git a/examples/tutorial8.rs b/examples/tutorial8.rs index 7661bd8..e32a449 100644 --- a/examples/tutorial8.rs +++ b/examples/tutorial8.rs @@ -19,7 +19,7 @@ fn func2() -> Result<(), Box> { chainerror::str_context!(Func1Error); fn func1() -> Result<(), Box> { - func2().context(Func1Error("func1 error".to_string()))?; + func2().context(Func1Error::new("func1 error"))?; Ok(()) } diff --git a/examples/tutorial9.rs b/examples/tutorial9.rs index 1cf24d3..bbbe810 100644 --- a/examples/tutorial9.rs +++ b/examples/tutorial9.rs @@ -20,7 +20,7 @@ chainerror::str_context!(Func1ErrorFunc2); chainerror::str_context!(Func1ErrorIO); fn func1() -> Result<(), Box> { - func2().context(Func1ErrorFunc2("func1 error calling func2".to_string()))?; + func2().context(Func1ErrorFunc2::new("func1 error calling func2"))?; let filename = "bar.txt"; do_some_io().context(Func1ErrorIO(format!("Error reading '{}'", filename)))?; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index a4c82c4..95dbcfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,7 +84,7 @@ impl Error { /// chainerror::str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { - /// func2().context(Func1Error("func1 error".into()))?; + /// func2().context(Func1Error::new("func1 error"))?; /// Ok(()) /// } /// @@ -576,7 +576,7 @@ where /// chainerror::str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { -/// func2().context(Func1Error("func1 error".into()))?; +/// func2().context(Func1Error::new("func1 error"))?; /// Ok(()) /// } /// # if let Err(e) = func1() { @@ -595,6 +595,11 @@ macro_rules! str_context { ($e:ident) => { #[derive(Clone)] pub struct $e(pub String); + impl $e { + pub fn new>(s: S) -> Self { + $e(s.into()) + } + } impl ::std::fmt::Display for $e { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "{}", self.0) From 101d2074e112d72020752ff1e21c08047c53efdb Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 17:04:39 +0200 Subject: [PATCH 75/85] feat: removed prelude It was causing the rust compiler to output the renamed structs in error messages confusing users who don't know about the old renamed ones. Signed-off-by: Harald Hoyer --- src/lib.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 95dbcfe..f15fd7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,26 +8,6 @@ use std::error::Error as StdError; use std::fmt::{Debug, Display, Formatter}; use std::panic::Location; -pub mod prelude { - //! convenience prelude - pub mod v1 { - //! convenience prelude - pub use super::super::Context as _; - pub use super::super::Context as ResultTrait; - pub use super::super::Error as ChainError; - pub use super::super::ErrorDown as _; - pub use super::super::ErrorDown as ChainErrorDown; - pub use super::super::Result as ChainResult; - pub use crate::err_kind as derive_err_kind; - pub use crate::str_context as derive_str_context; - } - pub mod v2 { - //! convenience prelude - pub use super::super::Context as _; - pub use super::super::ErrorDown as _; - } -} - /// chains an inner error kind `T` with a causing error pub struct Error { occurrence: Option, From 46b7f58e72671756d3462cfecc3c92d195c6ed37 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 17:06:10 +0200 Subject: [PATCH 76/85] feat: add `annotate()` method to Context to just annotate the passed error with location data Signed-off-by: Harald Hoyer --- examples/example.rs | 9 +++++++-- src/lib.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index c1d5702..82c073f 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,4 +1,4 @@ -use chainerror::prelude::v2::*; +use chainerror::Context as _; use std::error::Error; use std::fmt; use std::io; @@ -8,12 +8,17 @@ fn do_some_io() -> Result<(), Box> { Ok(()) } -fn func3() -> Result<(), Box> { +fn func4() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } +fn func3() -> Result<(), Box> { + func4().annotate()?; + Ok(()) +} + chainerror::str_context!(Func2Error); fn func2() -> chainerror::Result<(), Func2Error> { diff --git a/src/lib.rs b/src/lib.rs index f15fd7d..372cef2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -216,6 +216,9 @@ pub trait Context>> { /// Decorate the error with a `kind` of type `T` and the source `Location` fn context(self, kind: T) -> std::result::Result>; + /// Decorate the error just with the source `Location` + fn annotate(self) -> 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, @@ -223,6 +226,21 @@ pub trait Context>> { ) -> std::result::Result>; } +/// Convenience type to just decorate the error with the source `Location` +pub struct AnnotatedError(()); + +impl Display for AnnotatedError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "(passed error)") + } +} + +impl Debug for AnnotatedError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "(passed error)") + } +} + impl>> Context for std::result::Result { @@ -239,6 +257,19 @@ impl>> Context } } + #[track_caller] + #[inline] + fn annotate(self) -> std::result::Result> { + match self { + Ok(t) => Ok(t), + Err(error_cause) => Err(Error::new( + AnnotatedError(()), + Some(error_cause.into()), + Some(Location::caller().to_string()), + )), + } + } + #[track_caller] #[inline] fn map_context T>( From 9e03541ac102f9d3b3eab529dcc66c74be8e49a8 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 17:06:57 +0200 Subject: [PATCH 77/85] doc: extend README.md with display formats Signed-off-by: Harald Hoyer --- README.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/README.md b/README.md index 865cb0d..6f57ae8 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,81 @@ Along with the `Error` struct, `chainerror` comes with some useful helper mac Debug information is worth it! +## Multiple Output Formats + +`chainerror` supports multiple output formats, which can be selected with the different format specifiers: + +* `{}`: Display +```text +func1 error calling func2 +``` + +* `{:#}`: Alternative Display +```text +func1 error calling func2 +Caused by: + func2 error: calling func3 +Caused by: + (passed error) +Caused by: + Error reading 'foo.txt' +Caused by: + entity not found +``` + +* `{:?}`: Debug +```text +examples/example.rs:50:13: func1 error calling func2 +Caused by: +examples/example.rs:25:13: Func2Error(func2 error: calling func3) +Caused by: +examples/example.rs:18:13: (passed error) +Caused by: +examples/example.rs:13:18: Error reading 'foo.txt' +Caused by: +Kind(NotFound) + +``` + +* `{:#?}`: Alternative Debug +```text +Error { + occurrence: Some( + "examples/example.rs:50:13", + ), + kind: func1 error calling func2, + source: Some( + Error { + occurrence: Some( + "examples/example.rs:25:13", + ), + kind: Func2Error(func2 error: calling func3), + source: Some( + Error { + occurrence: Some( + "examples/example.rs:18:13", + ), + kind: (passed error), + source: Some( + Error { + occurrence: Some( + "examples/example.rs:13:18", + ), + kind: "Error reading 'foo.txt'", + source: Some( + Kind( + NotFound, + ), + ), + }, + ), + }, + ), + }, + ), +} +``` + ## Tutorial Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) From c630f84690ad8f1864c95f50d962f02427cd5c06 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 17:09:18 +0200 Subject: [PATCH 78/85] doc: README.md changed `text` to `console` Signed-off-by: Harald Hoyer --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6f57ae8..3a46567 100644 --- a/README.md +++ b/README.md @@ -95,12 +95,12 @@ Debug information is worth it! `chainerror` supports multiple output formats, which can be selected with the different format specifiers: * `{}`: Display -```text +```console func1 error calling func2 ``` * `{:#}`: Alternative Display -```text +```console func1 error calling func2 Caused by: func2 error: calling func3 @@ -113,7 +113,7 @@ Caused by: ``` * `{:?}`: Debug -```text +```console examples/example.rs:50:13: func1 error calling func2 Caused by: examples/example.rs:25:13: Func2Error(func2 error: calling func3) @@ -127,7 +127,7 @@ Kind(NotFound) ``` * `{:#?}`: Alternative Debug -```text +```console Error { occurrence: Some( "examples/example.rs:50:13", From 24382fbaed2d2acd97076f1f1a46c854da6789e1 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 28 Jul 2023 17:11:10 +0200 Subject: [PATCH 79/85] chore: Release chainerror version 1.0.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c4f3b17..59b8808 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.8.2" +version = "1.0.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT OR Apache-2.0" From 74f1dd43148be7d29b4e4d7d29cc40298d5ba9c5 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 31 Mar 2025 13:07:27 +0200 Subject: [PATCH 80/85] chore: update coverage workflow to simplify and modernize setup Revised the GitHub Actions workflow for code coverage by updating dependencies, using modern, maintained actions, and improving configuration clarity. Streamlined Rust installation and replaced manual steps with dedicated actions for better reliability. Adjusted Codecov settings for stricter error handling. Signed-off-by: Harald Hoyer --- .github/workflows/coverage.yml | 35 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 21025a8..6492f1a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -13,31 +13,24 @@ on: types: - created + jobs: - test: - name: coverage + coverage: runs-on: ubuntu-latest + env: + CARGO_TERM_COLOR: always steps: - - uses: actions/checkout@v1 - - uses: dtolnay/rust-toolchain@master - with: - target: x86_64-unknown-linux-gnu - toolchain: nightly - components: llvm-tools-preview - + - uses: actions/checkout@v4 + - name: Install Rust + run: | + rustup update nightly - name: Install cargo-llvm-cov - run: > - curl -LsSf 'https://github.com/taiki-e/cargo-llvm-cov/releases/download/v0.5.23/cargo-llvm-cov-x86_64-unknown-linux-musl.tar.gz' - | tar xzf - - && mv cargo-llvm-cov $HOME/.cargo/bin - - - name: Run cargo-llvm-cov - run: cargo llvm-cov --doctests --all --all-features --lcov --output-path lcov.info - + uses: taiki-e/install-action@cargo-llvm-cov + - name: Generate code coverage + run: cargo +nightly llvm-cov --all-features --workspace --codecov --doctests --output-path codecov.json - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: - directory: ./ - fail_ci_if_error: false - files: ./lcov.info - verbose: true + token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos + files: codecov.json + fail_ci_if_error: true From 5cb96eeee39b4f9efed24ddda81e6dbdf3f6e03a Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 31 Mar 2025 13:21:34 +0200 Subject: [PATCH 81/85] chore: update Rust installation in coverage workflow Replaced manual Rust installation with dtolnay/rust-toolchain action for better maintainability and clarity. Added necessary components like llvm-tools-preview to support code coverage generation. These changes simplify the workflow setup. --- .github/workflows/coverage.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 6492f1a..7dff904 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,9 +21,11 @@ jobs: CARGO_TERM_COLOR: always steps: - uses: actions/checkout@v4 - - name: Install Rust - run: | - rustup update nightly + - uses: dtolnay/rust-toolchain@master + with: + target: x86_64-unknown-linux-gnu + toolchain: nightly + components: llvm-tools-preview - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: Generate code coverage From 9aa0183d654c8394738c2f8dc428df6c0617f38f Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 31 Mar 2025 13:54:45 +0200 Subject: [PATCH 82/85] tests: add comprehensive unit tests for error handling utilities This commit introduces a series of unit tests to validate various error handling functionalities, including error chaining, root cause extraction, display/debug formatting, annotation, context mapping, downcasting, and custom error kinds. These tests improve code reliability and ensure expected behavior across different error scenarios. --- src/lib.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 372cef2..51eaf7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -607,6 +607,7 @@ macro_rules! str_context { #[derive(Clone)] pub struct $e(pub String); impl $e { + #[allow(dead_code)] pub fn new>(s: S) -> Self { $e(s.into()) } @@ -740,3 +741,137 @@ macro_rules! err_kind { } }; } + +#[cfg(test)] +mod tests { + use super::Context as _; + use super::*; + use std::io; + + #[test] + fn test_error_chain_with_multiple_causes() { + // Create a chain of errors + let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); + + str_context!(Level3Error); + str_context!(Level2Error); + str_context!(Level1Error); + + let err = Result::<(), _>::Err(io_error.into()) + .context(Level3Error("level 3".into())) + .context(Level2Error("level 2".into())) + .context(Level1Error("level 1".into())) + .unwrap_err(); + + // Test the error chain + assert!(err.is_chain::()); + assert!(err.find_chain_cause::().is_some()); + assert!(err.find_chain_cause::().is_some()); + assert!(err.find_chain_cause::().is_some()); + } + + #[test] + fn test_error_root_cause() { + let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); + + str_context!(WrapperError); + let err = Result::<(), _>::Err(io_error.into()) + .context(WrapperError("wrapper".into())) + .unwrap_err(); + + let root = err.root_cause().unwrap(); + assert!(root.is_chain::()); + } + + #[test] + fn test_error_display_and_debug() { + str_context!(CustomError); + let err = Error::new( + CustomError("test error".into()), + None, + Some("src/lib.rs:100".into()), + ); + + // Test Display formatting + assert_eq!(format!("{}", err), "test error"); + + // Test alternate Display formatting + assert_eq!(format!("{:#}", err), "test error"); + + // Test Debug formatting + let debug_output = format!("{:?}", err); + assert!(debug_output.contains("test error")); + assert!(debug_output.contains("src/lib.rs:100")); + } + + #[test] + fn test_error_annotation() { + let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); + let err = Result::<(), _>::Err(io_error.into()) + .annotate() + .unwrap_err(); + + assert!(err.source().is_some()); + err.source() + .unwrap() + .downcast_inner_ref::() + .unwrap(); + } + + #[test] + fn test_map_context() { + let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); + + str_context!(MappedError); + let err = Result::<(), _>::Err(io_error.into()) + .map_context(|e| MappedError(format!("Mapped: {}", e))) + .unwrap_err(); + + assert!(err.is_chain::()); + assert!(err.find_chain_cause::().is_some()); + } + + #[test] + fn test_error_downcasting() { + str_context!(OriginalError); + let original = Error::new(OriginalError("test".into()), None, None); + + let error: Box = Box::new(original); + + // Test downcast_chain_ref + assert!(error.is_chain::()); + assert!(error.downcast_chain_ref::().is_some()); + + // Test downcast_inner_ref + let inner = error.downcast_inner_ref::(); + assert!(inner.is_some()); + } + + #[derive(Debug, Clone)] + enum TestErrorKind { + Basic(String), + Complex { message: String }, + } + + impl Display for TestErrorKind { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + TestErrorKind::Basic(msg) => write!(f, "Basic error: {}", msg), + TestErrorKind::Complex { message } => write!(f, "Complex error: {}", message), + } + } + } + + #[test] + fn test_err_kind_macro() { + err_kind!(TestError, TestErrorKind); + + let err = TestError::from(TestErrorKind::Basic("test".into())); + assert!(matches!(err.kind(), TestErrorKind::Basic(_))); + + let complex_err = TestError::from(TestErrorKind::Complex { + message: "test".into(), + }); + assert!(matches!(complex_err.kind(), TestErrorKind::Complex { .. })); + } +} From 5390007cbeb85887e56d9a9c517ea1f0f0a51e85 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 31 Mar 2025 14:04:01 +0200 Subject: [PATCH 83/85] doc: add usage example for error handling in main function This commit adds a usage example demonstrating how to handle errors returned by the `func1` function in the main function. The example provides clarity on practical error handling and makes the documentation more comprehensive for users. --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 51eaf7e..450eab3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -689,6 +689,12 @@ macro_rules! str_context { /// do_some_io(filename).map_context(|e| ErrorKind::from(e))?; /// Ok(()) /// } +/// +/// # fn main() { +/// # if let Err(e) = func1() { +/// # eprintln!("Error:\n{:?}", e); +/// # } +/// # } /// ``` #[macro_export] macro_rules! err_kind { From 75b7fdf3630418d29c4f70c6b8e6b5dc832346fe Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 31 Mar 2025 14:14:32 +0200 Subject: [PATCH 84/85] tests: add tests for annotated error display, debug, and chaining Introduced tests to verify the behavior of `AnnotatedError` in display, debug, and error chaining scenarios. Ensured proper formatting for standalone errors and preservation of the error chain for wrapped errors. --- src/lib.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 450eab3..982ef83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -874,10 +874,43 @@ mod tests { let err = TestError::from(TestErrorKind::Basic("test".into())); assert!(matches!(err.kind(), TestErrorKind::Basic(_))); + // The annotated error should display "(passed error)" even in a chain + assert_eq!(format!("{}", err), "Basic error: test"); + assert_eq!(format!("{:?}", err), "Basic(\"test\")"); let complex_err = TestError::from(TestErrorKind::Complex { message: "test".into(), }); assert!(matches!(complex_err.kind(), TestErrorKind::Complex { .. })); + // The annotated error should display "(passed error)" even in a chain + assert_eq!(format!("{}", complex_err), "Complex error: test"); + assert_eq!( + format!("{:?}", complex_err), + "Complex { message: \"test\" }" + ); + } + #[test] + fn test_annotated_error_display_and_debug() { + let annotated = AnnotatedError(()); + + // Test Display formatting + assert_eq!(format!("{}", annotated), "(passed error)"); + + // Test Debug formatting + assert_eq!(format!("{:?}", annotated), "(passed error)"); + + // Test with error chain + let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); + let err = Result::<(), _>::Err(io_error.into()) + .annotate() + .unwrap_err(); + + // The annotated error should display "(passed error)" even in a chain + assert_eq!(format!("{}", err), "(passed error)"); + assert!(format!("{:?}", err).contains("(passed error)")); + + // Verify the error chain is preserved + assert!(err.source().is_some()); + assert!(err.source().unwrap().is_chain::()); } } From 4c42d3759894492b90af063ad82ceacf0925ee2c Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 31 Mar 2025 14:45:22 +0200 Subject: [PATCH 85/85] refactor: simplify downcasting logic with `std::mem::transmute` Simplified the downcasting implementations by replacing pointer casting logic with `std::mem::transmute`, ensuring type safety after matching. Added tests to validate various downcasting behaviors for both owned and trait-object error scenarios, improving overall reliability and test coverage. Signed-off-by: Harald Hoyer --- src/lib.rs | 149 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 129 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 982ef83..623523d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -338,11 +338,8 @@ impl ErrorDown for Error { #[inline] 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 StdError as *const &Error)) - } + // Use transmute when we've verified the types match + unsafe { Some(std::mem::transmute::<&Error, &Error>(self)) } } else { None } @@ -351,11 +348,8 @@ impl ErrorDown for Error { #[inline] 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 StdError as *mut &mut Error)) - } + // Use transmute when we've verified the types match + unsafe { Some(std::mem::transmute::<&mut Error, &mut Error>(self)) } } else { None } @@ -363,11 +357,8 @@ impl ErrorDown for Error { #[inline] 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 StdError as *const &Error)).kind) - } + // Use transmute when we've verified the types match + unsafe { Some(std::mem::transmute::<&U, &T>(&self.kind)) } } else { None } @@ -376,11 +367,8 @@ impl ErrorDown for Error { #[inline] 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 StdError as *mut &mut Error)).kind) - } + // Use transmute when we've verified the types match + unsafe { Some(std::mem::transmute::<&mut U, &mut T>(&mut self.kind)) } } else { None } @@ -913,4 +901,125 @@ mod tests { assert!(err.source().is_some()); assert!(err.source().unwrap().is_chain::()); } + + // Helper error types for testing + #[derive(Debug)] + struct TestError(String); + + impl std::fmt::Display for TestError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + + impl std::error::Error for TestError {} + + #[test] + fn test_downcast_chain_operations() { + // Create a test error chain + let original_error = Error::new( + TestError("test message".to_string()), + None, + Some("test location".to_string()), + ); + + // Test is_chain + assert!(original_error.is_chain::()); + assert!(!original_error.is_chain::()); + + // Test downcast_chain_ref + let downcast_ref = original_error.downcast_chain_ref::(); + assert!(downcast_ref.is_some()); + let downcast_kind = downcast_ref.unwrap().kind(); + assert_eq!(format!("{}", downcast_kind), "test message"); + assert_eq!( + format!("{:?}", downcast_kind), + "TestError(\"test message\")" + ); + + // Test invalid downcast_chain_ref + let invalid_downcast = original_error.downcast_chain_ref::(); + assert!(invalid_downcast.is_none()); + + // Test downcast_chain_mut + let mut mutable_error = original_error; + let downcast_mut = mutable_error.downcast_chain_mut::(); + assert!(downcast_mut.is_some()); + assert_eq!(downcast_mut.unwrap().kind().0, "test message"); + + // Test invalid downcast_chain_mut + let invalid_downcast_mut = mutable_error.downcast_chain_mut::(); + assert!(invalid_downcast_mut.is_none()); + } + + #[test] + fn test_downcast_inner_operations() { + // Create a test error + let mut error = Error::new( + TestError("inner test".to_string()), + None, + Some("test location".to_string()), + ); + + // Test downcast_inner_ref + let inner_ref = error.downcast_inner_ref::(); + assert!(inner_ref.is_some()); + assert_eq!(inner_ref.unwrap().0, "inner test"); + // Test invalid downcast_inner_ref + let invalid_inner = error.downcast_inner_ref::(); + assert!(invalid_inner.is_none()); + + // Test downcast_inner_mut + let inner_mut = error.downcast_inner_mut::(); + assert!(inner_mut.is_some()); + assert_eq!(inner_mut.unwrap().0, "inner test"); + + // Test invalid downcast_inner_mut + let invalid_inner_mut = error.downcast_inner_mut::(); + assert!(invalid_inner_mut.is_none()); + } + + #[test] + fn test_error_down_for_dyn_error() { + // Create a boxed error + let error: Box = Box::new(Error::new( + TestError("dyn test".to_string()), + None, + Some("test location".to_string()), + )); + + // Test is_chain through trait object + assert!(error.is_chain::()); + assert!(!error.is_chain::()); + + // Test downcast_chain_ref through trait object + let chain_ref = error.downcast_chain_ref::(); + assert!(chain_ref.is_some()); + assert_eq!(chain_ref.unwrap().kind().0, "dyn test"); + + // Test downcast_inner_ref through trait object + let inner_ref = error.downcast_inner_ref::(); + assert!(inner_ref.is_some()); + assert_eq!(inner_ref.unwrap().0, "dyn test"); + } + + #[test] + fn test_error_down_with_sync_send() { + // Create a boxed error with Send + Sync + let error: Box = Box::new(Error::new( + TestError("sync test".to_string()), + None, + Some("test location".to_string()), + )); + + // Test operations on Send + Sync error + assert!(error.is_chain::()); + assert!(error.downcast_chain_ref::().is_some()); + assert!(error.downcast_inner_ref::().is_some()); + + // Test invalid downcasts + assert!(!error.is_chain::()); + assert!(error.downcast_chain_ref::().is_none()); + assert!(error.downcast_inner_ref::().is_none()); + } }