mirror of
https://github.com/haraldh/chainerror.git
synced 2025-01-30 16:46:42 +01:00
commit
25ebe8333f
77
README.md
77
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<dyn std::error::Error + Send + Sync>;
|
||||
|
@ -90,6 +90,81 @@ Along with the `Error<T>` 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<example::Func1Error> {
|
||||
occurrence: Some(
|
||||
"examples/example.rs:50:13",
|
||||
),
|
||||
kind: func1 error calling func2,
|
||||
source: Some(
|
||||
Error<example::Func2Error> {
|
||||
occurrence: Some(
|
||||
"examples/example.rs:25:13",
|
||||
),
|
||||
kind: Func2Error(func2 error: calling func3),
|
||||
source: Some(
|
||||
Error<chainerror::AnnotatedError> {
|
||||
occurrence: Some(
|
||||
"examples/example.rs:18:13",
|
||||
),
|
||||
kind: (passed error),
|
||||
source: Some(
|
||||
Error<alloc::string::String> {
|
||||
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)
|
||||
|
|
24
README.tpl
24
README.tpl
|
@ -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.
|
1
booksrc/LICENSE-APACHE
Symbolic link
1
booksrc/LICENSE-APACHE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../LICENSE-APACHE
|
1
booksrc/LICENSE-MIT
Symbolic link
1
booksrc/LICENSE-MIT
Symbolic link
|
@ -0,0 +1 @@
|
|||
../LICENSE-MIT
|
|
@ -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<Error + Send + Sync>>` and we can
|
||||
use `ChainResult<(), Func1ErrorKind>`.
|
||||
|
||||
In `main` we can now directly use the methods of `ChainError<T>` without downcasting the error first.
|
||||
In `main` we can now directly use the methods of `chainerror::Error<T>` without downcasting the error first.
|
||||
|
||||
Also a nice `match` on `ChainError<T>.kind()` is now possible, which returns `&T`, meaning
|
||||
`&Func1ErrorKind` here.
|
||||
Also, a nice `match` on `chainerror::Error<T>.kind()` is now possible, which returns `&T`, meaning `&Func1ErrorKind` here.
|
||||
|
||||
~~~rust
|
||||
{{#include ../examples/tutorial10.rs}}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Deref for the ErrorKind
|
||||
|
||||
Because ChainError<T> implements Deref to &T, we can also match on `*e` instead of `e.kind()`
|
||||
Because chainerror::Error<T> 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}}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Writing a library
|
||||
|
||||
I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `ChainError<mycrate::ErrorKind>`
|
||||
I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `chainerror::Error<mycrate::ErrorKind>`
|
||||
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
|
||||
|
|
|
@ -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<Error + Send + Sync>)` as a result.
|
||||
|
||||
The `Debug` implementation of `ChainError<T>` (which is returned by `context()`)
|
||||
The `Debug` implementation of `chainerror::Error<T>` (which is returned by `context()`)
|
||||
prints the `Debug` of `T` prefixed with the stored filename and line number.
|
||||
|
||||
`ChainError<T>` in our case is `ChainError<&str>`.
|
||||
`chainerror::Error<T>` in our case is `chainerror::Error<&str>`.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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`.
|
||||
|
||||
To use the `Display` backtrace, you have to use the alternative display format output `{:#}`.
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
|
||||
~~~rust,ignore
|
||||
fn is_chain<T: 'static + Display + Debug>(&self) -> bool
|
||||
fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>
|
||||
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>
|
||||
fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&chainerror::Error<T>>
|
||||
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut chainerror::Error<T>>
|
||||
fn root_cause(&self) -> Option<&(dyn Error + 'static)>
|
||||
fn find_cause<U: Error + 'static>(&self) -> Option<&U>
|
||||
fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>>
|
||||
fn find_chain_cause<U: Error + 'static>(&self) -> Option<&chainerror::Error<U>>
|
||||
fn kind<'a>(&'a self) -> &'a T
|
||||
~~~
|
||||
|
||||
Using `downcast_chain_ref::<String>()` gives a `ChainError<String>`, which can be used
|
||||
Using `downcast_chain_ref::<String>()` gives a `chainerror::Error<String>`, which can be used
|
||||
to call `.find_cause::<io::Error>()`.
|
||||
|
||||
~~~rust,ignore
|
||||
|
|
|
@ -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<String>` we now have `struct Func1Error(String)` and `ChainError<Func1Error>`.
|
||||
Instead of `chainerror::Error<String>` we now have `struct Func1Error(String)` and `chainerror::Error<Func1Error>`.
|
||||
|
||||
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::<ChainError<Func2Error>>() {
|
||||
if let Some(f2err) = f1err.find_cause::<chainerror::Error<Func2Error>>() {
|
||||
~~~
|
||||
hiding the `ChainError<T>` implementation detail.
|
||||
hiding the `chainerror::Error<T>` implementation detail.
|
||||
|
||||
~~~rust
|
||||
{{#include ../examples/tutorial8.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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
fn func4() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let filename = "foo.txt";
|
||||
do_some_io().context(format!("Error reading '{}'", filename))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func2Error);
|
||||
fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
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))?;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::result::Result;
|
||||
|
||||
fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func2Error);
|
||||
chainerror::str_context!(Func2Error);
|
||||
|
||||
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
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))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func2Error);
|
||||
chainerror::str_context!(Func2Error);
|
||||
|
||||
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
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))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func2Error);
|
||||
chainerror::str_context!(Func2Error);
|
||||
|
||||
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
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))?;
|
||||
|
|
|
@ -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<dyn std::error::Error + Send + Sync>> {
|
||||
|
@ -10,7 +11,7 @@ pub mod mycrate {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func2Error);
|
||||
chainerror::str_context!(Func2Error);
|
||||
|
||||
fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
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<T> = std::result::Result<T, Error>;
|
||||
|
||||
|
|
|
@ -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<dyn std::error::Error + Send + Sync>> {
|
||||
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<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl std::fmt::Display for ErrorKind {
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func2Error);
|
||||
chainerror::str_context!(Func2Error);
|
||||
|
||||
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let filename = "foo.txt";
|
||||
|
@ -16,10 +16,10 @@ fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func1Error);
|
||||
chainerror::str_context!(Func1Error);
|
||||
|
||||
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
func2().context(Func1Error("func1 error".to_string()))?;
|
||||
func2().context(Func1Error::new("func1 error"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
|||
if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
|
||||
eprintln!("Func1Error: {}", f1err);
|
||||
|
||||
if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() {
|
||||
if let Some(f2err) = f1err.find_cause::<chainerror::Error<Func2Error>>() {
|
||||
eprintln!("Func2Error: {}", f2err);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func2Error);
|
||||
chainerror::str_context!(Func2Error);
|
||||
|
||||
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let filename = "foo.txt";
|
||||
|
@ -16,11 +16,11 @@ fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func1ErrorFunc2);
|
||||
derive_str_context!(Func1ErrorIO);
|
||||
chainerror::str_context!(Func1ErrorFunc2);
|
||||
chainerror::str_context!(Func1ErrorIO);
|
||||
|
||||
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
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<dyn Error + Send + Sync>> {
|
|||
|
||||
fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
if let Err(e) = func1() {
|
||||
if let Some(s) = e.downcast_ref::<ChainError<Func1ErrorIO>>() {
|
||||
if let Some(s) = e.downcast_ref::<chainerror::Error<Func1ErrorIO>>() {
|
||||
eprintln!("Func1ErrorIO:\n{:?}", s);
|
||||
}
|
||||
|
||||
|
|
116
src/lib.rs
116
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<T> {
|
||||
occurrence: Option<String>,
|
||||
|
@ -55,7 +43,8 @@ impl<T: 'static + Display + Debug> Error<T> {
|
|||
/// # 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<T: 'static + Display + Debug> Error<T> {
|
|||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// derive_str_context!(Func2Error);
|
||||
/// chainerror::str_context!(Func2Error);
|
||||
///
|
||||
/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
/// let filename = "foo.txt";
|
||||
|
@ -72,10 +61,10 @@ impl<T: 'static + Display + Debug> Error<T> {
|
|||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// derive_str_context!(Func1Error);
|
||||
/// chainerror::str_context!(Func1Error);
|
||||
///
|
||||
/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
/// func2().context(Func1Error("func1 error".into()))?;
|
||||
/// func2().context(Func1Error::new("func1 error"))?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
|
@ -107,13 +96,12 @@ impl<T: 'static + Display + Debug> Error<T> {
|
|||
/// # 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::<ChainError<FooError>>();
|
||||
/// err.find_cause::<chainerror::Error<FooError>>();
|
||||
///
|
||||
/// // leave out the ChainError<FooError> implementation detail
|
||||
/// // leave out the chainerror::Error<FooError> implementation detail
|
||||
/// err.find_chain_cause::<FooError>();
|
||||
/// ```
|
||||
#[inline]
|
||||
|
@ -130,17 +118,16 @@ impl<T: 'static + Display + Debug> Error<T> {
|
|||
/// # 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::<ChainError<FooErrorKind>>();
|
||||
/// err.find_cause::<chainerror::Error<FooErrorKind>>();
|
||||
/// // and/or
|
||||
/// err.find_chain_cause::<FooErrorKind>();
|
||||
/// // and/or
|
||||
/// err.find_cause::<FooErrorKind>();
|
||||
///
|
||||
/// // leave out the ChainError<FooErrorKind> implementation detail
|
||||
/// // leave out the chainerror::Error<FooErrorKind> implementation detail
|
||||
/// err.find_kind_or_cause::<FooErrorKind>();
|
||||
/// ```
|
||||
#[inline]
|
||||
|
@ -159,7 +146,7 @@ impl<T: 'static + Display + Debug> Error<T> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use chainerror::prelude::v1::*;
|
||||
/// use chainerror::Context as _;
|
||||
/// use std::error::Error;
|
||||
/// use std::io;
|
||||
///
|
||||
|
@ -168,7 +155,7 @@ impl<T: 'static + Display + Debug> Error<T> {
|
|||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// derive_str_context!(Func2Error);
|
||||
/// chainerror::str_context!(Func2Error);
|
||||
///
|
||||
/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
/// let filename = "foo.txt";
|
||||
|
@ -192,7 +179,7 @@ impl<T: 'static + Display + Debug> Error<T> {
|
|||
/// # }
|
||||
/// # }
|
||||
///
|
||||
/// 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<T: 'static + Display + Debug> Error<T> {
|
|||
}
|
||||
|
||||
/// Convenience methods for `Result<>` to turn the error into a decorated [`Error`](Error)
|
||||
pub trait ResultTrait<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> {
|
||||
pub trait Context<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> {
|
||||
/// Decorate the error with a `kind` of type `T` and the source `Location`
|
||||
fn context<T: 'static + Display + Debug>(self, kind: T) -> std::result::Result<O, Error<T>>;
|
||||
|
||||
/// Decorate the error just with the source `Location`
|
||||
fn annotate(self) -> std::result::Result<O, Error<AnnotatedError>>;
|
||||
|
||||
/// Decorate the `error` with a `kind` of type `T` produced with a `FnOnce(&error)` and the source `Location`
|
||||
fn map_context<T: 'static + Display + Debug, F: FnOnce(&E) -> T>(
|
||||
self,
|
||||
|
@ -236,7 +226,22 @@ pub trait ResultTrait<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> {
|
|||
) -> std::result::Result<O, Error<T>>;
|
||||
}
|
||||
|
||||
impl<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> ResultTrait<O, E>
|
||||
/// 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<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> Context<O, E>
|
||||
for std::result::Result<O, E>
|
||||
{
|
||||
#[track_caller]
|
||||
|
@ -252,6 +257,19 @@ impl<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> ResultTrait<O, E>
|
|||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
fn annotate(self) -> std::result::Result<O, Error<AnnotatedError>> {
|
||||
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: 'static + Display + Debug, F: FnOnce(&E) -> T>(
|
||||
|
@ -298,7 +316,7 @@ impl<T: 'static + Display + Debug> std::ops::Deref for Error<T> {
|
|||
}
|
||||
|
||||
/// Convenience trait to hide the [`Error<T>`](Error) implementation internals
|
||||
pub trait ChainErrorDown {
|
||||
pub trait ErrorDown {
|
||||
/// Test if of type `Error<T>`
|
||||
fn is_chain<T: 'static + Display + Debug>(&self) -> bool;
|
||||
/// Downcast to a reference of `Error<T>`
|
||||
|
@ -311,7 +329,7 @@ pub trait ChainErrorDown {
|
|||
fn downcast_inner_mut<T: 'static + StdError>(&mut self) -> Option<&mut T>;
|
||||
}
|
||||
|
||||
impl<U: 'static + Display + Debug> ChainErrorDown for Error<U> {
|
||||
impl<U: 'static + Display + Debug> ErrorDown for Error<U> {
|
||||
#[inline]
|
||||
fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
|
||||
TypeId::of::<T>() == TypeId::of::<U>()
|
||||
|
@ -369,7 +387,7 @@ impl<U: 'static + Display + Debug> ChainErrorDown for Error<U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl ChainErrorDown for dyn StdError + 'static {
|
||||
impl ErrorDown for dyn StdError + 'static {
|
||||
#[inline]
|
||||
fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
|
||||
self.is::<Error<T>>()
|
||||
|
@ -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<T: 'static + Display + Debug>(&self) -> bool {
|
||||
self.is::<Error<T>>()
|
||||
|
@ -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<T: 'static + Display + Debug>(&self) -> bool {
|
||||
self.is::<Error<T>>()
|
||||
|
@ -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<dyn Error>> {
|
||||
/// 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::<Func1Error>() {
|
||||
/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
|
||||
/// # assert!(f1err.find_cause::<chainerror::Error<Func2Error>>().is_some());
|
||||
/// # assert!(f1err.find_chain_cause::<Func2Error>().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: Into<String>>(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>);
|
||||
|
||||
|
|
|
@ -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::<Vec<_>>();
|
||||
assert_eq!(lines.len(), 5);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use chainerror::prelude::v1::*;
|
||||
use chainerror::Context;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
||||
|
|
Loading…
Reference in a new issue