This commit is contained in:
haraldh 2020-09-01 21:09:36 +00:00
parent 10db06b469
commit 4d8e0bf76f
16 changed files with 2168 additions and 4978 deletions

View file

@ -196,13 +196,96 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
</span><span class="boring">//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your
</span><span class="boring">//! binaries, you still have the error backtrace.
</span><span class="boring">//!
</span><span class="boring">//! `chainerror` has no dependencies!
</span><span class="boring">//! Having nested function returning errors, the output doesn't tell where the error originates from.
</span><span class="boring">//!
</span><span class="boring">//! ```rust
</span><span class="boring">//! use std::path::PathBuf;
</span><span class="boring">//!
</span><span class="boring">//! type BoxedError = Box&lt;dyn std::error::Error + Send + Sync&gt;;
</span><span class="boring">//! fn read_config_file(path: PathBuf) -&gt; Result&lt;(), BoxedError&gt; {
</span><span class="boring">//! // do stuff, return other errors
</span><span class="boring">//! let _buf = std::fs::read_to_string(&amp;path)?;
</span><span class="boring">//! // do stuff, return other errors
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! fn process_config_file() -&gt; Result&lt;(), BoxedError&gt; {
</span><span class="boring">//! // do stuff, return other errors
</span><span class="boring">//! let _buf = read_config_file(&quot;foo.txt&quot;.into())?;
</span><span class="boring">//! // do stuff, return other errors
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! fn main() {
</span><span class="boring">//! if let Err(e) = process_config_file() {
</span><span class="boring">//! eprintln!(&quot;Error:\n{:?}&quot;, e);
</span><span class="boring">//! }
</span><span class="boring">//! }
</span><span class="boring">//! ```
</span><span class="boring">//!
</span><span class="boring">//! This gives the output:
</span><span class="boring">//! ```console
</span><span class="boring">//! Error:
</span><span class="boring">//! Os { code: 2, kind: NotFound, message: &quot;No such file or directory&quot; }
</span><span class="boring">//! ```
</span><span class="boring">//! and you have no idea where it comes from.
</span><span class="boring">//!
</span><span class="boring">//!
</span><span class="boring">//! With `chainerror`, you can supply a context and get a nice error backtrace:
</span><span class="boring">//!
</span><span class="boring">//! ```rust
</span><span class="boring">//! use chainerror::prelude::v1::*;
</span><span class="boring">//! use std::path::PathBuf;
</span><span class="boring">//!
</span><span class="boring">//! type BoxedError = Box&lt;dyn std::error::Error + Send + Sync&gt;;
</span><span class="boring">//! fn read_config_file(path: PathBuf) -&gt; Result&lt;(), BoxedError&gt; {
</span><span class="boring">//! // do stuff, return other errors
</span><span class="boring">//! let _buf = std::fs::read_to_string(&amp;path).context(format!(&quot;Reading file: {:?}&quot;, &amp;path))?;
</span><span class="boring">//! // do stuff, return other errors
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! fn process_config_file() -&gt; Result&lt;(), BoxedError&gt; {
</span><span class="boring">//! // do stuff, return other errors
</span><span class="boring">//! let _buf = read_config_file(&quot;foo.txt&quot;.into()).context(&quot;read the config file&quot;)?;
</span><span class="boring">//! // do stuff, return other errors
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! fn main() {
</span><span class="boring">//! if let Err(e) = process_config_file() {
</span><span class="boring">//! eprintln!(&quot;Error:\n{:?}&quot;, e);
</span><span class="boring">//! # assert_eq!(
</span><span class="boring">//! # format!(&quot;{:?}\n&quot;, e),
</span><span class="boring">//! # &quot;\
</span><span class="boring">//! # src/lib.rs:16:51: read the config file\n\
</span><span class="boring">//! # Caused by:\n\
</span><span class="boring">//! # src/lib.rs:9:47: Reading file: \&quot;foo.txt\&quot;\n\
</span><span class="boring">//! # Caused by:\n\
</span><span class="boring">//! # Os { code: 2, kind: NotFound, message: \&quot;No such file or directory\&quot; }\n\
</span><span class="boring">//! # &quot;,
</span><span class="boring">//! # );
</span><span class="boring">//! }
</span><span class="boring">//! }
</span><span class="boring">//! ```
</span><span class="boring">//!
</span><span class="boring">//! with the output:
</span><span class="boring">//! ```console
</span><span class="boring">//! Error:
</span><span class="boring">//! examples/simple.rs:14:51: read the config file
</span><span class="boring">//! Caused by:
</span><span class="boring">//! examples/simple.rs:7:47: Reading file: &quot;foo.txt&quot;
</span><span class="boring">//! Caused by:
</span><span class="boring">//! Os { code: 2, kind: NotFound, message: &quot;No such file or directory&quot; }
</span><span class="boring">//! ```
</span><span class="boring">//!
</span><span class="boring">//! `chainerror` uses `.source()` of `std::error::Error` along with `#[track_caller]` and `Location` to provide a nice debug error backtrace.
</span><span class="boring">//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally.
</span><span class="boring">//!
</span><span class="boring">//! Along with the `ChainError&lt;T&gt;` struct, `chainerror` comes with some useful helper macros to save a lot of typing.
</span><span class="boring">//!
</span><span class="boring">//! `chainerror` has no dependencies!
</span><span class="boring">//!
</span><span class="boring">//! Debug information is worth it!
</span><span class="boring">//!
</span><span class="boring">//! ## Features
@ -213,200 +296,10 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</span><span class="boring">//! # Tutorial
</span><span class="boring">//!
</span><span class="boring">//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
</span><span class="boring">//!
</span><span class="boring">//! # Examples
</span><span class="boring">//!
</span><span class="boring">//! examples/example.rs:
</span><span class="boring">//! ```rust,ignore
</span><span class="boring">//! // […]
</span><span class="boring">//! fn main() {
</span><span class="boring">//! if let Err(e) = func1() {
</span><span class="boring">//! eprintln!(&quot;\nDebug Error {{:?}}:\n{:?}&quot;, e);
</span><span class="boring">//! eprintln!(&quot;\nAlternative Debug Error {{:#?}}:\n{:#?}\n&quot;, e);
</span><span class="boring">//! // […]
</span><span class="boring">//! }
</span><span class="boring">//! }
</span><span class="boring">//! ```
</span><span class="boring">//!
</span><span class="boring">//! ```console
</span><span class="boring">//! $ cargo run -q --example example
</span><span class="boring">//! Debug Error {:?}:
</span><span class="boring">//! examples/example.rs:46:13: func1 error calling func2
</span><span class="boring">//! Caused by:
</span><span class="boring">//! examples/example.rs:21:13: Func2Error(func2 error: calling func3)
</span><span class="boring">//! Caused by:
</span><span class="boring">//! examples/example.rs:14:18: Error reading 'foo.txt'
</span><span class="boring">//! Caused by:
</span><span class="boring">//! Kind(NotFound)
</span><span class="boring">//!
</span><span class="boring">//! Alternative Debug Error {:#?}:
</span><span class="boring">//! ChainError&lt;example::Func1Error&gt; {
</span><span class="boring">//! occurrence: Some(
</span><span class="boring">//! &quot;examples/example.rs:46:13&quot;,
</span><span class="boring">//! ),
</span><span class="boring">//! kind: func1 error calling func2,
</span><span class="boring">//! source: Some(
</span><span class="boring">//! ChainError&lt;example::Func2Error&gt; {
</span><span class="boring">//! occurrence: Some(
</span><span class="boring">//! &quot;examples/example.rs:21:13&quot;,
</span><span class="boring">//! ),
</span><span class="boring">//! kind: Func2Error(func2 error: calling func3),
</span><span class="boring">//! source: Some(
</span><span class="boring">//! ChainError&lt;alloc::string::String&gt; {
</span><span class="boring">//! occurrence: Some(
</span><span class="boring">//! &quot;examples/example.rs:14:18&quot;,
</span><span class="boring">//! ),
</span><span class="boring">//! kind: &quot;Error reading \'foo.txt\'&quot;,
</span><span class="boring">//! source: Some(
</span><span class="boring">//! Kind(
</span><span class="boring">//! NotFound,
</span><span class="boring">//! ),
</span><span class="boring">//! ),
</span><span class="boring">//! },
</span><span class="boring">//! ),
</span><span class="boring">//! },
</span><span class="boring">//! ),
</span><span class="boring">//! }
</span><span class="boring">//! ```
</span><span class="boring">//!
</span><span class="boring">//! ```rust
</span><span class="boring">//! use chainerror::prelude::v1::*;
</span><span class="boring">//! use std::error::Error;
</span><span class="boring">//! use std::io;
</span><span class="boring">//! use std::result::Result;
</span><span class="boring">//!
</span><span class="boring">//! fn do_some_io() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</span><span class="boring">//! Err(io::Error::from(io::ErrorKind::NotFound))?;
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! fn func2() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</span><span class="boring">//! let filename = &quot;foo.txt&quot;;
</span><span class="boring">//! do_some_io().context(format!(&quot;Error reading '{}'&quot;, filename))?;
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! fn func1() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</span><span class="boring">//! func2().context(&quot;func1 error&quot;)?;
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! if let Err(e) = func1() {
</span><span class="boring">//! #[cfg(not(windows))]
</span><span class="boring">//! assert_eq!(
</span><span class="boring">//! format!(&quot;\n{:?}\n&quot;, e),
</span><span class="boring">//! r#&quot;
</span><span class="boring">//! src/lib.rs:21:13: func1 error
</span><span class="boring">//! Caused by:
</span><span class="boring">//! src/lib.rs:16:18: Error reading 'foo.txt'
</span><span class="boring">//! Caused by:
</span><span class="boring">//! Kind(NotFound)
</span><span class="boring">//! &quot;#
</span><span class="boring">//! );
</span><span class="boring">//! }
</span><span class="boring">//! # else {
</span><span class="boring">//! # unreachable!();
</span><span class="boring">//! # }
</span><span class="boring">//! ```
</span><span class="boring">//!
</span><span class="boring">//!
</span><span class="boring">//! ```rust
</span><span class="boring">//! use chainerror::prelude::v1::*;
</span><span class="boring">//! use std::error::Error;
</span><span class="boring">//! use std::io;
</span><span class="boring">//! use std::result::Result;
</span><span class="boring">//!
</span><span class="boring">//! fn do_some_io() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</span><span class="boring">//! Err(io::Error::from(io::ErrorKind::NotFound))?;
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! fn func3() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</span><span class="boring">//! let filename = &quot;foo.txt&quot;;
</span><span class="boring">//! do_some_io().context(format!(&quot;Error reading '{}'&quot;, filename))?;
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! derive_str_context!(Func2Error);
</span><span class="boring">//!
</span><span class="boring">//! fn func2() -&gt; ChainResult&lt;(), Func2Error&gt; {
</span><span class="boring">//! func3().context(Func2Error(&quot;func2 error: calling func3&quot;.into()))?;
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! enum Func1Error {
</span><span class="boring">//! Func2,
</span><span class="boring">//! IO(String),
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! impl ::std::fmt::Display for Func1Error {
</span><span class="boring">//! fn fmt(&amp;self, f: &amp;mut ::std::fmt::Formatter) -&gt; ::std::fmt::Result {
</span><span class="boring">//! match self {
</span><span class="boring">//! Func1Error::Func2 =&gt; write!(f, &quot;func1 error calling func2&quot;),
</span><span class="boring">//! Func1Error::IO(filename) =&gt; write!(f, &quot;Error reading '{}'&quot;, filename),
</span><span class="boring">//! }
</span><span class="boring">//! }
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! impl ::std::fmt::Debug for Func1Error {
</span><span class="boring">//! fn fmt(&amp;self, f: &amp;mut ::std::fmt::Formatter) -&gt; ::std::fmt::Result {
</span><span class="boring">//! write!(f, &quot;{}&quot;, self)
</span><span class="boring">//! }
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! fn func1() -&gt; ChainResult&lt;(), Func1Error&gt; {
</span><span class="boring">//! func2().context(Func1Error::Func2)?;
</span><span class="boring">//! let filename = String::from(&quot;bar.txt&quot;);
</span><span class="boring">//! do_some_io().context(Func1Error::IO(filename))?;
</span><span class="boring">//! Ok(())
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! if let Err(e) = func1() {
</span><span class="boring">//! assert!(match e.kind() {
</span><span class="boring">//! Func1Error::Func2 =&gt; {
</span><span class="boring">//! eprintln!(&quot;Main Error Report: func1 error calling func2&quot;);
</span><span class="boring">//! true
</span><span class="boring">//! }
</span><span class="boring">//! Func1Error::IO(filename) =&gt; {
</span><span class="boring">//! eprintln!(&quot;Main Error Report: func1 error reading '{}'&quot;, filename);
</span><span class="boring">//! false
</span><span class="boring">//! }
</span><span class="boring">//! });
</span><span class="boring">//!
</span><span class="boring">//! assert!(e.find_chain_cause::&lt;Func2Error&gt;().is_some());
</span><span class="boring">//!
</span><span class="boring">//! if let Some(e) = e.find_chain_cause::&lt;Func2Error&gt;() {
</span><span class="boring">//! eprintln!(&quot;\nError reported by Func2Error: {}&quot;, e)
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! assert!(e.root_cause().is_some());
</span><span class="boring">//!
</span><span class="boring">//! if let Some(e) = e.root_cause() {
</span><span class="boring">//! let io_error = e.downcast_ref::&lt;io::Error&gt;().unwrap();
</span><span class="boring">//! eprintln!(&quot;\nThe root cause was: std::io::Error: {:#?}&quot;, io_error);
</span><span class="boring">//! }
</span><span class="boring">//!
</span><span class="boring">//! #[cfg(not(windows))]
</span><span class="boring">//! assert_eq!(
</span><span class="boring">//! format!(&quot;\n{:?}\n&quot;, e),
</span><span class="boring">//! r#&quot;
</span><span class="boring">//! src/lib.rs:48:13: func1 error calling func2
</span><span class="boring">//! Caused by:
</span><span class="boring">//! src/lib.rs:23:13: Func2Error(func2 error: calling func3)
</span><span class="boring">//! Caused by:
</span><span class="boring">//! src/lib.rs:16:18: Error reading 'foo.txt'
</span><span class="boring">//! Caused by:
</span><span class="boring">//! Kind(NotFound)
</span><span class="boring">//! &quot;#
</span><span class="boring">//! );
</span><span class="boring">//! }
</span><span class="boring">//! # else {
</span><span class="boring">//! # unreachable!();
</span><span class="boring">//! # }
</span><span class="boring">//! ```
</span><span class="boring">
</span><span class="boring">#![deny(clippy::all)]
</span><span class="boring">#![deny(clippy::integer_arithmetic)]
</span><span class="boring">#![allow(clippy::needless_doctest_main)]
</span><span class="boring">#![deny(missing_docs)]
</span><span class="boring">
</span><span class="boring">use std::any::TypeId;