mirror of
https://github.com/haraldh/chainerror.git
synced 2025-06-07 00:24:42 +02:00
deploy: 1003671be3
This commit is contained in:
parent
10db06b469
commit
4d8e0bf76f
16 changed files with 2168 additions and 4978 deletions
247
index.html
247
index.html
|
@ -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<dyn std::error::Error + Send + Sync>;
|
||||
fn read_config_file(path: PathBuf) -> Result<(), BoxedError> {
|
||||
// do stuff, return other errors
|
||||
let _buf = std::fs::read_to_string(&path)?;
|
||||
// do stuff, return other errors
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_config_file() -> Result<(), BoxedError> {
|
||||
// do stuff, return other errors
|
||||
let _buf = read_config_file("foo.txt".into())?;
|
||||
// do stuff, return other errors
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = process_config_file() {
|
||||
eprintln!("Error:\n{:?}", e);
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>This gives the output:</p>
|
||||
<pre><code class="language-console">Error:
|
||||
Os { code: 2, kind: NotFound, message: "No such file or directory" }
|
||||
</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<dyn std::error::Error + Send + Sync>;
|
||||
fn read_config_file(path: PathBuf) -> Result<(), BoxedError> {
|
||||
// do stuff, return other errors
|
||||
let _buf = std::fs::read_to_string(&path).context(format!("Reading file: {:?}", &path))?;
|
||||
// do stuff, return other errors
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_config_file() -> Result<(), BoxedError> {
|
||||
// do stuff, return other errors
|
||||
let _buf = read_config_file("foo.txt".into()).context("read the config file")?;
|
||||
// do stuff, return other errors
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = process_config_file() {
|
||||
eprintln!("Error:\n{:?}", e);
|
||||
}
|
||||
}
|
||||
</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: "foo.txt"
|
||||
Caused by:
|
||||
Os { code: 2, kind: NotFound, message: "No such file or directory" }
|
||||
</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<T></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!("\nDebug Error {{:?}}:\n{:?}", e);
|
||||
eprintln!("\nAlternative Debug Error {{:#?}}:\n{:#?}\n", 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<example::Func1Error> {
|
||||
occurrence: Some(
|
||||
"examples/example.rs:46:13",
|
||||
),
|
||||
kind: func1 error calling func2,
|
||||
source: Some(
|
||||
ChainError<example::Func2Error> {
|
||||
occurrence: Some(
|
||||
"examples/example.rs:21:13",
|
||||
),
|
||||
kind: Func2Error(func2 error: calling func3),
|
||||
source: Some(
|
||||
ChainError<alloc::string::String> {
|
||||
occurrence: Some(
|
||||
"examples/example.rs:14:18",
|
||||
),
|
||||
kind: "Error reading \'foo.txt\'",
|
||||
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() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let filename = "foo.txt";
|
||||
do_some_io().context(format!("Error reading '{}'", filename))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
func2().context("func1 error")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
if let Err(e) = func1() {
|
||||
#[cfg(not(windows))]
|
||||
assert_eq!(
|
||||
format!("\n{:?}\n", e),
|
||||
r#"
|
||||
src/lib.rs:21:13: func1 error
|
||||
Caused by:
|
||||
src/lib.rs:16:18: Error reading 'foo.txt'
|
||||
Caused by:
|
||||
Kind(NotFound)
|
||||
"#
|
||||
);
|
||||
}
|
||||
<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() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn func3() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let filename = "foo.txt";
|
||||
do_some_io().context(format!("Error reading '{}'", filename))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
derive_str_context!(Func2Error);
|
||||
|
||||
fn func2() -> ChainResult<(), Func2Error> {
|
||||
func3().context(Func2Error("func2 error: calling func3".into()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
enum Func1Error {
|
||||
Func2,
|
||||
IO(String),
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Func1Error {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
match self {
|
||||
Func1Error::Func2 => write!(f, "func1 error calling func2"),
|
||||
Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for Func1Error {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
fn func1() -> ChainResult<(), Func1Error> {
|
||||
func2().context(Func1Error::Func2)?;
|
||||
let filename = String::from("bar.txt");
|
||||
do_some_io().context(Func1Error::IO(filename))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
if let Err(e) = func1() {
|
||||
assert!(match e.kind() {
|
||||
Func1Error::Func2 => {
|
||||
eprintln!("Main Error Report: func1 error calling func2");
|
||||
true
|
||||
}
|
||||
Func1Error::IO(filename) => {
|
||||
eprintln!("Main Error Report: func1 error reading '{}'", filename);
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
assert!(e.find_chain_cause::<Func2Error>().is_some());
|
||||
|
||||
if let Some(e) = e.find_chain_cause::<Func2Error>() {
|
||||
eprintln!("\nError reported by Func2Error: {}", e)
|
||||
}
|
||||
|
||||
assert!(e.root_cause().is_some());
|
||||
|
||||
if let Some(e) = e.root_cause() {
|
||||
let io_error = e.downcast_ref::<io::Error>().unwrap();
|
||||
eprintln!("\nThe root cause was: std::io::Error: {:#?}", io_error);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
assert_eq!(
|
||||
format!("\n{:?}\n", e),
|
||||
r#"
|
||||
src/lib.rs:48:13: func1 error calling func2
|
||||
Caused by:
|
||||
src/lib.rs:23:13: Func2Error(func2 error: calling func3)
|
||||
Caused by:
|
||||
src/lib.rs:16:18: Error reading 'foo.txt'
|
||||
Caused by:
|
||||
Kind(NotFound)
|
||||
"#
|
||||
);
|
||||
}
|
||||
<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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue