rename cherr() to context()

This commit is contained in:
Harald Hoyer 2020-09-01 21:03:01 +02:00
parent 2af5fb7ad6
commit ed710fada3
25 changed files with 169 additions and 199 deletions

View file

@ -23,7 +23,5 @@ is-it-maintained-issue-resolution = { repository = "haraldh/chainerror" }
is-it-maintained-open-issues = { repository = "haraldh/chainerror" } is-it-maintained-open-issues = { repository = "haraldh/chainerror" }
[features] [features]
default = [ "location", "debug-cause" ] default = []
location = []
display-cause = [] display-cause = []
debug-cause = []

View file

@ -22,18 +22,9 @@ Debug information is worth it!
### Features ### Features
`default = [ "location", "debug-cause" ]`
`location`
: store the error location
`display-cause` `display-cause`
: turn on printing a backtrace of the errors in `Display` : turn on printing a backtrace of the errors in `Display`
`debug-cause`
: print a backtrace of the errors in `Debug`
## Tutorial ## Tutorial
Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
@ -101,12 +92,12 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(format!("Error reading '{}'", filename))?; do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(()) Ok(())
} }
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().cherr("func1 error")?; func2().context("func1 error")?;
Ok(()) Ok(())
} }
@ -139,14 +130,14 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func3() -> Result<(), Box<dyn Error + Send + Sync>> { fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(format!("Error reading '{}'", filename))?; do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> ChainResult<(), Func2Error> { fn func2() -> ChainResult<(), Func2Error> {
func3().cherr(Func2Error("func2 error: calling func3".into()))?; func3().context(Func2Error("func2 error: calling func3".into()))?;
Ok(()) Ok(())
} }
@ -171,9 +162,9 @@ impl ::std::fmt::Debug for Func1Error {
} }
fn func1() -> ChainResult<(), Func1Error> { fn func1() -> ChainResult<(), Func1Error> {
func2().cherr(Func1Error::Func2)?; func2().context(Func1Error::Func2)?;
let filename = String::from("bar.txt"); let filename = String::from("bar.txt");
do_some_io().cherr(Func1Error::IO(filename))?; do_some_io().context(Func1Error::IO(filename))?;
Ok(()) Ok(())
} }

View file

@ -9,7 +9,7 @@ this only
prints out the last `Error`. prints out the last `Error`.
~~~ ~~~
Error: StringError("func1 error") Error: "func1 error"
~~~ ~~~
The next chapters of this tutorial show how `chainerror` adds more information The next chapters of this tutorial show how `chainerror` adds more information

View file

@ -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 Because we derive `Debug` and implement `Display` our `Func1ErrorKind` enum, this enum can be used as
a `std::error::Error`. a `std::error::Error`.
Not using `String` errors anymore, the `cherr!()` macro seen in the beginning of Not using `String` errors anymore, the `context()` function seen in the beginning of
the tutorial has to be used again. the tutorial can be used again.
Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box<Error + Send + Sync>>` and we can Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box<Error + Send + Sync>>` and we can
use `ChainResult<(), Func1ErrorKind>`. use `ChainResult<(), Func1ErrorKind>`.

View file

@ -1,7 +1,7 @@
# Simple Chained String Errors # Simple Chained String Errors
With relatively small changes and the help of the `cherr!` macro of the `chainerror` crate With relatively small changes and the help of the `context()` method of the `chainerror` crate
the `String` errors are now chained together. the `&str` errors are now chained together.
Press the play button in the upper right corner and see the nice debug output. 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}} {{#include ../examples/tutorial2.rs:13:15}}
~~~ ~~~
The macro `cherr!(olderror, newerror)` stores `olderror` as the source/cause of `newerror` The function `context(newerror)` stores `olderror` as the source/cause of `newerror`
along with the filename (`file!()`) and line number (`line!()`) along with the `Location` of the `context()` call and returns `Err(newerror)`.
and returns `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<Error + Send + Sync>)` as a result. again have a `Err(Box<Error + Send + Sync>)` as a result.
The `Debug` implementation of `ChainError<T>` (which is returned by `cherr!()`) The `Debug` implementation of `ChainError<T>` (which is returned by `context()`)
prints the `Debug` of `T` prefixed with the stored filename and line number. prints the `Debug` of `T` prefixed with the stored filename and line number.
`ChainError<T>` in our case is `ChainError<String>`. `ChainError<T>` in our case is `ChainError<&str>`.

View file

@ -1,6 +1,6 @@
# Mapping Errors # 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 ~~~rust
{{#include ../examples/tutorial3.rs}} {{#include ../examples/tutorial3.rs}}

View file

@ -1,12 +1,7 @@
# Saving coding chars # More information
Because decorating an error with more information should not To give more context to the error, you want to use `format!`
let you jump through hoops, `chainerror` has a quick macro for that. to extend the information in the context string.
`mstrerror!()` fits right into `.map_err()` letting you quickly add
more debug strings.
`mstrerror!()` even understands `format!()` syntax like `println!()`.
~~~rust ~~~rust
{{#include ../examples/tutorial4.rs}} {{#include ../examples/tutorial4.rs}}

View file

@ -4,8 +4,8 @@ To distinguish the errors occuring in various places, we can define named string
"new type" pattern. "new type" pattern.
~~~rust,ignore ~~~rust,ignore
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
derive_str_cherr!(Func1Error); derive_str_context!(Func1Error);
~~~ ~~~
Instead of `ChainError<String>` we now have `struct Func1Error(String)` and `ChainError<Func1Error>`. Instead of `ChainError<String>` we now have `struct Func1Error(String)` and `ChainError<Func1Error>`.

View file

@ -11,14 +11,14 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func3() -> Result<(), Box<dyn Error + Send + Sync>> { fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(format!("Error reading '{}'", filename))?; do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> ChainResult<(), Func2Error> { fn func2() -> ChainResult<(), Func2Error> {
func3().cherr(Func2Error(format!("func2 error: calling func3")))?; func3().context(Func2Error(format!("func2 error: calling func3")))?;
Ok(()) Ok(())
} }
@ -43,9 +43,9 @@ impl ::std::fmt::Debug for Func1Error {
} }
fn func1() -> ChainResult<(), Func1Error> { fn func1() -> ChainResult<(), Func1Error> {
func2().cherr(Func1Error::Func2)?; func2().context(Func1Error::Func2)?;
let filename = String::from("bar.txt"); let filename = String::from("bar.txt");
do_some_io().cherr(Func1Error::IO(filename))?; do_some_io().context(Func1Error::IO(filename))?;
Ok(()) Ok(())
} }

View file

@ -8,11 +8,11 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
Ok(()) Ok(())
} }
@ -33,9 +33,9 @@ impl ::std::fmt::Display for Func1ErrorKind {
impl ::std::error::Error for Func1ErrorKind {} impl ::std::error::Error for Func1ErrorKind {}
fn func1() -> ChainResult<(), Func1ErrorKind> { fn func1() -> ChainResult<(), Func1ErrorKind> {
func2().cherr(Func1ErrorKind::Func2)?; func2().context(Func1ErrorKind::Func2)?;
let filename = String::from("bar.txt"); let filename = String::from("bar.txt");
do_some_io().cherr(Func1ErrorKind::IO(filename))?; do_some_io().context(Func1ErrorKind::IO(filename))?;
Ok(()) Ok(())
} }
@ -53,6 +53,8 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
} }
eprintln!("\nDebug Error:\n{:?}", e); eprintln!("\nDebug Error:\n{:?}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -8,11 +8,11 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
Ok(()) Ok(())
} }
@ -39,9 +39,9 @@ impl ::std::fmt::Debug for Func1ErrorKind {
impl ::std::error::Error for Func1ErrorKind {} impl ::std::error::Error for Func1ErrorKind {}
fn func1() -> ChainResult<(), Func1ErrorKind> { fn func1() -> ChainResult<(), Func1ErrorKind> {
func2().cherr(Func1ErrorKind::Func2)?; func2().context(Func1ErrorKind::Func2)?;
let filename = String::from("bar.txt"); let filename = String::from("bar.txt");
do_some_io().cherr(Func1ErrorKind::IO(filename))?; do_some_io().context(Func1ErrorKind::IO(filename))?;
Ok(()) Ok(())
} }
@ -59,6 +59,8 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
} }
eprintln!("\nDebug Error:\n{:?}", e); eprintln!("\nDebug Error:\n{:?}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -8,11 +8,11 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
Ok(()) Ok(())
} }
@ -39,9 +39,9 @@ impl ::std::fmt::Debug for Func1ErrorKind {
impl ::std::error::Error for Func1ErrorKind {} impl ::std::error::Error for Func1ErrorKind {}
fn func1() -> ChainResult<(), Func1ErrorKind> { fn func1() -> ChainResult<(), Func1ErrorKind> {
func2().cherr(Func1ErrorKind::Func2)?; func2().context(Func1ErrorKind::Func2)?;
let filename = String::from("bar.txt"); let filename = String::from("bar.txt");
do_some_io().cherr(Func1ErrorKind::IO(filename))?; do_some_io().context(Func1ErrorKind::IO(filename))?;
Ok(()) Ok(())
} }
@ -70,6 +70,8 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
} }
eprintln!("\nDebug Error:\n{:?}", e); eprintln!("\nDebug Error:\n{:?}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -7,11 +7,11 @@ pub mod mycrate {
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> { fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
Ok(()) Ok(())
} }
@ -35,9 +35,9 @@ pub mod mycrate {
} }
pub fn func1() -> Result<()> { pub fn func1() -> Result<()> {
func2().cherr(ErrorKind::Func2)?; func2().context(ErrorKind::Func2)?;
let filename = String::from("bar.txt"); let filename = String::from("bar.txt");
do_some_io().cherr(ErrorKind::IO(filename))?; do_some_io().context(ErrorKind::IO(filename))?;
Ok(()) Ok(())
} }
} }
@ -72,6 +72,8 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
} }
eprintln!("\nDebug Error:\n{:?}", e); eprintln!("\nDebug Error:\n{:?}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -27,7 +27,7 @@ pub mod mycrate {
} }
} }
macro_rules! mcherr { macro_rules! mcontext {
( $k:expr ) => {{ ( $k:expr ) => {{
|e| { |e| {
Error( Error(
@ -92,7 +92,7 @@ pub mod mycrate {
pub fn func2() -> std::result::Result<(), Error> { pub fn func2() -> std::result::Result<(), Error> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().map_err(mcherr!(ErrorKind::IO(format!( do_some_io().map_err(mcontext!(ErrorKind::IO(format!(
"Error reading '{}'", "Error reading '{}'",
filename filename
))))?; ))))?;
@ -115,7 +115,7 @@ pub mod mycrate {
} }
} }
macro_rules! mcherr { macro_rules! mcontext {
( $k:expr ) => {{ ( $k:expr ) => {{
|e| { |e| {
Error( Error(
@ -175,9 +175,9 @@ pub mod mycrate {
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
pub fn func1() -> Result<()> { pub fn func1() -> Result<()> {
func2().map_err(mcherr!(ErrorKind::Func2))?; func2().map_err(mcontext!(ErrorKind::Func2))?;
let filename = String::from("bar.txt"); 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(()) Ok(())
} }
} }
@ -212,6 +212,8 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
} }
eprintln!("\nDebug Error:\n{:?}", e); eprintln!("\nDebug Error:\n{:?}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -8,11 +8,11 @@ pub mod mycrate {
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> { fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filename = "foo.txt"; 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(()) Ok(())
} }
@ -85,15 +85,15 @@ pub mod mycrate {
let filename = "bar.txt"; let filename = "bar.txt";
do_some_io(filename).map_cherr(|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_cherr(|_| ErrorKind::IO(filename.into()))?; do_some_io(filename).map_context(|_| ErrorKind::IO(filename.into()))?;
do_some_io(filename).map_cherr(|e| ErrorKind::from(e))?; do_some_io(filename).map_context(|e| ErrorKind::from(e))?;
Ok(()) Ok(())
} }
pub fn super_func1() -> Result<()> { pub fn super_func1() -> Result<()> {
func1().map_cherr(|e| ErrorKind::from(e))?; func1().map_context(|e| ErrorKind::from(e))?;
Ok(()) Ok(())
} }
} }
@ -130,6 +130,8 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
} }
eprintln!("\nDebug Error:\n{:?}", e); eprintln!("\nDebug Error:\n{:?}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -11,14 +11,14 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = do_some_io() { if let Err(e) = do_some_io() {
Err(e).cherr("func2 error")?; Err(e).context("func2 error")?;
} }
Ok(()) Ok(())
} }
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func2() { if let Err(e) = func2() {
Err(e).cherr("func1 error")?; Err(e).context("func1 error")?;
} }
Ok(()) Ok(())
} }

View file

@ -10,18 +10,19 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
} }
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
do_some_io().cherr("func2 error")?; do_some_io().context("func2 error")?;
Ok(()) Ok(())
} }
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().cherr("func1 error")?; func2().context("func1 error")?;
Ok(()) Ok(())
} }
fn main() -> Result<(), Box<dyn Error + Send + Sync>> { fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func1() { if let Err(e) = func1() {
eprintln!("{:?}", e); eprintln!("{:?}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -10,18 +10,19 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(format!("Error reading '{}'", filename))?; do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(()) Ok(())
} }
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().cherr("func1 error")?; func2().context("func1 error")?;
Ok(()) Ok(())
} }
fn main() -> Result<(), Box<dyn Error + Send + Sync>> { fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func1() { if let Err(e) = func1() {
eprintln!("{:?}", e); eprintln!("{:?}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -10,7 +10,7 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(format!("Error reading '{}'", filename))?; do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(()) Ok(())
} }
@ -18,7 +18,7 @@ fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func2() { if let Err(e) = func2() {
if let Some(s) = e.source() { if let Some(s) = e.source() {
eprintln!("func2 failed because of '{}'", s); eprintln!("func2 failed because of '{}'", s);
Err(e).cherr("func1 error")?; Err(e).context("func1 error")?;
} }
} }
Ok(()) Ok(())
@ -27,6 +27,7 @@ fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
fn main() -> Result<(), Box<dyn Error + Send + Sync>> { fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func1() { if let Err(e) = func1() {
eprintln!("{}", e); eprintln!("{}", e);
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -10,12 +10,12 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(format!("Error reading '{}'", filename))?; do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(()) Ok(())
} }
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().cherr("func1 error")?; func2().context("func1 error")?;
Ok(()) Ok(())
} }
@ -35,6 +35,7 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
} }
s = c; s = c;
} }
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -10,12 +10,12 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(format!("Error reading '{}'", filename))?; do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(()) Ok(())
} }
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().cherr(format!("func1 error"))?; func2().context(format!("func1 error"))?;
Ok(()) Ok(())
} }
@ -36,6 +36,7 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
eprintln!("The root cause was: std::io::Error: {:#?}", ioerror); eprintln!("The root cause was: std::io::Error: {:#?}", ioerror);
} }
} }
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -8,18 +8,18 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
Ok(()) Ok(())
} }
derive_str_cherr!(Func1Error); derive_str_context!(Func1Error);
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().cherr(Func1Error(format!("func1 error")))?; func2().context(Func1Error(format!("func1 error")))?;
Ok(()) Ok(())
} }
@ -36,6 +36,7 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
eprintln!("Debug Func2Error:\n{:?}", f2err); eprintln!("Debug Func2Error:\n{:?}", f2err);
} }
} }
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -8,21 +8,21 @@ fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Ok(()) Ok(())
} }
derive_str_cherr!(Func2Error); derive_str_context!(Func2Error);
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt"; let filename = "foo.txt";
do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
Ok(()) Ok(())
} }
derive_str_cherr!(Func1ErrorFunc2); derive_str_context!(Func1ErrorFunc2);
derive_str_cherr!(Func1ErrorIO); derive_str_context!(Func1ErrorIO);
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().cherr(Func1ErrorFunc2(format!("func1 error calling func2")))?; func2().context(Func1ErrorFunc2(format!("func1 error calling func2")))?;
let filename = "bar.txt"; let filename = "bar.txt";
do_some_io().cherr(Func1ErrorIO(format!("Error reading '{}'", filename)))?; do_some_io().context(Func1ErrorIO(format!("Error reading '{}'", filename)))?;
Ok(()) Ok(())
} }
@ -35,6 +35,7 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Some(s) = e.downcast_chain_ref::<Func1ErrorFunc2>() { if let Some(s) = e.downcast_chain_ref::<Func1ErrorFunc2>() {
eprintln!("Func1ErrorFunc2:\n{:?}", s); eprintln!("Func1ErrorFunc2:\n{:?}", s);
} }
std::process::exit(1);
} }
Ok(()) Ok(())
} }

