Compare commits

..

No commits in common. "master" and "v0.6.0" have entirely different histories.

37 changed files with 767 additions and 801 deletions

View file

@ -1,7 +0,0 @@
version: 2
updates:
- package-ecosystem: cargo
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10

View file

@ -1,38 +1,22 @@
name: coverage
on:
# Trigger the workflow on push or pull request,
# but only for the master branch
push:
branches:
- master
pull_request:
branches:
- master
release:
types:
- created
on: [ "push" , "pull_request" ]
jobs:
coverage:
test:
name: coverage
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
container:
image: xd009642/tarpaulin
options: --security-opt seccomp=unconfined
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
target: x86_64-unknown-linux-gnu
toolchain: nightly
components: llvm-tools-preview
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Checkout repository
uses: actions/checkout@v2
- name: Generate code coverage
run: cargo +nightly llvm-cov --all-features --workspace --codecov --doctests --output-path codecov.json
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
run: |
cargo tarpaulin --verbose --workspace --timeout 120 --out Lcov --output-dir coverage
- name: Upload to coveralls
uses: coverallsapp/github-action@master
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
files: codecov.json
fail_ci_if_error: true
github-token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -7,10 +7,31 @@ on:
jobs:
deploy:
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set CURRENT_TWO_WEEKS for use in cache keys
run: echo "::set-env name=CURRENT_TWO_WEEKS::$(($(date +%V) / 2))"
- name: Cache cargo registry
uses: actions/cache@v1
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ env.CURRENT_TWO_WEEKS }}
- name: Cache cargo index
uses: actions/cache@v1
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ env.CURRENT_TWO_WEEKS }}
- name: Cache mdbook binary
uses: actions/cache@v1
with:
path: ~/.cargo/bin/mdbook
key: ${{ runner.os }}-cargo-mdbook-${{ env.CURRENT_TWO_WEEKS }}
- name: Build mdbook
run: cargo install mdbook

View file

@ -19,16 +19,17 @@ jobs:
strategy:
matrix:
version:
- 1.54.0
- 1.46.0
- stable
- beta
- nightly
steps:
- uses: actions/checkout@v1
- name: Install toolchain
uses: dtolnay/rust-toolchain@master
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.version }}
default: true
profile: minimal
- name: Build
run: cargo build --verbose
@ -44,7 +45,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@master
- uses: actions-rs/toolchain@v1
with:
components: rustfmt
toolchain: stable
@ -60,7 +61,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@master
- uses: actions-rs/toolchain@v1
with:
components: clippy
toolchain: stable
@ -70,3 +71,16 @@ jobs:
with:
command: clippy
args: -- -D warnings
readme:
name: cargo readme
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- run: cargo install cargo-readme
- run: cargo readme > README.md && git diff --exit-code

View file

