diff --git a/README.md b/README.md index 7df21e4..6f57ae8 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; @@ -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) 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. 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..82c073f 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,24 +1,28 @@ +use chainerror::Context as _; 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))?; Ok(()) } -fn func3() -> Result<(), Box> { +fn func4() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } -derive_str_context!(Func2Error); +fn func3() -> Result<(), Box> { + func4().annotate()?; + Ok(()) +} -fn func2() -> ChainResult<(), Func2Error> { - func3().context(Func2Error("func2 error: calling func3".to_string()))?; +chainerror::str_context!(Func2Error); + +fn func2() -> chainerror::Result<(), Func2Error> { + func3().context(Func2Error::new("func2 error: calling func3"))?; Ok(()) } @@ -27,8 +31,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 +40,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..e32a449 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,10 +16,10 @@ fn func2() -> Result<(), Box> { Ok(()) } -derive_str_context!(Func1Error); +chainerror::str_context!(Func1Error); fn func1() -> Result<(), Box> { - func2().context(Func1Error("func1 error".to_string()))?; + func2().context(Func1Error::new("func1 error"))?; Ok(()) } @@ -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..bbbe810 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,11 +16,11 @@ 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()))?; + func2().context(Func1ErrorFunc2::new("func1 error calling func2"))?; let filename = "bar.txt"; do_some_io().context(Func1ErrorIO(format!("Error reading '{}'", filename)))?; Ok(()) @@ -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); } diff --git a/src/lib.rs b/src/lib.rs index 1b6039c..372cef2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,18 +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::ChainErrorDown as _; - pub use super::super::Error as ChainError; - pub use super::super::Result as ChainResult; - pub use super::super::ResultTrait as _; - pub use crate::{derive_err_kind, derive_str_context}; - } -} - /// chains an inner error kind `T` with a causing error pub struct Error { occurrence: Option, @@ -55,7 +43,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 +53,7 @@ impl Error { /// Ok(()) /// } /// - /// derive_str_context!(Func2Error); + /// chainerror::str_context!(Func2Error); /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; @@ -72,10 +61,10 @@ impl Error { /// Ok(()) /// } /// - /// derive_str_context!(Func1Error); + /// chainerror::str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { - /// func2().context(Func1Error("func1 error".into()))?; + /// func2().context(Func1Error::new("func1 error"))?; /// Ok(()) /// } /// @@ -107,13 +96,12 @@ 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::>(); + /// err.find_cause::>(); /// - /// // leave out the ChainError implementation detail + /// // leave out the chainerror::Error implementation detail /// err.find_chain_cause::(); /// ``` #[inline] @@ -130,17 +118,16 @@ 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::>(); + /// 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] @@ -159,7 +146,7 @@ impl Error { /// # Examples /// /// ```rust - /// use chainerror::prelude::v1::*; + /// use chainerror::Context as _; /// use std::error::Error; /// use std::io; /// @@ -168,7 +155,7 @@ impl Error { /// Ok(()) /// } /// - /// derive_str_context!(Func2Error); + /// chainerror::str_context!(Func2Error); /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; @@ -192,7 +179,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(()) @@ -225,10 +212,13 @@ 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>; + /// 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, @@ -236,7 +226,22 @@ pub trait ResultTrait>> { ) -> std::result::Result>; } -impl>> ResultTrait +/// 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 { #[track_caller] @@ -252,6 +257,19 @@ impl>> ResultTrait } } + #[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>( @@ -298,7 +316,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 +329,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 +387,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 +420,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 +453,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 +567,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,23 +576,23 @@ where /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// derive_str_context!(Func2Error); +/// chainerror::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(()) /// } /// -/// derive_str_context!(Func1Error); +/// 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() { /// # 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,10 +602,15 @@ where /// # } /// ``` #[macro_export] -macro_rules! derive_str_context { +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) @@ -612,7 +636,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 +650,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 +690,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>); diff --git a/tests/test_basic.rs b/tests/test_basic.rs index ac5412c..bbc9f16 100644 --- a/tests/test_basic.rs +++ b/tests/test_basic.rs @@ -1,4 +1,4 @@ -use chainerror::prelude::v1::*; +use chainerror::Context; #[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..9023078 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -1,4 +1,4 @@ -use chainerror::prelude::v1::*; +use chainerror::Context; use std::error::Error; use std::io;