1
0
Fork 0
mirror of https://github.com/haraldh/chainerror.git synced 2025-02-23 00:34:45 +01:00
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

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

3571
print.html

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -233,13 +233,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -250,200 +333,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;

View file

@ -246,13 +246,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -263,200 +346,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;

View file

@ -244,13 +244,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -261,200 +344,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;

View file

@ -170,13 +170,96 @@ have to change much or anything.</p>
</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
@ -187,200 +270,10 @@ have to change much or anything.</p>
</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;

View file

@ -196,13 +196,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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;

View file

@ -194,13 +194,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -211,200 +294,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;

View file

@ -195,13 +195,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -212,200 +295,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;

View file

@ -200,13 +200,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -217,200 +300,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;

View file

@ -212,13 +212,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -229,200 +312,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;

View file

@ -224,13 +224,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -241,200 +324,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;

View file

@ -221,13 +221,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -238,200 +321,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;

View file

@ -224,13 +224,96 @@ fn main() -&gt; Result&lt;(), Box&lt;dyn Error + Send + Sync&gt;&gt; {
</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
@ -241,200 +324,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;