diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7dff904..21025a8 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -13,26 +13,31 @@ on: types: - created - jobs: - coverage: + test: + name: coverage runs-on: ubuntu-latest - env: - CARGO_TERM_COLOR: always steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v1 - uses: dtolnay/rust-toolchain@master with: target: x86_64-unknown-linux-gnu toolchain: nightly components: llvm-tools-preview + - name: Install cargo-llvm-cov - uses: taiki-e/install-action@cargo-llvm-cov - - name: Generate code coverage - run: cargo +nightly llvm-cov --all-features --workspace --codecov --doctests --output-path codecov.json + run: > + curl -LsSf 'https://github.com/taiki-e/cargo-llvm-cov/releases/download/v0.5.23/cargo-llvm-cov-x86_64-unknown-linux-musl.tar.gz' + | tar xzf - + && mv cargo-llvm-cov $HOME/.cargo/bin + + - name: Run cargo-llvm-cov + run: cargo llvm-cov --doctests --all --all-features --lcov --output-path lcov.info + - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: - token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos - files: codecov.json - fail_ci_if_error: true + directory: ./ + fail_ci_if_error: false + files: ./lcov.info + verbose: true diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 7062679..bcc15ae 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -7,7 +7,7 @@ on: jobs: deploy: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 59b8808..9c1824f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "1.0.0" +version = "0.8.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT OR Apache-2.0" @@ -21,6 +21,3 @@ github = { repository = "haraldh/chainerror", workflow = "Rust" } maintenance = { status = "actively-developed" } is-it-maintained-issue-resolution = { repository = "haraldh/chainerror" } is-it-maintained-open-issues = { repository = "haraldh/chainerror" } - -[package.metadata.docs.rs] -cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/README.md b/README.md index 3a46567..7a7b3a9 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ and you have no idea where it comes from. With `chainerror`, you can supply a context and get a nice error backtrace: ```rust -use chainerror::Context as _; +use chainerror::prelude::v1::*; use std::path::PathBuf; type BoxedError = Box; @@ -84,87 +84,12 @@ Os { code: 2, kind: NotFound, message: "No such file or directory" } `chainerror` uses `.source()` of `std::error::Error` along with `#[track_caller]` and `Location` to provide a nice debug error backtrace. It encapsulates all types, which have `Display + Debug` and can store the error cause internally. -Along with the `Error` struct, `chainerror` comes with some useful helper macros to save a lot of typing. +Along with the `ChainError` struct, `chainerror` comes with some useful helper macros to save a lot of typing. `chainerror` has no dependencies! Debug information is worth it! -## Multiple Output Formats - -`chainerror` supports multiple output formats, which can be selected with the different format specifiers: - -* `{}`: Display -```console -func1 error calling func2 -``` - -* `{:#}`: Alternative Display -```console -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 -```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' -Caused by: -Kind(NotFound) - -``` - -* `{:#?}`: Alternative Debug -```console -Error { - occurrence: Some( - "examples/example.rs:50:13", - ), - kind: func1 error calling func2, - source: Some( - Error { - occurrence: Some( - "examples/example.rs:25:13", - ), - kind: Func2Error(func2 error: calling func3), - source: Some( - Error { - occurrence: Some( - "examples/example.rs:18:13", - ), - kind: (passed error), - source: Some( - Error { - occurrence: Some( - "examples/example.rs:13:18", - ), - kind: "Error reading 'foo.txt'", - source: Some( - Kind( - NotFound, - ), - ), - }, - ), - }, - ), - }, - ), -} -``` - ## Tutorial Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) @@ -173,8 +98,8 @@ Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) Licensed under either of -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or ) +* 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. diff --git a/README.tpl b/README.tpl new file mode 100644 index 0000000..01e969a --- /dev/null +++ b/README.tpl @@ -0,0 +1,24 @@ +[![Crate](https://img.shields.io/crates/v/chainerror.svg)](https://crates.io/crates/chainerror) +[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/chainerror/) +[![Coverage Status](https://coveralls.io/repos/github/haraldh/chainerror/badge.svg?branch=master)](https://coveralls.io/github/haraldh/chainerror?branch=master) +{{badges}} + +# {{crate}} + +{{readme}} + +## License + +Licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/booksrc/LICENSE-APACHE b/booksrc/LICENSE-APACHE deleted file mode 120000 index 965b606..0000000 --- a/booksrc/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/booksrc/LICENSE-MIT b/booksrc/LICENSE-MIT deleted file mode 120000 index 76219eb..0000000 --- a/booksrc/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/booksrc/tutorial10.md b/booksrc/tutorial10.md index 5547954..07d0100 100644 --- a/booksrc/tutorial10.md +++ b/booksrc/tutorial10.md @@ -1,6 +1,6 @@ # ErrorKind to the rescue -To cope with different kind of errors, we introduce the `kind` of an error `Func1ErrorKind` with an enum. +To cope with different kind of errors, we introduce the kind of an error `Func1ErrorKind` with an enum. Because we derive `Debug` and implement `Display` our `Func1ErrorKind` enum, this enum can be used as a `std::error::Error`. @@ -8,9 +8,10 @@ a `std::error::Error`. Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box>` and we can use `ChainResult<(), Func1ErrorKind>`. -In `main` we can now directly use the methods of `chainerror::Error` without downcasting the error first. +In `main` we can now directly use the methods of `ChainError` without downcasting the error first. -Also, a nice `match` on `chainerror::Error.kind()` is now possible, which returns `&T`, meaning `&Func1ErrorKind` here. +Also a nice `match` on `ChainError.kind()` is now possible, which returns `&T`, meaning +`&Func1ErrorKind` here. ~~~rust {{#include ../examples/tutorial10.rs}} diff --git a/booksrc/tutorial11.md b/booksrc/tutorial11.md index 7d37814..197b9d3 100644 --- a/booksrc/tutorial11.md +++ b/booksrc/tutorial11.md @@ -21,7 +21,7 @@ which gives us a lot more detail. To create your own Errors, you might find [crates](https://crates.io) which create enum `Display+Debug` via derive macros. -Also, noteworthy is [custom_error](https://crates.io/crates/custom_error) to define your custom errors, +Also noteworthy is [custom_error](https://crates.io/crates/custom_error) to define your custom errors, which can then be used with `chainerror`. ~~~rust diff --git a/booksrc/tutorial12.md b/booksrc/tutorial12.md index 037803b..a8c80e6 100644 --- a/booksrc/tutorial12.md +++ b/booksrc/tutorial12.md @@ -1,6 +1,6 @@ # Deref for the ErrorKind -Because chainerror::Error implements Deref to &T, we can also match on `*e` instead of `e.kind()` +Because ChainError implements Deref to &T, we can also match on `*e` instead of `e.kind()` or call a function with `&e` ~~~rust {{#include ../examples/tutorial12.rs}} diff --git a/booksrc/tutorial13.md b/booksrc/tutorial13.md index 0ed9db8..6d438f4 100644 --- a/booksrc/tutorial13.md +++ b/booksrc/tutorial13.md @@ -1,6 +1,6 @@ # Writing a library -I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `chainerror::Error` +I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `ChainError` so you can tell your library users to use the `.kind()` method as `std::io::Error` does. If you later decide to make your own `Error` implementation, your library users don't diff --git a/booksrc/tutorial2.md b/booksrc/tutorial2.md index a330c1a..27e1fbb 100644 --- a/booksrc/tutorial2.md +++ b/booksrc/tutorial2.md @@ -25,7 +25,7 @@ along with the `Location` of the `context()` call and returns `Err(newerror)`. `?` then returns the inner error applying `.into()`, so that we again have a `Err(Box)` as a result. -The `Debug` implementation of `chainerror::Error` (which is returned by `context()`) +The `Debug` implementation of `ChainError` (which is returned by `context()`) prints the `Debug` of `T` prefixed with the stored filename and line number. -`chainerror::Error` in our case is `chainerror::Error<&str>`. +`ChainError` in our case is `ChainError<&str>`. diff --git a/booksrc/tutorial3.md b/booksrc/tutorial3.md index f6c26ac..e44cc7d 100644 --- a/booksrc/tutorial3.md +++ b/booksrc/tutorial3.md @@ -14,13 +14,13 @@ If you compare the output to the previous example, you will see, that: ~~~ -Error: 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. diff --git a/booksrc/tutorial5.md b/booksrc/tutorial5.md index 57932fa..05db784 100644 --- a/booksrc/tutorial5.md +++ b/booksrc/tutorial5.md @@ -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`. + \ No newline at end of file diff --git a/booksrc/tutorial7.md b/booksrc/tutorial7.md index 90665f7..ccb6ff6 100644 --- a/booksrc/tutorial7.md +++ b/booksrc/tutorial7.md @@ -4,15 +4,15 @@ ~~~rust,ignore fn is_chain(&self) -> bool -fn downcast_chain_ref(&self) -> Option<&chainerror::Error> -fn downcast_chain_mut(&mut self) -> Option<&mut chainerror::Error> +fn downcast_chain_ref(&self) -> Option<&ChainError> +fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> fn root_cause(&self) -> Option<&(dyn Error + 'static)> fn find_cause(&self) -> Option<&U> -fn find_chain_cause(&self) -> Option<&chainerror::Error> +fn find_chain_cause(&self) -> Option<&ChainError> fn kind<'a>(&'a self) -> &'a T ~~~ -Using `downcast_chain_ref::()` gives a `chainerror::Error`, which can be used +Using `downcast_chain_ref::()` gives a `ChainError`, which can be used to call `.find_cause::()`. ~~~rust,ignore diff --git a/booksrc/tutorial8.md b/booksrc/tutorial8.md index 17070fe..da0c405 100644 --- a/booksrc/tutorial8.md +++ b/booksrc/tutorial8.md @@ -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` we now have `struct Func1Error(String)` and `chainerror::Error`. +Instead of `ChainError` we now have `struct Func1Error(String)` and `ChainError`. In the `main` function you can see, how we can match the different errors. @@ -18,9 +18,9 @@ Also see: ~~~ as a shortcut to ~~~rust,ignore - if let Some(f2err) = f1err.find_cause::>() { + if let Some(f2err) = f1err.find_cause::>() { ~~~ -hiding the `chainerror::Error` implementation detail. +hiding the `ChainError` implementation detail. ~~~rust {{#include ../examples/tutorial8.rs}} diff --git a/examples/example.rs b/examples/example.rs index 82c073f..859a12c 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func4() -> Result<(), Box> { +fn func3() -> Result<(), Box> { let filename = "foo.txt"; do_some_io().context(format!("Error reading '{}'", filename))?; Ok(()) } -fn func3() -> Result<(), Box> { - 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("func2 error: calling func3".to_string()))?; 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))?; diff --git a/examples/tutorial1.rs b/examples/tutorial1.rs index 08ca3e8..939fb73 100644 --- a/examples/tutorial1.rs +++ b/examples/tutorial1.rs @@ -3,6 +3,7 @@ use std::error::Error; use std::io; +use std::result::Result; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial10.rs b/examples/tutorial10.rs index 3e0af4a..5118c63 100644 --- a/examples/tutorial10.rs +++ b/examples/tutorial10.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -chainerror::str_context!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -32,7 +32,7 @@ impl ::std::fmt::Display for Func1ErrorKind { } impl ::std::error::Error for Func1ErrorKind {} -fn func1() -> chainerror::Result<(), Func1ErrorKind> { +fn func1() -> ChainResult<(), Func1ErrorKind> { func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); do_some_io().context(Func1ErrorKind::IO(filename))?; diff --git a/examples/tutorial11.rs b/examples/tutorial11.rs index abbca94..a943b97 100644 --- a/examples/tutorial11.rs +++ b/examples/tutorial11.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -chainerror::str_context!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -38,7 +38,7 @@ impl ::std::fmt::Debug for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} -fn func1() -> chainerror::Result<(), Func1ErrorKind> { +fn func1() -> ChainResult<(), Func1ErrorKind> { func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); do_some_io().context(Func1ErrorKind::IO(filename))?; diff --git a/examples/tutorial12.rs b/examples/tutorial12.rs index caa8fca..e2bf644 100644 --- a/examples/tutorial12.rs +++ b/examples/tutorial12.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -chainerror::str_context!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -38,7 +38,7 @@ impl ::std::fmt::Debug for Func1ErrorKind { impl ::std::error::Error for Func1ErrorKind {} -fn func1() -> chainerror::Result<(), Func1ErrorKind> { +fn func1() -> ChainResult<(), Func1ErrorKind> { func2().context(Func1ErrorKind::Func2)?; let filename = String::from("bar.txt"); do_some_io().context(Func1ErrorKind::IO(filename))?; diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 0d50d68..35dc832 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -2,8 +2,7 @@ #![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> { @@ -11,7 +10,7 @@ pub mod mycrate { Ok(()) } - chainerror::str_context!(Func2Error); + derive_str_context!(Func2Error); fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; @@ -25,7 +24,7 @@ pub mod mycrate { IO(String), } - chainerror::err_kind!(Error, ErrorKind); + derive_err_kind!(Error, ErrorKind); pub type Result = std::result::Result; diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs index 42355b3..39594b4 100644 --- a/examples/tutorial15.rs +++ b/examples/tutorial15.rs @@ -2,8 +2,7 @@ #![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 +10,7 @@ pub mod mycrate { Ok(()) } - chainerror::str_context!(Func2Error); + derive_str_context!(Func2Error); fn func2() -> std::result::Result<(), Box> { let filename = "foo.txt"; @@ -27,7 +26,7 @@ pub mod mycrate { Unknown, } - chainerror::err_kind!(Error, ErrorKind); + derive_err_kind!(Error, ErrorKind); pub type Result = std::result::Result; impl std::fmt::Display for ErrorKind { diff --git a/examples/tutorial2.rs b/examples/tutorial2.rs index 96742ad..8c68dc0 100644 --- a/examples/tutorial2.rs +++ b/examples/tutorial2.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial3.rs b/examples/tutorial3.rs index 62a44eb..8475d6f 100644 --- a/examples/tutorial3.rs +++ b/examples/tutorial3.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial4.rs b/examples/tutorial4.rs index 3dea51c..d1b247b 100644 --- a/examples/tutorial4.rs +++ b/examples/tutorial4.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial5.rs b/examples/tutorial5.rs index d9fe708..333208c 100644 --- a/examples/tutorial5.rs +++ b/examples/tutorial5.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial6.rs b/examples/tutorial6.rs index 5d81310..de1a0a8 100644 --- a/examples/tutorial6.rs +++ b/examples/tutorial6.rs @@ -1,10 +1,10 @@ #![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> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial7.rs b/examples/tutorial7.rs index 214484c..9a1ca67 100644 --- a/examples/tutorial7.rs +++ b/examples/tutorial7.rs @@ -1,10 +1,10 @@ #![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> { Err(io::Error::from(io::ErrorKind::NotFound))?; diff --git a/examples/tutorial8.rs b/examples/tutorial8.rs index e32a449..cf5e654 100644 --- a/examples/tutorial8.rs +++ b/examples/tutorial8.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -chainerror::str_context!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -16,10 +16,10 @@ fn func2() -> Result<(), Box> { Ok(()) } -chainerror::str_context!(Func1Error); +derive_str_context!(Func1Error); fn func1() -> Result<(), Box> { - func2().context(Func1Error::new("func1 error"))?; + func2().context(Func1Error("func1 error".to_string()))?; Ok(()) } @@ -28,7 +28,7 @@ fn main() -> Result<(), Box> { if let Some(f1err) = e.downcast_chain_ref::() { eprintln!("Func1Error: {}", f1err); - if let Some(f2err) = f1err.find_cause::>() { + if let Some(f2err) = f1err.find_cause::>() { eprintln!("Func2Error: {}", f2err); } diff --git a/examples/tutorial9.rs b/examples/tutorial9.rs index bbbe810..754c621 100644 --- a/examples/tutorial9.rs +++ b/examples/tutorial9.rs @@ -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> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -chainerror::str_context!(Func2Error); +derive_str_context!(Func2Error); fn func2() -> Result<(), Box> { let filename = "foo.txt"; @@ -16,11 +16,11 @@ fn func2() -> Result<(), Box> { Ok(()) } -chainerror::str_context!(Func1ErrorFunc2); -chainerror::str_context!(Func1ErrorIO); +derive_str_context!(Func1ErrorFunc2); +derive_str_context!(Func1ErrorIO); fn func1() -> Result<(), Box> { - func2().context(Func1ErrorFunc2::new("func1 error calling func2"))?; + func2().context(Func1ErrorFunc2("func1 error calling func2".to_string()))?; let filename = "bar.txt"; do_some_io().context(Func1ErrorIO(format!("Error reading '{}'", filename)))?; Ok(()) @@ -28,7 +28,7 @@ fn func1() -> Result<(), Box> { fn main() -> Result<(), Box> { if let Err(e) = func1() { - if let Some(s) = e.downcast_ref::>() { + if let Some(s) = e.downcast_ref::>() { eprintln!("Func1ErrorIO:\n{:?}", s); } diff --git a/src/lib.rs b/src/lib.rs index 623523d..50dbd51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,18 @@ use std::error::Error as StdError; use std::fmt::{Debug, Display, Formatter}; use std::panic::Location; +pub mod prelude { + //! convenience prelude + pub mod v1 { + //! convenience prelude + pub use super::super::ChainErrorDown as _; + pub use super::super::Error as ChainError; + pub use super::super::Result as ChainResult; + pub use super::super::ResultTrait as _; + pub use crate::{derive_err_kind, derive_str_context}; + } +} + /// chains an inner error kind `T` with a causing error pub struct Error { occurrence: Option, @@ -43,8 +55,7 @@ impl Error { /// # Examples /// /// ```rust - /// use chainerror::Context as _; - /// use chainerror::ErrorDown as _; + /// use chainerror::prelude::v1::*; /// use std::error::Error; /// use std::io; /// @@ -53,7 +64,7 @@ impl Error { /// Ok(()) /// } /// - /// chainerror::str_context!(Func2Error); + /// derive_str_context!(Func2Error); /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; @@ -61,10 +72,10 @@ impl Error { /// Ok(()) /// } /// - /// chainerror::str_context!(Func1Error); + /// derive_str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { - /// func2().context(Func1Error::new("func1 error"))?; + /// func2().context(Func1Error("func1 error".into()))?; /// Ok(()) /// } /// @@ -89,19 +100,20 @@ impl Error { .next() } - /// Find the first error cause of type [`Error`](Error), if any exists + /// Find the first error cause of type `ChainError`, if any exists /// - /// Same as `find_cause`, but hides the [`Error`](Error) implementation internals + /// Same as `find_cause`, but hides the `ChainError` implementation internals /// /// # Examples /// /// ```rust - /// # chainerror::str_context!(FooError); - /// # let err = chainerror::Error::new(String::new(), None, None); + /// # use chainerror::prelude::v1::*; + /// # derive_str_context!(FooError); + /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing - /// err.find_cause::>(); + /// err.find_cause::>(); /// - /// // leave out the chainerror::Error implementation detail + /// // leave out the ChainError implementation detail /// err.find_chain_cause::(); /// ``` #[inline] @@ -111,23 +123,24 @@ impl Error { .next() } - /// Find the first error cause of type [`Error`](Error) or `U`, if any exists and return `U` + /// Find the first error cause of type `ChainError` or `U`, if any exists and return `U` /// - /// Same as `find_cause` and `find_chain_cause`, but hides the [`Error`](Error) implementation internals + /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError` implementation internals /// /// # Examples /// /// ```rust - /// # chainerror::str_context!(FooErrorKind); - /// # let err = chainerror::Error::new(String::new(), None, None); + /// # use chainerror::prelude::v1::*; + /// # derive_str_context!(FooErrorKind); + /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing - /// err.find_cause::>(); + /// err.find_cause::>(); /// // and/or /// err.find_chain_cause::(); /// // and/or /// err.find_cause::(); /// - /// // leave out the chainerror::Error implementation detail + /// // leave out the ChainError implementation detail /// err.find_kind_or_cause::(); /// ``` #[inline] @@ -141,12 +154,12 @@ impl Error { .next() } - /// Return a reference to T of [`Error`](Error) + /// Return a reference to T of `ChainError` /// /// # Examples /// /// ```rust - /// use chainerror::Context as _; + /// use chainerror::prelude::v1::*; /// use std::error::Error; /// use std::io; /// @@ -155,7 +168,7 @@ impl Error { /// Ok(()) /// } /// - /// chainerror::str_context!(Func2Error); + /// derive_str_context!(Func2Error); /// /// fn func2() -> Result<(), Box> { /// let filename = "foo.txt"; @@ -179,7 +192,7 @@ impl Error { /// # } /// # } /// - /// fn func1() -> chainerror::Result<(), Func1ErrorKind> { + /// fn func1() -> ChainResult<(), Func1ErrorKind> { /// func2().context(Func1ErrorKind::Func2)?; /// do_some_io().context(Func1ErrorKind::IO("bar.txt".into()))?; /// Ok(()) @@ -211,14 +224,11 @@ impl Error { } } -/// Convenience methods for `Result<>` to turn the error into a decorated [`Error`](Error) -pub trait Context>> { +/// Convenience methods for `Result<>` to turn the error into a decorated ChainError +pub trait ResultTrait>> { /// Decorate the error with a `kind` of type `T` and the source `Location` fn context(self, kind: T) -> std::result::Result>; - /// Decorate the error just with the source `Location` - fn annotate(self) -> std::result::Result>; - /// Decorate the `error` with a `kind` of type `T` produced with a `FnOnce(&error)` and the source `Location` fn map_context T>( self, @@ -226,22 +236,7 @@ pub trait Context>> { ) -> std::result::Result>; } -/// Convenience type to just decorate the error with the source `Location` -pub struct AnnotatedError(()); - -impl Display for AnnotatedError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "(passed error)") - } -} - -impl Debug for AnnotatedError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "(passed error)") - } -} - -impl>> Context +impl>> ResultTrait for std::result::Result { #[track_caller] @@ -257,19 +252,6 @@ impl>> Context } } - #[track_caller] - #[inline] - fn annotate(self) -> std::result::Result> { - match self { - Ok(t) => Ok(t), - Err(error_cause) => Err(Error::new( - AnnotatedError(()), - Some(error_cause.into()), - Some(Location::caller().to_string()), - )), - } - } - #[track_caller] #[inline] fn map_context T>( @@ -315,21 +297,21 @@ impl std::ops::Deref for Error { } } -/// Convenience trait to hide the [`Error`](Error) implementation internals -pub trait ErrorDown { - /// Test if of type `Error` +/// Convenience trait to hide the `ChainError` implementation internals +pub trait ChainErrorDown { + /// Test if of type `ChainError` fn is_chain(&self) -> bool; - /// Downcast to a reference of `Error` + /// Downcast to a reference of `ChainError` fn downcast_chain_ref(&self) -> Option<&Error>; - /// Downcast to a mutable reference of `Error` + /// Downcast to a mutable reference of `ChainError` fn downcast_chain_mut(&mut self) -> Option<&mut Error>; - /// Downcast to T of `Error` + /// Downcast to T of `ChainError` fn downcast_inner_ref(&self) -> Option<&T>; - /// Downcast to T mutable reference of `Error` + /// Downcast to T mutable reference of `ChainError` fn downcast_inner_mut(&mut self) -> Option<&mut T>; } -impl ErrorDown for Error { +impl ChainErrorDown for Error { #[inline] fn is_chain(&self) -> bool { TypeId::of::() == TypeId::of::() @@ -338,8 +320,11 @@ impl ErrorDown for Error { #[inline] fn downcast_chain_ref(&self) -> Option<&Error> { if self.is_chain::() { - // Use transmute when we've verified the types match - unsafe { Some(std::mem::transmute::<&Error, &Error>(self)) } + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(*(self as *const dyn StdError as *const &Error)) + } } else { None } @@ -348,8 +333,11 @@ impl ErrorDown for Error { #[inline] fn downcast_chain_mut(&mut self) -> Option<&mut Error> { if self.is_chain::() { - // Use transmute when we've verified the types match - unsafe { Some(std::mem::transmute::<&mut Error, &mut Error>(self)) } + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut *(self as *mut dyn StdError as *mut &mut Error)) + } } else { None } @@ -357,8 +345,11 @@ impl ErrorDown for Error { #[inline] fn downcast_inner_ref(&self) -> Option<&T> { if self.is_chain::() { - // Use transmute when we've verified the types match - unsafe { Some(std::mem::transmute::<&U, &T>(&self.kind)) } + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&(*(self as *const dyn StdError as *const &Error)).kind) + } } else { None } @@ -367,15 +358,18 @@ impl ErrorDown for Error { #[inline] fn downcast_inner_mut(&mut self) -> Option<&mut T> { if self.is_chain::() { - // Use transmute when we've verified the types match - unsafe { Some(std::mem::transmute::<&mut U, &mut T>(&mut self.kind)) } + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut (*(self as *mut dyn StdError as *mut &mut Error)).kind) + } } else { None } } } -impl ErrorDown for dyn StdError + 'static { +impl ChainErrorDown for dyn StdError + 'static { #[inline] fn is_chain(&self) -> bool { self.is::>() @@ -408,7 +402,7 @@ impl ErrorDown for dyn StdError + 'static { } } -impl ErrorDown for dyn StdError + 'static + Send { +impl ChainErrorDown for dyn StdError + 'static + Send { #[inline] fn is_chain(&self) -> bool { self.is::>() @@ -441,7 +435,7 @@ impl ErrorDown for dyn StdError + 'static + Send { } } -impl ErrorDown for dyn StdError + 'static + Send + Sync { +impl ChainErrorDown for dyn StdError + 'static + Send + Sync { #[inline] fn is_chain(&self) -> bool { self.is::>() @@ -511,7 +505,7 @@ impl Debug for Error { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if f.alternate() { - let mut f = f.debug_struct(&format!("Error<{}>", std::any::type_name::())); + let mut f = f.debug_struct(&format!("ChainError<{}>", std::any::type_name::())); let f = f .field("occurrence", &self.occurrence) @@ -555,8 +549,7 @@ where /// # Examples /// /// ```rust -/// # use chainerror::Context as _; -/// # use chainerror::ErrorDown as _; +/// # use chainerror::prelude::v1::*; /// # use std::error::Error; /// # use std::io; /// # use std::result::Result; @@ -564,23 +557,23 @@ where /// # Err(io::Error::from(io::ErrorKind::NotFound))?; /// # Ok(()) /// # } -/// chainerror::str_context!(Func2Error); +/// derive_str_context!(Func2Error); /// -/// fn func2() -> chainerror::Result<(), Func2Error> { +/// fn func2() -> ChainResult<(), Func2Error> { /// let filename = "foo.txt"; /// do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?; /// Ok(()) /// } /// -/// chainerror::str_context!(Func1Error); +/// derive_str_context!(Func1Error); /// /// fn func1() -> Result<(), Box> { -/// func2().context(Func1Error::new("func1 error"))?; +/// func2().context(Func1Error("func1 error".into()))?; /// Ok(()) /// } /// # if let Err(e) = func1() { /// # if let Some(f1err) = e.downcast_chain_ref::() { -/// # assert!(f1err.find_cause::>().is_some()); +/// # assert!(f1err.find_cause::>().is_some()); /// # assert!(f1err.find_chain_cause::().is_some()); /// # } else { /// # panic!(); @@ -590,16 +583,10 @@ where /// # } /// ``` #[macro_export] -macro_rules! str_context { +macro_rules! derive_str_context { ($e:ident) => { #[derive(Clone)] pub struct $e(pub String); - impl $e { - #[allow(dead_code)] - pub fn new>(s: S) -> Self { - $e(s.into()) - } - } impl ::std::fmt::Display for $e { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "{}", self.0) @@ -614,9 +601,9 @@ macro_rules! str_context { }; } -/// Derive an Error for an ErrorKind, which wraps a [`Error`](Error) and implements a `kind()` method +/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method /// -/// It basically hides [`Error`](Error) to the outside and only exposes the [`kind()`](Error::kind) +/// It basically hides `ChainError` to the outside and only exposes the `kind()` /// method. /// /// Error::kind() returns the ErrorKind @@ -625,7 +612,7 @@ macro_rules! str_context { /// # Examples /// /// ```rust -/// use chainerror::Context as _; +/// use chainerror::prelude::v1::*; /// use std::io; /// /// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { @@ -639,7 +626,7 @@ macro_rules! str_context { /// Unknown, /// } /// -/// chainerror::err_kind!(Error, ErrorKind); +/// derive_err_kind!(Error, ErrorKind); /// /// impl std::fmt::Display for ErrorKind { /// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { @@ -677,15 +664,9 @@ macro_rules! str_context { /// do_some_io(filename).map_context(|e| ErrorKind::from(e))?; /// Ok(()) /// } -/// -/// # fn main() { -/// # if let Err(e) = func1() { -/// # eprintln!("Error:\n{:?}", e); -/// # } -/// # } /// ``` #[macro_export] -macro_rules! err_kind { +macro_rules! derive_err_kind { ($e:ident, $k:ident) => { pub struct $e($crate::Error<$k>); @@ -701,7 +682,7 @@ macro_rules! err_kind { } } - impl From<$crate::Error<$k>> for $e { + impl From> for $e { fn from(e: $crate::Error<$k>) -> Self { $e(e) } @@ -735,291 +716,3 @@ macro_rules! err_kind { } }; } - -#[cfg(test)] -mod tests { - use super::Context as _; - use super::*; - use std::io; - - #[test] - fn test_error_chain_with_multiple_causes() { - // Create a chain of errors - let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); - - str_context!(Level3Error); - str_context!(Level2Error); - str_context!(Level1Error); - - let err = Result::<(), _>::Err(io_error.into()) - .context(Level3Error("level 3".into())) - .context(Level2Error("level 2".into())) - .context(Level1Error("level 1".into())) - .unwrap_err(); - - // Test the error chain - assert!(err.is_chain::()); - assert!(err.find_chain_cause::().is_some()); - assert!(err.find_chain_cause::().is_some()); - assert!(err.find_chain_cause::().is_some()); - } - - #[test] - fn test_error_root_cause() { - let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); - - str_context!(WrapperError); - let err = Result::<(), _>::Err(io_error.into()) - .context(WrapperError("wrapper".into())) - .unwrap_err(); - - let root = err.root_cause().unwrap(); - assert!(root.is_chain::()); - } - - #[test] - fn test_error_display_and_debug() { - str_context!(CustomError); - let err = Error::new( - CustomError("test error".into()), - None, - Some("src/lib.rs:100".into()), - ); - - // Test Display formatting - assert_eq!(format!("{}", err), "test error"); - - // Test alternate Display formatting - assert_eq!(format!("{:#}", err), "test error"); - - // Test Debug formatting - let debug_output = format!("{:?}", err); - assert!(debug_output.contains("test error")); - assert!(debug_output.contains("src/lib.rs:100")); - } - - #[test] - fn test_error_annotation() { - let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); - let err = Result::<(), _>::Err(io_error.into()) - .annotate() - .unwrap_err(); - - assert!(err.source().is_some()); - err.source() - .unwrap() - .downcast_inner_ref::() - .unwrap(); - } - - #[test] - fn test_map_context() { - let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); - - str_context!(MappedError); - let err = Result::<(), _>::Err(io_error.into()) - .map_context(|e| MappedError(format!("Mapped: {}", e))) - .unwrap_err(); - - assert!(err.is_chain::()); - assert!(err.find_chain_cause::().is_some()); - } - - #[test] - fn test_error_downcasting() { - str_context!(OriginalError); - let original = Error::new(OriginalError("test".into()), None, None); - - let error: Box = Box::new(original); - - // Test downcast_chain_ref - assert!(error.is_chain::()); - assert!(error.downcast_chain_ref::().is_some()); - - // Test downcast_inner_ref - let inner = error.downcast_inner_ref::(); - assert!(inner.is_some()); - } - - #[derive(Debug, Clone)] - enum TestErrorKind { - Basic(String), - Complex { message: String }, - } - - impl Display for TestErrorKind { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - TestErrorKind::Basic(msg) => write!(f, "Basic error: {}", msg), - TestErrorKind::Complex { message } => write!(f, "Complex error: {}", message), - } - } - } - - #[test] - fn test_err_kind_macro() { - err_kind!(TestError, TestErrorKind); - - let err = TestError::from(TestErrorKind::Basic("test".into())); - assert!(matches!(err.kind(), TestErrorKind::Basic(_))); - // The annotated error should display "(passed error)" even in a chain - assert_eq!(format!("{}", err), "Basic error: test"); - assert_eq!(format!("{:?}", err), "Basic(\"test\")"); - - let complex_err = TestError::from(TestErrorKind::Complex { - message: "test".into(), - }); - assert!(matches!(complex_err.kind(), TestErrorKind::Complex { .. })); - // The annotated error should display "(passed error)" even in a chain - assert_eq!(format!("{}", complex_err), "Complex error: test"); - assert_eq!( - format!("{:?}", complex_err), - "Complex { message: \"test\" }" - ); - } - #[test] - fn test_annotated_error_display_and_debug() { - let annotated = AnnotatedError(()); - - // Test Display formatting - assert_eq!(format!("{}", annotated), "(passed error)"); - - // Test Debug formatting - assert_eq!(format!("{:?}", annotated), "(passed error)"); - - // Test with error chain - let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found"); - let err = Result::<(), _>::Err(io_error.into()) - .annotate() - .unwrap_err(); - - // The annotated error should display "(passed error)" even in a chain - assert_eq!(format!("{}", err), "(passed error)"); - assert!(format!("{:?}", err).contains("(passed error)")); - - // Verify the error chain is preserved - assert!(err.source().is_some()); - assert!(err.source().unwrap().is_chain::()); - } - - // Helper error types for testing - #[derive(Debug)] - struct TestError(String); - - impl std::fmt::Display for TestError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } - } - - impl std::error::Error for TestError {} - - #[test] - fn test_downcast_chain_operations() { - // Create a test error chain - let original_error = Error::new( - TestError("test message".to_string()), - None, - Some("test location".to_string()), - ); - - // Test is_chain - assert!(original_error.is_chain::()); - assert!(!original_error.is_chain::()); - - // Test downcast_chain_ref - let downcast_ref = original_error.downcast_chain_ref::(); - assert!(downcast_ref.is_some()); - let downcast_kind = downcast_ref.unwrap().kind(); - assert_eq!(format!("{}", downcast_kind), "test message"); - assert_eq!( - format!("{:?}", downcast_kind), - "TestError(\"test message\")" - ); - - // Test invalid downcast_chain_ref - let invalid_downcast = original_error.downcast_chain_ref::(); - assert!(invalid_downcast.is_none()); - - // Test downcast_chain_mut - let mut mutable_error = original_error; - let downcast_mut = mutable_error.downcast_chain_mut::(); - assert!(downcast_mut.is_some()); - assert_eq!(downcast_mut.unwrap().kind().0, "test message"); - - // Test invalid downcast_chain_mut - let invalid_downcast_mut = mutable_error.downcast_chain_mut::(); - assert!(invalid_downcast_mut.is_none()); - } - - #[test] - fn test_downcast_inner_operations() { - // Create a test error - let mut error = Error::new( - TestError("inner test".to_string()), - None, - Some("test location".to_string()), - ); - - // Test downcast_inner_ref - let inner_ref = error.downcast_inner_ref::(); - assert!(inner_ref.is_some()); - assert_eq!(inner_ref.unwrap().0, "inner test"); - // Test invalid downcast_inner_ref - let invalid_inner = error.downcast_inner_ref::(); - assert!(invalid_inner.is_none()); - - // Test downcast_inner_mut - let inner_mut = error.downcast_inner_mut::(); - assert!(inner_mut.is_some()); - assert_eq!(inner_mut.unwrap().0, "inner test"); - - // Test invalid downcast_inner_mut - let invalid_inner_mut = error.downcast_inner_mut::(); - assert!(invalid_inner_mut.is_none()); - } - - #[test] - fn test_error_down_for_dyn_error() { - // Create a boxed error - let error: Box = Box::new(Error::new( - TestError("dyn test".to_string()), - None, - Some("test location".to_string()), - )); - - // Test is_chain through trait object - assert!(error.is_chain::()); - assert!(!error.is_chain::()); - - // Test downcast_chain_ref through trait object - let chain_ref = error.downcast_chain_ref::(); - assert!(chain_ref.is_some()); - assert_eq!(chain_ref.unwrap().kind().0, "dyn test"); - - // Test downcast_inner_ref through trait object - let inner_ref = error.downcast_inner_ref::(); - assert!(inner_ref.is_some()); - assert_eq!(inner_ref.unwrap().0, "dyn test"); - } - - #[test] - fn test_error_down_with_sync_send() { - // Create a boxed error with Send + Sync - let error: Box = Box::new(Error::new( - TestError("sync test".to_string()), - None, - Some("test location".to_string()), - )); - - // Test operations on Send + Sync error - assert!(error.is_chain::()); - assert!(error.downcast_chain_ref::().is_some()); - assert!(error.downcast_inner_ref::().is_some()); - - // Test invalid downcasts - assert!(!error.is_chain::()); - assert!(error.downcast_chain_ref::().is_none()); - assert!(error.downcast_inner_ref::().is_none()); - } -} diff --git a/tests/test_basic.rs b/tests/test_basic.rs deleted file mode 100644 index bbc9f16..0000000 --- a/tests/test_basic.rs +++ /dev/null @@ -1,33 +0,0 @@ -use chainerror::Context; - -#[test] -fn test_basic() { - use std::path::PathBuf; - type BoxedError = Box; - fn read_config_file(path: PathBuf) -> Result<(), BoxedError> { - // do stuff, return other errors - let _buf = std::fs::read_to_string(&path).context(format!("Reading file: {:?}", &path))?; - // do stuff, return other errors - Ok(()) - } - fn process_config_file() -> Result<(), BoxedError> { - // do stuff, return other errors - read_config_file("_non_existent.txt".into()).context("read the config file")?; - // do stuff, return other errors - Ok(()) - } - - if let Err(e) = process_config_file() { - let os_notfound_error = std::io::Error::from_raw_os_error(2); - let s = format!("{:?}", e); - let lines = s.lines().collect::>(); - assert_eq!(lines.len(), 5); - assert!(lines[0].starts_with("tests/test_basic.rs:")); - assert_eq!(lines[1], "Caused by:"); - assert!(lines[2].starts_with("tests/test_basic.rs:")); - assert_eq!(lines[3], "Caused by:"); - assert_eq!(lines[4], format!("{:?}", os_notfound_error)); - } else { - panic!(); - } -} diff --git a/tests/test_iter.rs b/tests/test_iter.rs index 9023078..f5f0e58 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -1,4 +1,4 @@ -use chainerror::Context; +use chainerror::prelude::v1::*; use std::error::Error; use std::io;