View file

@ -12,18 +12,9 @@
//! //!
//! ## Features //! ## Features
//! //!
//! `default = [ "location", "debug-cause" ]`
//!
//! `location`
//! : store the error location
//!
//! `display-cause` //! `display-cause`
//! : turn on printing a backtrace of the errors in `Display` //! : turn on printing a backtrace of the errors in `Display`
//! //!
//! `debug-cause`
//! : print a backtrace of the errors in `Debug`
//!
//!
//! # Tutorial //! # Tutorial
//! //!
//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) //! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
@ -91,17 +82,16 @@
//! //!
//! fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { //! fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
//! let filename = "foo.txt"; //! let filename = "foo.txt";
//! do_some_io().cherr(format!("Error reading '{}'", filename))?; //! do_some_io().context(format!("Error reading '{}'", filename))?;
//! Ok(()) //! Ok(())
//! } //! }
//! //!
//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { //! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
//! func2().cherr("func1 error")?; //! func2().context("func1 error")?;
//! Ok(()) //! Ok(())
//! } //! }
//! //!
//! if let Err(e) = func1() { //! if let Err(e) = func1() {
//! # #[cfg(feature = "debug-cause")]
//! #[cfg(not(windows))] //! #[cfg(not(windows))]
//! assert_eq!( //! assert_eq!(
//! format!("\n{:?}\n", e), //! format!("\n{:?}\n", e),
@ -133,14 +123,14 @@
//! //!
//! fn func3() -> Result<(), Box<dyn Error + Send + Sync>> { //! fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
//! let filename = "foo.txt"; //! let filename = "foo.txt";
//! do_some_io().cherr(format!("Error reading '{}'", filename))?; //! do_some_io().context(format!("Error reading '{}'", filename))?;
//! Ok(()) //! Ok(())
//! } //! }
//! //!
//! derive_str_cherr!(Func2Error); //! derive_str_context!(Func2Error);
//! //!
//! fn func2() -> ChainResult<(), Func2Error> { //! fn func2() -> ChainResult<(), Func2Error> {
//! func3().cherr(Func2Error("func2 error: calling func3".into()))?; //! func3().context(Func2Error("func2 error: calling func3".into()))?;
//! Ok(()) //! Ok(())
//! } //! }
//! //!
@ -165,9 +155,9 @@
//! } //! }
//! //!
//! fn func1() -> ChainResult<(), Func1Error> { //! fn func1() -> ChainResult<(), Func1Error> {
//! func2().cherr(Func1Error::Func2)?; //! func2().context(Func1Error::Func2)?;
//! let filename = String::from("bar.txt"); //! let filename = String::from("bar.txt");
//! do_some_io().cherr(Func1Error::IO(filename))?; //! do_some_io().context(Func1Error::IO(filename))?;
//! Ok(()) //! Ok(())
//! } //! }
//! //!
@ -196,7 +186,6 @@
//! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error); //! eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error);
//! } //! }
//! //!
//! # #[cfg(feature = "no-debug-cause")]
//! #[cfg(not(windows))] //! #[cfg(not(windows))]
//! assert_eq!( //! assert_eq!(
//! format!("\n{:?}\n", e), //! format!("\n{:?}\n", e),
@ -229,15 +218,15 @@ pub mod prelude {
//! convenience prelude //! convenience prelude
pub mod v1 { pub mod v1 {
//! convenience prelude //! convenience prelude
pub use crate::ChainErrorDown as _; pub use super::super::ChainErrorDown as _;
pub use crate::ResultTrait as _; pub use super::super::ResultTrait as _;
pub use crate::{derive_err_kind, derive_str_cherr, ChainError, ChainResult}; 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 /// chains an inner error kind `T` with a causing error
pub struct ChainError<T> { pub struct ChainError<T> {
#[cfg(feature = "location")]
occurrence: Option<String>, occurrence: Option<String>,
kind: T, kind: T,
error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
@ -247,8 +236,7 @@ pub struct ChainError<T> {
pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>; pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>;
impl<T: 'static + Display + Debug> ChainError<T> { impl<T: 'static + Display + Debug> ChainError<T> {
#[cfg(feature = "location")] /// Use the `context()` or `map_context()` Result methods instead of calling this directly
/// Use the `cherr()` or `map_cherr()` Result methods instead of calling this directly
#[inline] #[inline]
pub fn new( pub fn new(
kind: T, kind: T,
@ -262,17 +250,6 @@ impl<T: 'static + Display + Debug> ChainError<T> {
} }
} }
#[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<Box<dyn Error + 'static + Send + Sync>>,
_occurrence: Option<String>,
) -> Self {
Self { kind, error_cause }
}
/// return the root cause of the error chain, if any exists /// 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 Error + 'static)> {
self.iter().last() self.iter().last()
@ -292,18 +269,18 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// Ok(()) /// Ok(())
/// } /// }
/// ///
/// derive_str_cherr!(Func2Error); /// derive_str_context!(Func2Error);
/// ///
/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
/// let filename = "foo.txt"; /// let filename = "foo.txt";
/// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; /// do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
/// Ok(()) /// Ok(())
/// } /// }
/// ///
/// derive_str_cherr!(Func1Error); /// derive_str_context!(Func1Error);
/// ///
/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
/// func2().cherr(Func1Error("func1 error".into()))?; /// func2().context(Func1Error("func1 error".into()))?;
/// Ok(()) /// Ok(())
/// } /// }
/// ///
@ -334,7 +311,7 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// ///
/// ```rust /// ```rust
/// # use chainerror::prelude::v1::*; /// # use chainerror::prelude::v1::*;
/// # derive_str_cherr!(FooError); /// # derive_str_context!(FooError);
/// # let err = ChainError::new(String::new(), None, None); /// # let err = ChainError::new(String::new(), None, None);
/// // Instead of writing /// // Instead of writing
/// err.find_cause::<ChainError<FooError>>(); /// err.find_cause::<ChainError<FooError>>();
@ -357,7 +334,7 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// ///
/// ```rust /// ```rust
/// # use chainerror::prelude::v1::*; /// # use chainerror::prelude::v1::*;
/// # derive_str_cherr!(FooErrorKind); /// # derive_str_context!(FooErrorKind);
/// # let err = ChainError::new(String::new(), None, None); /// # let err = ChainError::new(String::new(), None, None);
/// // Instead of writing /// // Instead of writing
/// err.find_cause::<ChainError<FooErrorKind>>(); /// err.find_cause::<ChainError<FooErrorKind>>();
@ -394,11 +371,11 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// Ok(()) /// Ok(())
/// } /// }
/// ///
/// derive_str_cherr!(Func2Error); /// derive_str_context!(Func2Error);
/// ///
/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
/// let filename = "foo.txt"; /// let filename = "foo.txt";
/// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; /// do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
/// Ok(()) /// Ok(())
/// } /// }
/// ///
@ -419,8 +396,8 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// # } /// # }
/// ///
/// fn func1() -> ChainResult<(), Func1ErrorKind> { /// fn func1() -> ChainResult<(), Func1ErrorKind> {
/// func2().cherr(Func1ErrorKind::Func2)?; /// func2().context(Func1ErrorKind::Func2)?;
/// do_some_io().cherr(Func1ErrorKind::IO("bar.txt".into()))?; /// do_some_io().context(Func1ErrorKind::IO("bar.txt".into()))?;
/// Ok(()) /// Ok(())
/// } /// }
/// ///
@ -453,10 +430,10 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// Convenience methods for `Result<>` to turn the error into a decorated ChainError /// Convenience methods for `Result<>` to turn the error into a decorated ChainError
pub trait ResultTrait<O, E: Into<Box<dyn Error + 'static + Send + Sync>>> { pub trait ResultTrait<O, E: Into<Box<dyn Error + 'static + Send + Sync>>> {
/// Decorate the error with a `kind` of type `T` and the source `Location` /// Decorate the error with a `kind` of type `T` and the source `Location`
fn cherr<T: 'static + Display + Debug>(self, kind: T) -> std::result::Result<O, ChainError<T>>; fn context<T: 'static + Display + Debug>(self, kind: T) -> std::result::Result<O, ChainError<T>>;
/// Decorate the error with a `kind` of type `T` produced with a `FnOnce` and the source `Location` /// Decorate the `error` with a `kind` of type `T` produced with a `FnOnce(&error)` and the source `Location`
fn map_cherr<T: 'static + Display + Debug, F: FnOnce(&E) -> T>( fn map_context<T: 'static + Display + Debug, F: FnOnce(&E) -> T>(
self, self,
op: F, op: F,
) -> std::result::Result<O, ChainError<T>>; ) -> std::result::Result<O, ChainError<T>>;
@ -466,7 +443,7 @@ impl<O, E: Into<Box<dyn Error + 'static + Send + Sync>>> ResultTrait<O, E>
for std::result::Result<O, E> for std::result::Result<O, E>
{ {
#[track_caller] #[track_caller]
fn cherr<T: 'static + Display + Debug>(self, kind: T) -> std::result::Result<O, ChainError<T>> { fn context<T: 'static + Display + Debug>(self, kind: T) -> std::result::Result<O, ChainError<T>> {
match self { match self {
Ok(t) => Ok(t), Ok(t) => Ok(t),
Err(error_cause) => Err(ChainError::new( Err(error_cause) => Err(ChainError::new(
@ -478,7 +455,7 @@ impl<O, E: Into<Box<dyn Error + 'static + Send + Sync>>> ResultTrait<O, E>
} }
#[track_caller] #[track_caller]
fn map_cherr<T: 'static + Display + Debug, F: FnOnce(&E) -> T>( fn map_context<T: 'static + Display + Debug, F: FnOnce(&E) -> T>(
self, self,
op: F, op: F,
) -> std::result::Result<O, ChainError<T>> { ) -> std::result::Result<O, ChainError<T>> {
@ -741,22 +718,16 @@ impl<T: 'static + Display + Debug> Debug for ChainError<T> {
if f.alternate() { if f.alternate() {
let mut f = f.debug_struct(&format!("ChainError<{}>", std::any::type_name::<T>())); let mut f = f.debug_struct(&format!("ChainError<{}>", std::any::type_name::<T>()));
#[cfg(feature = "location")] let f = f
let f = f.field("occurrence", &self.occurrence); .field("occurrence", &self.occurrence)
.field("kind", &self.kind)
let f = f.field("kind", &self.kind); .field("source", &self.source());
#[cfg(feature = "debug-cause")]
let f = f.field("source", &self.source());
f.finish() f.finish()
} else { } else {
#[cfg(feature = "location")]
{
if let Some(ref o) = self.occurrence { if let Some(ref o) = self.occurrence {
write!(f, "{}: ", o)?; write!(f, "{}: ", o)?;
} }
}
if TypeId::of::<String>() == TypeId::of::<T>() if TypeId::of::<String>() == TypeId::of::<T>()
|| TypeId::of::<&str>() == TypeId::of::<T>() || TypeId::of::<&str>() == TypeId::of::<T>()
@ -766,13 +737,10 @@ impl<T: 'static + Display + Debug> Debug for ChainError<T> {
Debug::fmt(&self.kind, f)?; Debug::fmt(&self.kind, f)?;
} }
#[cfg(feature = "debug-cause")]
{
if let Some(e) = self.source() { if let Some(e) = self.source() {
writeln!(f, "\nCaused by:")?; writeln!(f, "\nCaused by:")?;
Debug::fmt(&e, f)?; Debug::fmt(&e, f)?;
} }
}
Ok(()) Ok(())
} }
} }
@ -825,18 +793,18 @@ where
/// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Err(io::Error::from(io::ErrorKind::NotFound))?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// derive_str_cherr!(Func2Error); /// derive_str_context!(Func2Error);
/// ///
/// fn func2() -> ChainResult<(), Func2Error> { /// fn func2() -> ChainResult<(), Func2Error> {
/// let filename = "foo.txt"; /// let filename = "foo.txt";
/// do_some_io().cherr(Func2Error(format!("Error reading '{}'", filename)))?; /// do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
/// Ok(()) /// Ok(())
/// } /// }
/// ///
/// derive_str_cherr!(Func1Error); /// derive_str_context!(Func1Error);
/// ///
/// fn func1() -> Result<(), Box<dyn Error>> { /// fn func1() -> Result<(), Box<dyn Error>> {
/// func2().cherr(Func1Error("func1 error".into()))?; /// func2().context(Func1Error("func1 error".into()))?;
/// Ok(()) /// Ok(())
/// } /// }
/// # if let Err(e) = func1() { /// # if let Err(e) = func1() {
@ -851,7 +819,7 @@ where
/// # } /// # }
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! derive_str_cherr { macro_rules! derive_str_context {
($e:ident) => { ($e:ident) => {
#[derive(Clone)] #[derive(Clone)]
pub struct $e(pub String); pub struct $e(pub String);
@ -928,9 +896,9 @@ macro_rules! derive_str_cherr {
/// let filename = "bar.txt"; /// let filename = "bar.txt";
/// ///
/// do_some_io(filename) /// do_some_io(filename)
/// .map_cherr(|e| ErrorKind::from_io_error(e, filename.into()))?; /// .map_context(|e| ErrorKind::from_io_error(e, filename.into()))?;
/// do_some_io(filename).map_cherr(|e| ErrorKind::IO(filename.into()))?; /// do_some_io(filename).map_context(|e| ErrorKind::IO(filename.into()))?;
/// do_some_io(filename).map_cherr(|e| ErrorKind::from(e))?; /// do_some_io(filename).map_context(|e| ErrorKind::from(e))?;
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```

View file

@ -7,12 +7,12 @@ use std::io;
fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> { fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> {
use std::fmt::Write; use std::fmt::Write;
let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound));
let err = err.cherr("1"); let err = err.context("1");
let err = err.cherr("2"); let err = err.context("2");
let err = err.cherr("3"); let err = err.context("3");
let err = err.cherr("4"); let err = err.context("4");
let err = err.cherr("5"); let err = err.context("5");
let err = err.cherr("6"); let err = err.context("6");
let err = err.err().unwrap(); let err = err.err().unwrap();
let mut res = String::new(); let mut res = String::new();
@ -36,12 +36,12 @@ fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> {
#[test] #[test]
fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> { fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> {
let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound));
let err = err.cherr("1"); let err = err.context("1");
let err = err.cherr("2"); let err = err.context("2");
let err = err.cherr("3"); let err = err.context("3");
let err = err.cherr("4"); let err = err.context("4");
let err = err.cherr("5"); let err = err.context("5");
let err = err.cherr("6"); let err = err.context("6");
let err = err.err().unwrap(); let err = err.err().unwrap();
let res = err.to_string(); let res = err.to_string();
@ -61,12 +61,12 @@ fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> {
#[test] #[test]
fn test_find_cause() -> Result<(), Box<dyn Error + Send + Sync>> { fn test_find_cause() -> Result<(), Box<dyn Error + Send + Sync>> {
let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound));
let err = err.cherr("1"); let err = err.context("1");
let err = err.cherr("2"); let err = err.context("2");
let err = err.cherr("3"); let err = err.context("3");
let err = err.cherr("4"); let err = err.context("4");
let err = err.cherr("5"); let err = err.context("5");
let err = err.cherr("6"); let err = err.context("6");
let err = err.err().unwrap(); let err = err.err().unwrap();
let io_error: Option<&io::Error> = err.find_cause::<io::Error>(); let io_error: Option<&io::Error> = err.find_cause::<io::Error>();
@ -79,12 +79,12 @@ fn test_find_cause() -> Result<(), Box<dyn Error + Send + Sync>> {
#[test] #[test]
fn test_root_cause() -> Result<(), Box<dyn Error + Send + Sync>> { fn test_root_cause() -> Result<(), Box<dyn Error + Send + Sync>> {
let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound)); let err: Result<(), _> = Err(io::Error::from(io::ErrorKind::NotFound));
let err = err.cherr("1"); let err = err.context("1");
let err = err.cherr("2"); let err = err.context("2");
let err = err.cherr("3"); let err = err.context("3");
let err = err.cherr("4"); let err = err.context("4");
let err = err.cherr("5"); let err = err.context("5");
let err = err.cherr("6"); let err = err.context("6");
let err = err.err().unwrap(); let err = err.err().unwrap();
let err: Option<&(dyn std::error::Error + 'static)> = err.root_cause(); let err: Option<&(dyn std::error::Error + 'static)> = err.root_cause();