@ -1,9 +1,9 @@
[package]
name = "chainerror"
version = "1.0.0"
version = "0.6.0"
authors = ["Harald Hoyer <harald@redhat.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
license = "MIT/Apache-2.0"
documentation = "https://docs.rs/chainerror"
homepage = "https://haraldh.github.io/chainerror/"
repository = "https://github.com/haraldh/chainerror"
@ -22,5 +22,6 @@ maintenance = { status = "actively-developed" }
is-it-maintained-issue-resolution = { repository = "haraldh/chainerror" }
is-it-maintained-open-issues = { repository = "haraldh/chainerror" }
[package.metadata.docs.rs]
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
[features]
default = []
display-cause = []

291
README.md
View file

@ -1,6 +1,9 @@
[![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://codecov.io/gh/haraldh/chainerror/branch/master/graph/badge.svg?token=HGLJFGA11B)](https://codecov.io/gh/haraldh/chainerror)
[![Coverage Status](https://coveralls.io/repos/github/haraldh/chainerror/badge.svg?branch=master)](https://coveralls.io/github/haraldh/chainerror?branch=master)
[![Workflow Status](https://github.com/haraldh/chainerror/workflows/Rust/badge.svg)](https://github.com/haraldh/chainerror/actions?query=workflow%3A%22Rust%22)
[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/haraldh/chainerror.svg)](https://isitmaintained.com/project/haraldh/chainerror "Average time to resolve an issue")
[![Percentage of issues still open](https://isitmaintained.com/badge/open/haraldh/chainerror.svg)](https://isitmaintained.com/project/haraldh/chainerror "Percentage of issues still open")
![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg)
# chainerror
@ -8,149 +11,67 @@
`chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your
binaries, you still have the error backtrace.
Having nested function returning errors, the output doesn't tell where the error originates from.
```rust
use std::path::PathBuf;
type BoxedError = Box<dyn std::error::Error + Send + Sync>;
fn read_config_file(path: PathBuf) -> Result<(), BoxedError> {
// do stuff, return other errors
let _buf = std::fs::read_to_string(&path)?;
// do stuff, return other errors
Ok(())
}
fn process_config_file() -> Result<(), BoxedError> {
// do stuff, return other errors
let _buf = read_config_file("foo.txt".into())?;
// do stuff, return other errors
Ok(())
}
fn main() {
if let Err(e) = process_config_file() {
eprintln!("Error:\n{:?}", e);
}
}
```
This gives the output:
```console
Error:
Os { code: 2, kind: NotFound, message: "No such file or directory" }
```
and you have no idea where it comes from.
With `chainerror`, you can supply a context and get a nice error backtrace:
```rust
use chainerror::Context as _;
use std::path::PathBuf;
type BoxedError = Box<dyn std::error::Error + Send + Sync>;
fn read_config_file(path: PathBuf) -> Result<(), BoxedError> {
// do stuff, return other errors
let _buf = std::fs::read_to_string(&path).context(format!("Reading file: {:?}", &path))?;
// do stuff, return other errors
Ok(())
}
fn process_config_file() -> Result<(), BoxedError> {
// do stuff, return other errors
let _buf = read_config_file("foo.txt".into()).context("read the config file")?;
// do stuff, return other errors
Ok(())
}
fn main() {
if let Err(e) = process_config_file() {
eprintln!("Error:\n{:?}", e);
}
}
```
with the output:
```console
Error:
examples/simple.rs:14:51: read the config file
Caused by:
examples/simple.rs:7:47: Reading file: "foo.txt"
Caused by:
Os { code: 2, kind: NotFound, message: "No such file or directory" }
```
`chainerror` has no dependencies!
`chainerror` uses `.source()` of `std::error::Error` along with `#[track_caller]` and `Location` to provide a nice debug error backtrace.
It encapsulates all types, which have `Display + Debug` and can store the error cause internally.
Along with the `Error<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing.
`chainerror` has no dependencies!
Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing.
Debug information is worth it!
## Multiple Output Formats
### Features
`chainerror` supports multiple output formats, which can be selected with the different format specifiers:
`display-cause`
: turn on printing a backtrace of the errors in `Display`
* `{}`: Display
```console
func1 error calling func2
## Tutorial
Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
## Examples
examples/example.rs:
```rust
// […]
fn main() {
if let Err(e) = func1() {
eprintln!("\nDebug Error {{:?}}:\n{:?}", e);
eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", e);
// […]
}
}
```
* `{:#}`: Alternative Display
```console
func1 error calling func2
$ cargo run -q --example example
Debug Error {:?}:
examples/example.rs:46:13: func1 error calling func2
Caused by:
func2 error: calling func3
examples/example.rs:21:13: Func2Error(func2 error: calling func3)
Caused by:
(passed error)
Caused by:
Error reading 'foo.txt'
Caused by:
entity not found
```
* `{:?}`: Debug
```console
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'
examples/example.rs:14:18: Error reading 'foo.txt'
Caused by:
Kind(NotFound)
```
* `{:#?}`: Alternative Debug
```console
Error<example::Func1Error> {
Alternative Debug Error {:#?}:
ChainError<example::Func1Error> {
occurrence: Some(
"examples/example.rs:50:13",
"examples/example.rs:46:13",
),
kind: func1 error calling func2,
source: Some(
Error<example::Func2Error> {
ChainError<example::Func2Error> {
occurrence: Some(
"examples/example.rs:25:13",
"examples/example.rs:21:13",
),
kind: Func2Error(func2 error: calling func3),
source: Some(
Error<chainerror::AnnotatedError> {
ChainError<alloc::string::String> {
occurrence: Some(
"examples/example.rs:18:13",
"examples/example.rs:14:18",
),
kind: (passed error),
source: Some(
Error<alloc::string::String> {
occurrence: Some(
"examples/example.rs:13:18",
),
kind: "Error reading 'foo.txt'",
kind: "Error reading \'foo.txt\'",
source: Some(
Kind(
NotFound,
@ -160,21 +81,145 @@ Error<example::Func1Error> {
),
},
),
},
),
}
```
## Tutorial
```rust
use chainerror::prelude::v1::*;
use std::error::Error;
use std::io;
use std::result::Result;
Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt";
do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(())
}
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().context("func1 error")?;
Ok(())
}
if let Err(e) = func1() {
#[cfg(not(windows))]
assert_eq!(
format!("\n{:?}\n", e),
r#"
src/lib.rs:21:13: func1 error
Caused by:
src/lib.rs:16:18: Error reading 'foo.txt'
Caused by:
Kind(NotFound)
"#
);
}
```
```rust
use chainerror::prelude::v1::*;
use std::error::Error;
use std::io;
use std::result::Result;
fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt";
do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(())
}
derive_str_context!(Func2Error);
fn func2() -> ChainResult<(), Func2Error> {
func3().context(Func2Error("func2 error: calling func3".into()))?;
Ok(())
}
enum Func1Error {
Func2,
IO(String),
}
impl ::std::fmt::Display for Func1Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
Func1Error::Func2 => write!(f, "func1 error calling func2"),
Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename),
}
}
}
impl ::std::fmt::Debug for Func1Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self)
}
}
fn func1() -> ChainResult<(), Func1Error> {
func2().context(Func1Error::Func2)?;
let filename = String::from("bar.txt");
do_some_io().context(Func1Error::IO(filename))?;
Ok(())
}
if let Err(e) = func1() {
assert!(match e.kind() {
Func1Error::Func2 => {
eprintln!("Main Error Report: func1 error calling func2");
true
}
Func1Error::IO(filename) => {
eprintln!("Main Error Report: func1 error reading '{}'", filename);
false
}
});
assert!(e.find_chain_cause::<Func2Error>().is_some());
if let Some(e) = e.find_chain_cause::<Func2Error>() {
eprintln!("\nError reported by Func2Error: {}", e)
}
assert!(e.root_cause().is_some());
if let Some(e) = e.root_cause() {
let io_error = e.downcast_ref::<io::Error>().unwrap();
eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error);
}
#[cfg(not(windows))]
assert_eq!(
format!("\n{:?}\n", e),
r#"
src/lib.rs:48:13: func1 error calling func2
Caused by:
src/lib.rs:23:13: Func2Error(func2 error: calling func3)
Caused by:
src/lib.rs:16:18: Error reading 'foo.txt'
Caused by:
Kind(NotFound)
"#
);
}
```
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
at your option.

24
README.tpl Normal file
View file

@ -0,0 +1,24 @@
[![Crate](https://img.shields.io/crates/v/chainerror.svg)](https://crates.io/crates/chainerror)
[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/chainerror/)
[![Coverage Status](https://coveralls.io/repos/github/haraldh/chainerror/badge.svg?branch=master)](https://coveralls.io/github/haraldh/chainerror?branch=master)
{{badges}}
# {{crate}}
{{readme}}
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

View file

@ -1 +0,0 @@
../LICENSE-APACHE

View file

@ -1 +0,0 @@
../LICENSE-MIT

View file

@ -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,9 +8,10 @@ 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::Error<T>` without downcasting the error first.
In `main` we can now directly use the methods of `ChainError<T>` without downcasting the error first.
Also, a nice `match` on `chainerror::Error<T>.kind()` is now possible, which returns `&T`, meaning `&Func1ErrorKind` here.
Also a nice `match` on `ChainError<T>.kind()` is now possible, which returns `&T`, meaning
`&Func1ErrorKind` here.
~~~rust
{{#include ../examples/tutorial10.rs}}

View file

@ -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

View file

@ -1,6 +1,6 @@
# Deref for the ErrorKind
Because chainerror::Error<T> implements Deref to &T, we can also match on `*e` instead of `e.kind()`
Because ChainError<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}}

View file

@ -1,6 +1,6 @@
# Writing a library
I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `chainerror::Error<mycrate::ErrorKind>`
I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `ChainError<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

View file

@ -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::Error<T>` (which is returned by `context()`)
The `Debug` implementation of `ChainError<T>` (which is returned by `context()`)
prints the `Debug` of `T` prefixed with the stored filename and line number.
`chainerror::Error<T>` in our case is `chainerror::Error<&str>`.
`ChainError<T>` in our case is `ChainError<&str>`.

View file

@ -14,13 +14,13 @@ If you compare the output to the previous example, you will see,
that:
~~~
Error: examples/tutorial2.rs:20:16: func1 error
Error: src/main.rs:19: "func1 error"
~~~
changed to just:
~~~
examples/tutorial3.rs:17:13: func1 error
src/main.rs:16: "func1 error"
~~~
This is, because we caught the error of `func1()` in `main()` and print it out ourselves.

View file

@ -14,4 +14,5 @@ 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 use the `Display` backtrace, you have to use the alternative display format output `{:#}`.
To enable the `Display` backtrace, you have to enable the feature `display-cause` for `chainerror`.

View file

@ -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::Error<T>>
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut chainerror::Error<T>>
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 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::Error<U>>
fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>>
fn kind<'a>(&'a self) -> &'a T
~~~
Using `downcast_chain_ref::<String>()` gives a `chainerror::Error<String>`, which can be used
Using `downcast_chain_ref::<String>()` gives a `ChainError<String>`, which can be used
to call `.find_cause::<io::Error>()`.
~~~rust,ignore

View file

@ -1,14 +1,14 @@
# Finding an Error cause
To distinguish the errors occurring in various places, we can define named string errors with the
To distinguish the errors occuring in various places, we can define named string errors with the
"new type" pattern.
~~~rust,ignore
chainerror::str_context!(Func2Error);
chainerror::str_context!(Func1Error);
derive_str_context!(Func2Error);
derive_str_context!(Func1Error);
~~~
Instead of `chainerror::Error<String>` we now have `struct Func1Error(String)` and `chainerror::Error<Func1Error>`.
Instead of `ChainError<String>` we now have `struct Func1Error(String)` and `ChainError<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::Error<Func2Error>>() {
if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() {
~~~
hiding the `chainerror::Error<T>` implementation detail.
hiding the `ChainError<T>` implementation detail.
~~~rust
{{#include ../examples/tutorial8.rs}}

View file

@ -1,28 +1,24 @@
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 func4() -> Result<(), Box<dyn Error + Send + Sync>> {
fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt";
do_some_io().context(format!("Error reading '{}'", filename))?;
Ok(())
}
fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
func4().annotate()?;
Ok(())
}
derive_str_context!(Func2Error);
chainerror::str_context!(Func2Error);
fn func2() -> chainerror::Result<(), Func2Error> {
func3().context(Func2Error::new("func2 error: calling func3"))?;
fn func2() -> ChainResult<(), Func2Error> {
func3().context(Func2Error(format!("func2 error: calling func3")))?;
Ok(())
}
@ -31,8 +27,8 @@ enum Func1Error {
IO(String),
}
impl fmt::Display for Func1Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl ::std::fmt::Display for Func1Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
Func1Error::Func2 => write!(f, "func1 error calling func2"),
Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename),
@ -40,13 +36,13 @@ impl fmt::Display for Func1Error {
}
}
impl fmt::Debug for Func1Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl ::std::fmt::Debug for Func1Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self)
}
}
fn func1() -> chainerror::Result<(), Func1Error> {
fn func1() -> ChainResult<(), Func1Error> {
func2().context(Func1Error::Func2)?;
let filename = String::from("bar.txt");
do_some_io().context(Func1Error::IO(filename))?;
@ -55,10 +51,6 @@ fn func1() -> chainerror::Result<(), Func1Error> {
fn main() {
if let Err(e) = func1() {
eprintln!("\nDisplay Error {{}}:\n{}", e);
eprintln!("\nAlternative Display Error {{:#}}:\n{:#}", e);
eprintln!("\nDebug Error {{:?}}:\n{:?}", e);
eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", e);

View file

@ -1,8 +1,6 @@
#![allow(clippy::single_match)]
#![allow(clippy::redundant_pattern_matching)]
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))?;

View file

@ -1,14 +1,14 @@
use chainerror::Context as _;
use chainerror::prelude::v1::*;
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(())
}
chainerror::str_context!(Func2Error);
derive_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() -> chainerror::Result<(), Func1ErrorKind> {
fn func1() -> ChainResult<(), Func1ErrorKind> {
func2().context(Func1ErrorKind::Func2)?;
let filename = String::from("bar.txt");
do_some_io().context(Func1ErrorKind::IO(filename))?;

View file

@ -1,14 +1,14 @@
use chainerror::Context as _;
use chainerror::prelude::v1::*;
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(())
}
chainerror::str_context!(Func2Error);
derive_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() -> chainerror::Result<(), Func1ErrorKind> {
fn func1() -> ChainResult<(), Func1ErrorKind> {
func2().context(Func1ErrorKind::Func2)?;
let filename = String::from("bar.txt");
do_some_io().context(Func1ErrorKind::IO(filename))?;

View file

@ -1,14 +1,14 @@
use chainerror::Context as _;
use chainerror::prelude::v1::*;
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(())
}
chainerror::str_context!(Func2Error);
derive_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() -> chainerror::Result<(), Func1ErrorKind> {
fn func1() -> ChainResult<(), Func1ErrorKind> {
func2().context(Func1ErrorKind::Func2)?;
let filename = String::from("bar.txt");
do_some_io().context(Func1ErrorKind::IO(filename))?;

View file

@ -1,9 +1,5 @@
#![allow(clippy::single_match)]
#![allow(clippy::redundant_pattern_matching)]
pub mod mycrate {
use chainerror::Context as _;
use chainerror::prelude::v1::*;
use std::io;
fn do_some_io() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
@ -11,7 +7,7 @@ pub mod mycrate {
Ok(())
}
chainerror::str_context!(Func2Error);
derive_str_context!(Func2Error);
fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filename = "foo.txt";
@ -25,7 +21,7 @@ pub mod mycrate {
IO(String),
}
chainerror::err_kind!(Error, ErrorKind);
derive_err_kind!(Error, ErrorKind);
pub type Result<T> = std::result::Result<T, Error>;

View file

@ -1,6 +1,3 @@
#![allow(clippy::single_match)]
#![allow(clippy::redundant_pattern_matching)]
pub mod mycrate {
use std::error::Error as StdError;

View file

@ -1,9 +1,5 @@
#![allow(clippy::single_match)]
#![allow(clippy::redundant_pattern_matching)]
pub mod mycrate {
use chainerror::{Context as _, ErrorDown as _};
use chainerror::prelude::v1::*;
use std::io;
fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
@ -11,7 +7,7 @@ pub mod mycrate {
Ok(())
}
chainerror::str_context!(Func2Error);
derive_str_context!(Func2Error);
fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filename = "foo.txt";
@ -27,7 +23,7 @@ pub mod mycrate {
Unknown,
}
chainerror::err_kind!(Error, ErrorKind);
derive_err_kind!(Error, ErrorKind);
pub type Result<T> = std::result::Result<T, Error>;
impl std::fmt::Display for ErrorKind {
@ -88,7 +84,7 @@ pub mod mycrate {
let filename = "bar.txt";
do_some_io(filename).map_context(|e| ErrorKind::from_io_error(e, filename.into()))?;
do_some_io(filename).map_context(|e| ErrorKind::from_io_error(&e, filename.into()))?;
do_some_io(filename).map_context(|_| ErrorKind::IO(filename.into()))?;
do_some_io(filename).map_context(|e| ErrorKind::from(e))?;

View file

@ -1,7 +1,8 @@
use chainerror::Context as _;
use chainerror::prelude::v1::*;
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))?;

View file

@ -1,7 +1,8 @@
use chainerror::Context as _;
use chainerror::prelude::v1::*;
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))?;

View file

@ -1,7 +1,7 @@
use chainerror::Context as _;
use chainerror::prelude::v1::*;
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))?;

View file

@ -1,7 +1,7 @@
use chainerror::Context as _;
use chainerror::prelude::v1::*;
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))?;

View file

@ -1,10 +1,7 @@
#![allow(clippy::single_match)]
#![allow(clippy::redundant_pattern_matching)]
use chainerror::Context as _;
use chainerror::prelude::v1::*;
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))?;

View file

@ -1,10 +1,7 @@
#![allow(clippy::single_match)]
#![allow(clippy::redundant_pattern_matching)]
use chainerror::{Context as _, ErrorDown as _};
use chainerror::prelude::v1::*;
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))?;
@ -18,7 +15,7 @@ fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
}
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().context("func1 error")?;
func2().context(format!("func1 error"))?;
Ok(())
}

View file

@ -1,14 +1,14 @@
use chainerror::{Context as _, ErrorDown as _};
use chainerror::prelude::v1::*;
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(())
}
chainerror::str_context!(Func2Error);
derive_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(())
}
chainerror::str_context!(Func1Error);
derive_str_context!(Func1Error);
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().context(Func1Error::new("func1 error"))?;
func2().context(Func1Error(format!("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::Error<Func2Error>>() {
if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() {
eprintln!("Func2Error: {}", f2err);
}

View file

@ -1,14 +1,14 @@
use chainerror::{Context as _, ErrorDown};
use chainerror::prelude::v1::*;
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(())
}
chainerror::str_context!(Func2Error);
derive_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(())
}
chainerror::str_context!(Func1ErrorFunc2);
chainerror::str_context!(Func1ErrorIO);
derive_str_context!(Func1ErrorFunc2);
derive_str_context!(Func1ErrorIO);
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().context(Func1ErrorFunc2::new("func1 error calling func2"))?;
func2().context(Func1ErrorFunc2(format!("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::Error<Func1ErrorIO>>() {
if let Some(s) = e.downcast_ref::<ChainError<Func1ErrorIO>>() {
eprintln!("Func1ErrorIO:\n{:?}", s);
}

File diff suppressed because it is too large Load diff

View file

@ -1,33 +0,0 @@
use chainerror::Context;
#[test]
fn test_basic() {
use std::path::PathBuf;
type BoxedError = Box<dyn std::error::Error + Send + Sync>;
fn read_config_file(path: PathBuf) -> Result<(), BoxedError> {
// do stuff, return other errors
let _buf = std::fs::read_to_string(&path).context(format!("Reading file: {:?}", &path))?;
// do stuff, return other errors
Ok(())
}
fn process_config_file() -> Result<(), BoxedError> {
// do stuff, return other errors
read_config_file("_non_existent.txt".into()).context("read the config file")?;
// do stuff, return other errors
Ok(())
}
if let Err(e) = process_config_file() {
let os_notfound_error = std::io::Error::from_raw_os_error(2);
let s = format!("{:?}", e);
let lines = s.lines().collect::<Vec<_>>();
assert_eq!(lines.len(), 5);
assert!(lines[0].starts_with("tests/test_basic.rs:"));
assert_eq!(lines[1], "Caused by:");
assert!(lines[2].starts_with("tests/test_basic.rs:"));
assert_eq!(lines[3], "Caused by:");
assert_eq!(lines[4], format!("{:?}", os_notfound_error));
} else {
panic!();
}
}

View file

@ -1,7 +1,8 @@
use chainerror::Context;
use chainerror::prelude::v1::*;
use std::error::Error;
use std::io;
#[cfg(not(feature = "display-cause"))]
#[test]
fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> {
use std::fmt::Write;
@ -17,13 +18,13 @@ fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> {
let mut res = String::new();
for e in err.iter() {
write!(res, "{}", e)?;
write!(res, "{}", e.to_string())?;
}
assert_eq!(res, "654321entity not found");
let io_error: Option<&io::Error> = err
.iter()
.filter_map(<dyn Error>::downcast_ref::<io::Error>)
.filter_map(Error::downcast_ref::<io::Error>)
.next();
assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound);
@ -31,8 +32,9 @@ fn test_iter() -> Result<(), Box<dyn Error + Send + Sync>> {
Ok(())
}
#[cfg(feature = "display-cause")]
#[test]
fn test_iter_alternate() -> 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 = err.context("1");
let err = err.context("2");
@ -42,13 +44,13 @@ fn test_iter_alternate() -> Result<(), Box<dyn Error + Send + Sync>> {
let err = err.context("6");
let err = err.err().unwrap();
let res = format!("{:#}", err);
let res = err.to_string();
assert_eq!(res, format!("6\nCaused by:\n 5\nCaused by:\n 4\nCaused by:\n 3\nCaused by:\n 2\nCaused by:\n 1\nCaused by:\n {:#}", io::Error::from(io::ErrorKind::NotFound)));
assert_eq!(res, "6\nCaused by:\n5\nCaused by:\n4\nCaused by:\n3\nCaused by:\n2\nCaused by:\n1\nCaused by:\nentity not found");
let io_error: Option<&io::Error> = err
.iter()
.filter_map(<dyn Error>::downcast_ref::<io::Error>)
.filter_map(Error::downcast_ref::<io::Error>)
.next();
assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound);
@ -86,7 +88,7 @@ fn test_root_cause() -> Result<(), Box<dyn Error + Send + Sync>> {
let err = err.err().unwrap();
let err: Option<&(dyn std::error::Error + 'static)> = err.root_cause();
let io_error: Option<&io::Error> = err.and_then(<dyn Error>::downcast_ref::<io::Error>);
let io_error: Option<&io::Error> = err.and_then(Error::downcast_ref::<io::Error>);
assert_eq!(io_error.unwrap().kind(), io::ErrorKind::NotFound);