diff --git a/book.js b/book.js
index 8c6946d..186f9ae 100644
--- a/book.js
+++ b/book.js
@@ -16,9 +16,6 @@ function playpen_text(playpen) {
}
(function codeSnippets() {
- // Hide Rust code lines prepended with a specific character
- var hiding_character = "#";
-
function fetch_with_timeout(url, options, timeout = 6000) {
return Promise.race([
fetch(url, options),
@@ -55,6 +52,15 @@ function playpen_text(playpen) {
editor.addEventListener("change", function (e) {
update_play_button(playpen_block, playground_crates);
});
+ // add Ctrl-Enter command to execute rust code
+ editor.commands.addCommand({
+ name: "run",
+ bindKey: {
+ win: "Ctrl-Enter",
+ mac: "Ctrl-Enter"
+ },
+ exec: _editor => run_rust_code(playpen_block)
+ });
}
}
}
@@ -101,31 +107,24 @@ function playpen_text(playpen) {
}
let text = playpen_text(code_block);
+ let classes = code_block.querySelector('code').classList;
+ let has_2018 = classes.contains("edition2018");
+ let edition = has_2018 ? "2018" : "2015";
var params = {
- channel: "stable",
- mode: "debug",
- edition: "2018",
- crateType: "bin",
- tests: false,
- code: text
+ version: "stable",
+ optimize: "0",
+ code: text,
+ edition: edition
};
if (text.indexOf("#![feature") !== -1) {
- params.channel = "nightly";
- }
-
- if (text.indexOf("#[test]") !== -1) {
- params.tests = "true";
- }
-
- if (text.indexOf("#![crate_type=\"lib\"]") !== -1) {
- params.crateType = "lib";
+ params.version = "nightly";
}
result_block.innerText = "Running...";
- fetch_with_timeout("https://play.rust-lang.org/execute", {
+ fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
headers: {
'Content-Type': "application/json",
},
@@ -134,7 +133,7 @@ function playpen_text(playpen) {
body: JSON.stringify(params)
})
.then(response => response.json())
- .then(response => result_block.innerText = response.stderr + "\n\n" + response.stdout)
+ .then(response => result_block.innerText = response.result)
.catch(error => result_block.innerText = "Playground Communication: " + error.message);
}
@@ -168,95 +167,59 @@ function playpen_text(playpen) {
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
- var code_block = block;
- var pre_block = block.parentNode;
- // hide lines
- var lines = code_block.innerHTML.split("\n");
- var first_non_hidden_line = false;
- var lines_hidden = false;
- var trimmed_line = "";
-
- for (var n = 0; n < lines.length; n++) {
- trimmed_line = lines[n].trim();
- if (trimmed_line[0] == hiding_character && trimmed_line[1] != hiding_character) {
- if (first_non_hidden_line) {
- lines[n] = "" + "\n" + lines[n].replace(/(\s*)# ?/, "$1") + "";
- }
- else {
- lines[n] = "" + lines[n].replace(/(\s*)# ?/, "$1") + "\n" + "";
- }
- lines_hidden = true;
- }
- else if (first_non_hidden_line) {
- lines[n] = "\n" + lines[n];
- }
- else {
- first_non_hidden_line = true;
- }
- if (trimmed_line[0] == hiding_character && trimmed_line[1] == hiding_character) {
- lines[n] = lines[n].replace("##", "#")
- }
- }
- code_block.innerHTML = lines.join("");
-
+ var lines = Array.from(block.querySelectorAll('.boring'));
// If no lines were hidden, return
- if (!lines_hidden) { return; }
+ if (!lines.length) { return; }
+ block.classList.add("hide-boring");
var buttons = document.createElement('div');
buttons.className = 'buttons';
buttons.innerHTML = "";
// add expand button
+ var pre_block = block.parentNode;
pre_block.insertBefore(buttons, pre_block.firstChild);
pre_block.querySelector('.buttons').addEventListener('click', function (e) {
if (e.target.classList.contains('fa-expand')) {
- var lines = pre_block.querySelectorAll('span.hidden');
-
e.target.classList.remove('fa-expand');
e.target.classList.add('fa-compress');
e.target.title = 'Hide lines';
e.target.setAttribute('aria-label', e.target.title);
- Array.from(lines).forEach(function (line) {
- line.classList.remove('hidden');
- line.classList.add('unhidden');
- });
+ block.classList.remove('hide-boring');
} else if (e.target.classList.contains('fa-compress')) {
- var lines = pre_block.querySelectorAll('span.unhidden');
-
e.target.classList.remove('fa-compress');
e.target.classList.add('fa-expand');
e.target.title = 'Show hidden lines';
e.target.setAttribute('aria-label', e.target.title);
- Array.from(lines).forEach(function (line) {
- line.classList.remove('unhidden');
- line.classList.add('hidden');
- });
+ block.classList.add('hide-boring');
}
});
});
- Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
- var pre_block = block.parentNode;
- if (!pre_block.classList.contains('playpen')) {
- var buttons = pre_block.querySelector(".buttons");
- if (!buttons) {
- buttons = document.createElement('div');
- buttons.className = 'buttons';
- pre_block.insertBefore(buttons, pre_block.firstChild);
+ if (window.playpen_copyable) {
+ Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
+ var pre_block = block.parentNode;
+ if (!pre_block.classList.contains('playpen')) {
+ var buttons = pre_block.querySelector(".buttons");
+ if (!buttons) {
+ buttons = document.createElement('div');
+ buttons.className = 'buttons';
+ pre_block.insertBefore(buttons, pre_block.firstChild);
+ }
+
+ var clipButton = document.createElement('button');
+ clipButton.className = 'fa fa-copy clip-button';
+ clipButton.title = 'Copy to clipboard';
+ clipButton.setAttribute('aria-label', clipButton.title);
+ clipButton.innerHTML = '';
+
+ buttons.insertBefore(clipButton, buttons.firstChild);
}
-
- var clipButton = document.createElement('button');
- clipButton.className = 'fa fa-copy clip-button';
- clipButton.title = 'Copy to clipboard';
- clipButton.setAttribute('aria-label', clipButton.title);
- clipButton.innerHTML = '';
-
- buttons.insertBefore(clipButton, buttons.firstChild);
- }
- });
+ });
+ }
// Process playpen code blocks
Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) {
@@ -274,19 +237,21 @@ function playpen_text(playpen) {
runCodeButton.title = 'Run this code';
runCodeButton.setAttribute('aria-label', runCodeButton.title);
- var copyCodeClipboardButton = document.createElement('button');
- copyCodeClipboardButton.className = 'fa fa-copy clip-button';
- copyCodeClipboardButton.innerHTML = '';
- copyCodeClipboardButton.title = 'Copy to clipboard';
- copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
-
buttons.insertBefore(runCodeButton, buttons.firstChild);
- buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
-
runCodeButton.addEventListener('click', function (e) {
run_rust_code(pre_block);
});
+ if (window.playpen_copyable) {
+ var copyCodeClipboardButton = document.createElement('button');
+ copyCodeClipboardButton.className = 'fa fa-copy clip-button';
+ copyCodeClipboardButton.innerHTML = '';
+ copyCodeClipboardButton.title = 'Copy to clipboard';
+ copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
+
+ buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
+ }
+
let code_block = pre_block.querySelector("code");
if (window.ace && code_block.classList.contains("editable")) {
var undoChangesButton = document.createElement('button');
@@ -328,7 +293,7 @@ function playpen_text(playpen) {
themeToggleButton.focus();
}
- function set_theme(theme) {
+ function set_theme(theme, store = true) {
let ace_theme;
if (theme == 'coal' || theme == 'navy') {
@@ -341,13 +306,11 @@ function playpen_text(playpen) {
stylesheets.ayuHighlight.disabled = false;
stylesheets.tomorrowNight.disabled = true;
stylesheets.highlight.disabled = true;
-
ace_theme = "ace/theme/tomorrow_night";
} else {
stylesheets.ayuHighlight.disabled = true;
stylesheets.tomorrowNight.disabled = true;
stylesheets.highlight.disabled = false;
-
ace_theme = "ace/theme/dawn";
}
@@ -365,9 +328,10 @@ function playpen_text(playpen) {
try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { }
if (previousTheme === null || previousTheme === undefined) { previousTheme = default_theme; }
- try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
+ if (store) {
+ try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
+ }
- document.body.className = theme;
html.classList.remove(previousTheme);
html.classList.add(theme);
}
@@ -377,7 +341,7 @@ function playpen_text(playpen) {
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
- set_theme(theme);
+ set_theme(theme, false);
themeToggleButton.addEventListener('click', function () {
if (themePopup.style.display === 'block') {
@@ -399,7 +363,7 @@ function playpen_text(playpen) {
}
});
- // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang-nursery/mdBook/issues/628
+ // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
document.addEventListener('click', function(e) {
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
hideThemes();
@@ -444,8 +408,10 @@ function playpen_text(playpen) {
(function sidebar() {
var html = document.querySelector("html");
var sidebar = document.getElementById("sidebar");
+ var sidebarScrollBox = document.getElementById("sidebar-scrollbox");
var sidebarLinks = document.querySelectorAll('#sidebar a');
var sidebarToggleButton = document.getElementById("sidebar-toggle");
+ var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
var firstContact = null;
function showSidebar() {
@@ -459,6 +425,17 @@ function playpen_text(playpen) {
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
}
+
+ var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
+
+ function toggleSection(ev) {
+ ev.currentTarget.parentElement.classList.toggle('expanded');
+ }
+
+ Array.from(sidebarAnchorToggles).forEach(function (el) {
+ el.addEventListener('click', toggleSection);
+ });
+
function hideSidebar() {
html.classList.remove('sidebar-visible')
html.classList.add('sidebar-hidden');
@@ -485,6 +462,23 @@ function playpen_text(playpen) {
}
});
+ sidebarResizeHandle.addEventListener('mousedown', initResize, false);
+
+ function initResize(e) {
+ window.addEventListener('mousemove', resize, false);
+ window.addEventListener('mouseup', stopResize, false);
+ html.classList.add('sidebar-resizing');
+ }
+ function resize(e) {
+ document.documentElement.style.setProperty('--sidebar-width', (e.clientX - sidebar.offsetLeft) + 'px');
+ }
+ //on mouseup remove windows functions mousemove & mouseup
+ function stopResize(e) {
+ html.classList.remove('sidebar-resizing');
+ window.removeEventListener('mousemove', resize, false);
+ window.removeEventListener('mouseup', stopResize, false);
+ }
+
document.addEventListener('touchstart', function (e) {
firstContact = {
x: e.touches[0].clientX,
@@ -513,7 +507,7 @@ function playpen_text(playpen) {
// Scroll sidebar to current active section
var activeSection = sidebar.querySelector(".active");
if (activeSection) {
- sidebar.scrollTop = activeSection.offsetTop;
+ sidebarScrollBox.scrollTop = activeSection.offsetTop;
}
})();
@@ -554,7 +548,7 @@ function playpen_text(playpen) {
elem.className = 'fa fa-copy tooltipped';
}
- var clipboardSnippets = new Clipboard('.clip-button', {
+ var clipboardSnippets = new ClipboardJS('.clip-button', {
text: function (trigger) {
hideTooltip(trigger);
let playpen = trigger.closest("pre");
@@ -606,6 +600,6 @@ function playpen_text(playpen) {
menu.classList.remove('bordered');
}
- previousScrollTop = document.scrollingElement.scrollTop;
+ previousScrollTop = Math.max(document.scrollingElement.scrollTop, 0);
}, { passive: true });
})();
diff --git a/print.html b/print.html
index e8b8f62..9235fd8 100644
--- a/print.html
+++ b/print.html
@@ -275,32 +275,32 @@ prints out the last Error
.
You can also run the tutorial examples in the checked out chainerror git repo.
-$ cargo run -q --example tutorial1
+$ cargo run -q --example tutorial1
use std::error::Error;
use std::io;
use std::result::Result;
-fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
+fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
-fn func2() -> Result<(), Box<Error + Send + Sync>> {
+fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(_) = do_some_io() {
Err("func2 error")?;
}
Ok(())
}
-fn func1() -> Result<(), Box<Error + Send + Sync>> {
+fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(_) = func2() {
Err("func1 error")?;
}
Ok(())
}
-fn main() -> Result<(), Box<Error + Send + Sync>> {
+fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
func1()
}
@@ -314,32 +314,1222 @@ use std::error::Error;
use std::io;
use std::result::Result;
-fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
+fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
-fn func2() -> Result<(), Box<Error + Send + Sync>> {
+fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = do_some_io() {
Err(cherr!(e, "func2 error"))?;
}
Ok(())
}
-fn func1() -> Result<(), Box<Error + Send + Sync>> {
+fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func2() {
Err(cherr!(e, "func1 error"))?;
}
Ok(())
}
-fn main() -> Result<(), Box<Error + Send + Sync>> {
+fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
func1()
}
#[allow(dead_code)]
mod chainerror {
-{{#includecomment ../src/lib.rs}}
-}
+//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your
+//! binaries, you still have the error backtrace.
+//!
+//! `chainerror` has no dependencies!
+//!
+//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace.
+//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally.
+//!
+//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing.
+//!
+//! ## Features
+//!
+//! `no-fileline`
+//! : completely turn off storing filename and line
+//!
+//! `display-cause`
+//! : turn on printing a backtrace of the errors in `Display`
+//!
+//! `no-debug-cause`
+//! : turn off printing a backtrace of the errors in `Debug`
+//!
+//!
+//! # Tutorial
+//!
+//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
+//!
+//! # Examples
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+//! func2().map_err(mstrerr!("func1 error"))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! if let Err(e) = func1() {
+//! #[cfg(not(windows))]
+//! assert_eq!(
+//! format!("\n{:?}\n", e), r#"
+//! src/lib.rs:20: func1 error
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+//!
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! derive_str_cherr!(Func2Error);
+//!
+//! fn func2() -> ChainResult<(), Func2Error> {
+//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?;
+//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?;
+//! let filename = String::from("bar.txt");
+//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! 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:47: func1 error calling func2
+//! Caused by:
+//! src/lib.rs:22: Func2Error(func2 error: calling func3)
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+
+#![deny(
+ warnings,
+ absolute_paths_not_starting_with_crate,
+ deprecated_in_future,
+ keyword_idents,
+ macro_use_extern_crate,
+ missing_debug_implementations,
+ trivial_numeric_casts,
+ unused_extern_crates,
+ unused_import_braces,
+ unused_qualifications,
+ unused_results,
+ unused_labels,
+ unused_lifetimes,
+ unstable_features,
+ unreachable_pub,
+ future_incompatible,
+ missing_copy_implementations,
+ missing_doc_code_examples,
+ rust_2018_idioms,
+ rust_2018_compatibility
+)]
+
+use std::any::TypeId;
+use std::error::Error;
+use std::fmt::{Debug, Display, Formatter, Result};
+
+/// chains an inner error kind `T` with a causing error
+pub struct ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ occurrence: Option<&'static str>,
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+}
+
+/// convenience type alias
+pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>;
+
+impl<T: 'static + Display + Debug> ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ occurrence: Option<&'static str>,
+ ) -> Self {
+ Self {
+ occurrence,
+ kind,
+ error_cause,
+ }
+ }
+
+ #[cfg(feature = "no-fileline")]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ _occurrence: Option<&'static str>,
+ ) -> Self {
+ Self { kind, error_cause }
+ }
+
+ /// return the root cause of the error chain, if any exists
+ pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> {
+ self.iter().last()
+ }
+
+ /// Find the first error cause of type U, if any exists
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func1Error);
+ ///
+ /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+ ///
+ /// assert!(f1err.find_cause::<io::Error>().is_some());
+ ///
+ /// assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+ /// }
+ /// # else {
+ /// # panic!();
+ /// # }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter().filter_map(Error::downcast_ref::<U>).next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>`, if any exists
+ ///
+ /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooError);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooError>>();
+ ///
+ /// // leave out the ChainError<FooError> implementation detail
+ /// err.find_chain_cause::<FooError>();
+ /// ```
+ #[inline]
+ pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> {
+ self.iter()
+ .filter_map(Error::downcast_ref::<ChainError<U>>)
+ .next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U`
+ ///
+ /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooErrorKind);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooErrorKind>>();
+ /// // and/or
+ /// err.find_chain_cause::<FooErrorKind>();
+ /// // and/or
+ /// err.find_cause::<FooErrorKind>();
+ ///
+ /// // leave out the ChainError<FooErrorKind> implementation detail
+ /// err.find_kind_or_cause::<FooErrorKind>();
+ /// ```
+ #[inline]
+ pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter()
+ .filter_map(|e| {
+ e.downcast_ref::<ChainError<U>>()
+ .map(|e| e.kind())
+ .or_else(|| e.downcast_ref::<U>())
+ })
+ .next()
+ }
+
+ /// Return a reference to T of `ChainError<T>`
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// #[derive(Debug)]
+ /// enum Func1ErrorKind {
+ /// Func2,
+ /// IO(String),
+ /// }
+ ///
+ /// /// impl ::std::fmt::Display for Func1ErrorKind {…}
+ /// # impl ::std::fmt::Display for Func1ErrorKind {
+ /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ /// # match self {
+ /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"),
+ /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+ /// # }
+ /// # }
+ /// # }
+ ///
+ /// fn func1() -> ChainResult<(), Func1ErrorKind> {
+ /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?;
+ /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// match e.kind() {
+ /// Func1ErrorKind::Func2 => {}
+ /// Func1ErrorKind::IO(filename) => panic!(),
+ /// }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn kind(&self) -> &T {
+ &self.kind
+ }
+
+ /// Returns an Iterator over all error causes/sources
+ ///
+ /// # Example
+ ///
+ ///
+ #[inline]
+ pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> {
+ ErrorIter {
+ current: Some(self),
+ }
+ }
+}
+
+struct ErrorIter<'a> {
+ current: Option<&'a (dyn Error + 'static)>,
+}
+
+impl<'a> Iterator for ErrorIter<'a> {
+ type Item = &'a (dyn Error + 'static);
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ let current = self.current;
+ self.current = self.current.and_then(Error::source);
+ current
+ }
+}
+
+impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.kind
+ }
+}
+
+/// Convenience trait to hide the `ChainError<T>` implementation internals
+pub trait ChainErrorDown {
+ /// Test if of type `ChainError<T>`
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool;
+ /// Downcast to a reference of `ChainError<T>`
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>;
+ /// Downcast to a mutable reference of `ChainError<T>`
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>;
+ /// Downcast to T of `ChainError<T>`
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>;
+ /// Downcast to T mutable reference of `ChainError<T>`
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>;
+}
+
+impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ TypeId::of::<T>() == TypeId::of::<U>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&*(self as *const dyn Error as *const &ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send + Sync {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &mut ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Display for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ write!(f, "{}", self.kind)?;
+
+ #[cfg(feature = "display-cause")]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Display::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<T: 'static + Display + Debug> Debug for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ #[cfg(not(feature = "no-fileline"))]
+ {
+ if let Some(ref o) = self.occurrence {
+ Display::fmt(o, f)?;
+ }
+ }
+
+ if self.is_chain::<String>() {
+ Display::fmt(&self.kind, f)?;
+ } else {
+ Debug::fmt(&self.kind, f)?;
+ }
+
+ #[cfg(not(feature = "no-debug-cause"))]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Debug::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// `ChainErrorFrom<T>` is similar to `From<T>`
+pub trait ChainErrorFrom<T>: Sized {
+ /// similar to From<T>::from()
+ fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>;
+}
+
+/// `IntoChainError<T>` is similar to `Into<T>`
+pub trait IntoChainError<T>: Sized {
+ /// similar to Into<T>::into()
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>;
+}
+
+impl<T, U> IntoChainError<U> for T
+where
+ U: ChainErrorFrom<T>,
+{
+ #[inline]
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> {
+ U::chain_error_from(self, line_filename)
+ }
+}
+
+impl<T, U> ChainErrorFrom<T> for U
+where
+ T: Into<U>,
+ U: 'static + Display + Debug,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ let e: U = t.into();
+ ChainError::new(e, None, line_filename)
+ }
+}
+
+/*
+impl<T, U> ChainErrorFrom<T> for U
+ where
+ T: 'static + Error + Into<Box<T>> + Clone,
+ U: 'static + Display + Debug + From<T>,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename)
+ }
+}
+*/
+
+/// map into `ChainError<T>` with `T::from(err)`
+///
+/// adds `line!()` and `file!()` information
+#[macro_export]
+macro_rules! minto_cherr {
+ ( $k:ident ) => (
+ |e| $crate::cherr!(e, $k::from(&e))
+ );
+ ( $enum:ident $(:: $enum_path:ident)* ) => (
+ |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e))
+ );
+}
+
+/// Creates a new `ChainError<T>`
+///
+/// # Examples
+///
+/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`.
+/// ```rust
+/// # use chainerror::*;
+/// # #[derive(Debug)]
+/// enum FooError {
+/// Bar,
+/// Baz(&'static str),
+/// }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+///
+/// // impl ::std::fmt::Display for FooError
+///
+/// fn do_some_stuff() -> bool {
+/// false
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// if ! do_some_stuff() {
+/// Err(cherr!(FooError::Baz("Error")))?;
+/// }
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+///
+/// Additionally an error cause can be added.
+///
+/// ```rust
+/// # use chainerror::*;
+/// # use std::io;
+/// # use std::error::Error;
+/// # #[derive(Debug)]
+/// # enum FooError {
+/// # Bar,
+/// # Baz(&'static str),
+/// # }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// Err(io::Error::from(io::ErrorKind::NotFound))?;
+/// Ok(())
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// do_some_stuff().map_err(
+/// |e| cherr!(e, FooError::Baz("Error"))
+/// )?;
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! cherr {
+ ( $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( $e:path, $k:expr ) => ({
+ $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `|e| cherr!(e, format!(…))`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!("func1 error"))?;
+/// Ok(())
+/// }
+///
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # #[cfg(not(windows))]
+/// # assert_eq!(
+/// # format!("\n{:?}\n", e), r#"
+/// # src/lib.rs:18: func1 error
+/// # Caused by:
+/// # src/lib.rs:13: Error reading 'foo.txt'
+/// # Caused by:
+/// # Kind(NotFound)
+/// # "#
+/// # );
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+///
+/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with
+/// `derive_str_cherr!(T)`
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! mstrerr {
+ ( $t:path, $msg:expr ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ |e| $crate::cherr!(e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # use std::error::Error;
+/// # use std::result::Result;
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// Err(strerr!(Func2Error, "Error reading '{}'", filename))
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! strerr {
+ ( $t:path, $msg:expr ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ $crate::cherr!(format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! derive_str_cherr {
+ ($e:ident) => {
+ #[derive(Clone)]
+ pub struct $e(pub String);
+ impl ::std::fmt::Display for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+ impl ::std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}({})", stringify!($e), self.0)
+ }
+ }
+ impl ::std::error::Error for $e {}
+ };
+}
+
+/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method
+///
+/// It basically hides `ChainError` to the outside and only exposes the `kind()`
+/// method.
+///
+/// Error::kind() returns the ErrorKind
+/// Error::source() returns the parent error
+///
+/// # Examples
+///
+/// ```rust
+/// use std::io;
+/// use chainerror::*;
+///
+/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
+/// return Err(io::Error::from(io::ErrorKind::NotFound));
+/// }
+///
+/// #[derive(Debug, Clone)]
+/// pub enum ErrorKind {
+/// IO(String),
+/// FatalError(String),
+/// Unknown,
+/// }
+///
+/// derive_err_kind!(Error, ErrorKind);
+///
+/// impl std::fmt::Display for ErrorKind {
+/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
+/// match self {
+/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e),
+/// ErrorKind::Unknown => write!(f, "unknown error"),
+/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+/// }
+/// }
+/// }
+///
+/// impl ErrorKind {
+/// fn from_io_error(e: &io::Error, f: String) -> Self {
+/// match e.kind() {
+/// io::ErrorKind::BrokenPipe => panic!("Should not happen"),
+/// io::ErrorKind::ConnectionReset => {
+/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e))
+/// }
+/// _ => ErrorKind::IO(f),
+/// }
+/// }
+/// }
+///
+/// impl From<&io::Error> for ErrorKind {
+/// fn from(e: &io::Error) -> Self {
+/// ErrorKind::IO(format!("{}", e))
+/// }
+/// }
+///
+/// pub fn func1() -> std::result::Result<(), Error> {
+/// let filename = "bar.txt";
+///
+/// do_some_io(filename)
+/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?;
+/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?;
+/// Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! derive_err_kind {
+ ($e:ident, $k:ident) => {
+ pub struct $e($crate::ChainError<$k>);
+
+ impl $e {
+ pub fn kind(&self) -> &$k {
+ self.0.kind()
+ }
+ }
+
+ impl From<$k> for $e {
+ fn from(e: $k) -> Self {
+ $e($crate::ChainError::new(e, None, None))
+ }
+ }
+
+ impl From<ChainError<$k>> for $e {
+ fn from(e: $crate::ChainError<$k>) -> Self {
+ $e(e)
+ }
+ }
+
+ impl From<&$e> for $k
+ where
+ $k: Clone,
+ {
+ fn from(e: &$e) -> Self {
+ e.kind().clone()
+ }
+ }
+
+ impl $crate::ChainErrorFrom<$e> for $k
+ where
+ $k: Clone,
+ {
+ #[inline]
+ fn chain_error_from(
+ t: $e,
+ line_filename: Option<&'static str>,
+ ) -> $crate::ChainError<$k> {
+ $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename)
+ }
+ }
+
+ impl std::error::Error for $e {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.0.source()
+ }
+ }
+
+ impl std::fmt::Display for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+ }
+
+ impl std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Debug::fmt(&self.0, f)
+ }
+ }
+ };
+}
+}
if let Err(e) = do_some_io() {
@@ -362,22 +1552,22 @@ use std::error::Error;
use std::io;
use std::result::Result;
-fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
+fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
-fn func2() -> Result<(), Box<Error + Send + Sync>> {
+fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
do_some_io().map_err(|e| cherr!(e, "func2 error"))?;
Ok(())
}
-fn func1() -> Result<(), Box<Error + Send + Sync>> {
+fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
func2().map_err(|e| cherr!(e, "func1 error"))?;
Ok(())
}
-fn main() -> Result<(), Box<Error + Send + Sync>> {
+fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func1() {
eprintln!("{:?}", e);
}
@@ -385,8 +1575,1198 @@ fn main() -> Result<(), Box<Error + Send + Sync>> {
}
#[allow(dead_code)]
mod chainerror {
-{{#includecomment ../src/lib.rs}}
-}
+//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your
+//! binaries, you still have the error backtrace.
+//!
+//! `chainerror` has no dependencies!
+//!
+//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace.
+//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally.
+//!
+//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing.
+//!
+//! ## Features
+//!
+//! `no-fileline`
+//! : completely turn off storing filename and line
+//!
+//! `display-cause`
+//! : turn on printing a backtrace of the errors in `Display`
+//!
+//! `no-debug-cause`
+//! : turn off printing a backtrace of the errors in `Debug`
+//!
+//!
+//! # Tutorial
+//!
+//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
+//!
+//! # Examples
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+//! func2().map_err(mstrerr!("func1 error"))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! if let Err(e) = func1() {
+//! #[cfg(not(windows))]
+//! assert_eq!(
+//! format!("\n{:?}\n", e), r#"
+//! src/lib.rs:20: func1 error
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+//!
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! derive_str_cherr!(Func2Error);
+//!
+//! fn func2() -> ChainResult<(), Func2Error> {
+//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?;
+//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?;
+//! let filename = String::from("bar.txt");
+//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! 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:47: func1 error calling func2
+//! Caused by:
+//! src/lib.rs:22: Func2Error(func2 error: calling func3)
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+
+#![deny(
+ warnings,
+ absolute_paths_not_starting_with_crate,
+ deprecated_in_future,
+ keyword_idents,
+ macro_use_extern_crate,
+ missing_debug_implementations,
+ trivial_numeric_casts,
+ unused_extern_crates,
+ unused_import_braces,
+ unused_qualifications,
+ unused_results,
+ unused_labels,
+ unused_lifetimes,
+ unstable_features,
+ unreachable_pub,
+ future_incompatible,
+ missing_copy_implementations,
+ missing_doc_code_examples,
+ rust_2018_idioms,
+ rust_2018_compatibility
+)]
+
+use std::any::TypeId;
+use std::error::Error;
+use std::fmt::{Debug, Display, Formatter, Result};
+
+/// chains an inner error kind `T` with a causing error
+pub struct ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ occurrence: Option<&'static str>,
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+}
+
+/// convenience type alias
+pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>;
+
+impl<T: 'static + Display + Debug> ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ occurrence: Option<&'static str>,
+ ) -> Self {
+ Self {
+ occurrence,
+ kind,
+ error_cause,
+ }
+ }
+
+ #[cfg(feature = "no-fileline")]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ _occurrence: Option<&'static str>,
+ ) -> Self {
+ Self { kind, error_cause }
+ }
+
+ /// return the root cause of the error chain, if any exists
+ pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> {
+ self.iter().last()
+ }
+
+ /// Find the first error cause of type U, if any exists
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func1Error);
+ ///
+ /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+ ///
+ /// assert!(f1err.find_cause::<io::Error>().is_some());
+ ///
+ /// assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+ /// }
+ /// # else {
+ /// # panic!();
+ /// # }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter().filter_map(Error::downcast_ref::<U>).next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>`, if any exists
+ ///
+ /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooError);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooError>>();
+ ///
+ /// // leave out the ChainError<FooError> implementation detail
+ /// err.find_chain_cause::<FooError>();
+ /// ```
+ #[inline]
+ pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> {
+ self.iter()
+ .filter_map(Error::downcast_ref::<ChainError<U>>)
+ .next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U`
+ ///
+ /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooErrorKind);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooErrorKind>>();
+ /// // and/or
+ /// err.find_chain_cause::<FooErrorKind>();
+ /// // and/or
+ /// err.find_cause::<FooErrorKind>();
+ ///
+ /// // leave out the ChainError<FooErrorKind> implementation detail
+ /// err.find_kind_or_cause::<FooErrorKind>();
+ /// ```
+ #[inline]
+ pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter()
+ .filter_map(|e| {
+ e.downcast_ref::<ChainError<U>>()
+ .map(|e| e.kind())
+ .or_else(|| e.downcast_ref::<U>())
+ })
+ .next()
+ }
+
+ /// Return a reference to T of `ChainError<T>`
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// #[derive(Debug)]
+ /// enum Func1ErrorKind {
+ /// Func2,
+ /// IO(String),
+ /// }
+ ///
+ /// /// impl ::std::fmt::Display for Func1ErrorKind {…}
+ /// # impl ::std::fmt::Display for Func1ErrorKind {
+ /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ /// # match self {
+ /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"),
+ /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+ /// # }
+ /// # }
+ /// # }
+ ///
+ /// fn func1() -> ChainResult<(), Func1ErrorKind> {
+ /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?;
+ /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// match e.kind() {
+ /// Func1ErrorKind::Func2 => {}
+ /// Func1ErrorKind::IO(filename) => panic!(),
+ /// }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn kind(&self) -> &T {
+ &self.kind
+ }
+
+ /// Returns an Iterator over all error causes/sources
+ ///
+ /// # Example
+ ///
+ ///
+ #[inline]
+ pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> {
+ ErrorIter {
+ current: Some(self),
+ }
+ }
+}
+
+struct ErrorIter<'a> {
+ current: Option<&'a (dyn Error + 'static)>,
+}
+
+impl<'a> Iterator for ErrorIter<'a> {
+ type Item = &'a (dyn Error + 'static);
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ let current = self.current;
+ self.current = self.current.and_then(Error::source);
+ current
+ }
+}
+
+impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.kind
+ }
+}
+
+/// Convenience trait to hide the `ChainError<T>` implementation internals
+pub trait ChainErrorDown {
+ /// Test if of type `ChainError<T>`
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool;
+ /// Downcast to a reference of `ChainError<T>`
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>;
+ /// Downcast to a mutable reference of `ChainError<T>`
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>;
+ /// Downcast to T of `ChainError<T>`
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>;
+ /// Downcast to T mutable reference of `ChainError<T>`
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>;
+}
+
+impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ TypeId::of::<T>() == TypeId::of::<U>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&*(self as *const dyn Error as *const &ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send + Sync {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &mut ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Display for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ write!(f, "{}", self.kind)?;
+
+ #[cfg(feature = "display-cause")]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Display::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<T: 'static + Display + Debug> Debug for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ #[cfg(not(feature = "no-fileline"))]
+ {
+ if let Some(ref o) = self.occurrence {
+ Display::fmt(o, f)?;
+ }
+ }
+
+ if self.is_chain::<String>() {
+ Display::fmt(&self.kind, f)?;
+ } else {
+ Debug::fmt(&self.kind, f)?;
+ }
+
+ #[cfg(not(feature = "no-debug-cause"))]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Debug::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// `ChainErrorFrom<T>` is similar to `From<T>`
+pub trait ChainErrorFrom<T>: Sized {
+ /// similar to From<T>::from()
+ fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>;
+}
+
+/// `IntoChainError<T>` is similar to `Into<T>`
+pub trait IntoChainError<T>: Sized {
+ /// similar to Into<T>::into()
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>;
+}
+
+impl<T, U> IntoChainError<U> for T
+where
+ U: ChainErrorFrom<T>,
+{
+ #[inline]
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> {
+ U::chain_error_from(self, line_filename)
+ }
+}
+
+impl<T, U> ChainErrorFrom<T> for U
+where
+ T: Into<U>,
+ U: 'static + Display + Debug,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ let e: U = t.into();
+ ChainError::new(e, None, line_filename)
+ }
+}
+
+/*
+impl<T, U> ChainErrorFrom<T> for U
+ where
+ T: 'static + Error + Into<Box<T>> + Clone,
+ U: 'static + Display + Debug + From<T>,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename)
+ }
+}
+*/
+
+/// map into `ChainError<T>` with `T::from(err)`
+///
+/// adds `line!()` and `file!()` information
+#[macro_export]
+macro_rules! minto_cherr {
+ ( $k:ident ) => (
+ |e| $crate::cherr!(e, $k::from(&e))
+ );
+ ( $enum:ident $(:: $enum_path:ident)* ) => (
+ |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e))
+ );
+}
+
+/// Creates a new `ChainError<T>`
+///
+/// # Examples
+///
+/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`.
+/// ```rust
+/// # use chainerror::*;
+/// # #[derive(Debug)]
+/// enum FooError {
+/// Bar,
+/// Baz(&'static str),
+/// }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+///
+/// // impl ::std::fmt::Display for FooError
+///
+/// fn do_some_stuff() -> bool {
+/// false
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// if ! do_some_stuff() {
+/// Err(cherr!(FooError::Baz("Error")))?;
+/// }
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+///
+/// Additionally an error cause can be added.
+///
+/// ```rust
+/// # use chainerror::*;
+/// # use std::io;
+/// # use std::error::Error;
+/// # #[derive(Debug)]
+/// # enum FooError {
+/// # Bar,
+/// # Baz(&'static str),
+/// # }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// Err(io::Error::from(io::ErrorKind::NotFound))?;
+/// Ok(())
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// do_some_stuff().map_err(
+/// |e| cherr!(e, FooError::Baz("Error"))
+/// )?;
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! cherr {
+ ( $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( $e:path, $k:expr ) => ({
+ $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `|e| cherr!(e, format!(…))`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!("func1 error"))?;
+/// Ok(())
+/// }
+///
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # #[cfg(not(windows))]
+/// # assert_eq!(
+/// # format!("\n{:?}\n", e), r#"
+/// # src/lib.rs:18: func1 error
+/// # Caused by:
+/// # src/lib.rs:13: Error reading 'foo.txt'
+/// # Caused by:
+/// # Kind(NotFound)
+/// # "#
+/// # );
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+///
+/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with
+/// `derive_str_cherr!(T)`
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! mstrerr {
+ ( $t:path, $msg:expr ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ |e| $crate::cherr!(e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # use std::error::Error;
+/// # use std::result::Result;
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// Err(strerr!(Func2Error, "Error reading '{}'", filename))
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! strerr {
+ ( $t:path, $msg:expr ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ $crate::cherr!(format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! derive_str_cherr {
+ ($e:ident) => {
+ #[derive(Clone)]
+ pub struct $e(pub String);
+ impl ::std::fmt::Display for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+ impl ::std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}({})", stringify!($e), self.0)
+ }
+ }
+ impl ::std::error::Error for $e {}
+ };
+}
+
+/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method
+///
+/// It basically hides `ChainError` to the outside and only exposes the `kind()`
+/// method.
+///
+/// Error::kind() returns the ErrorKind
+/// Error::source() returns the parent error
+///
+/// # Examples
+///
+/// ```rust
+/// use std::io;
+/// use chainerror::*;
+///
+/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
+/// return Err(io::Error::from(io::ErrorKind::NotFound));
+/// }
+///
+/// #[derive(Debug, Clone)]
+/// pub enum ErrorKind {
+/// IO(String),
+/// FatalError(String),
+/// Unknown,
+/// }
+///
+/// derive_err_kind!(Error, ErrorKind);
+///
+/// impl std::fmt::Display for ErrorKind {
+/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
+/// match self {
+/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e),
+/// ErrorKind::Unknown => write!(f, "unknown error"),
+/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+/// }
+/// }
+/// }
+///
+/// impl ErrorKind {
+/// fn from_io_error(e: &io::Error, f: String) -> Self {
+/// match e.kind() {
+/// io::ErrorKind::BrokenPipe => panic!("Should not happen"),
+/// io::ErrorKind::ConnectionReset => {
+/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e))
+/// }
+/// _ => ErrorKind::IO(f),
+/// }
+/// }
+/// }
+///
+/// impl From<&io::Error> for ErrorKind {
+/// fn from(e: &io::Error) -> Self {
+/// ErrorKind::IO(format!("{}", e))
+/// }
+/// }
+///
+/// pub fn func1() -> std::result::Result<(), Error> {
+/// let filename = "bar.txt";
+///
+/// do_some_io(filename)
+/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?;
+/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?;
+/// Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! derive_err_kind {
+ ($e:ident, $k:ident) => {
+ pub struct $e($crate::ChainError<$k>);
+
+ impl $e {
+ pub fn kind(&self) -> &$k {
+ self.0.kind()
+ }
+ }
+
+ impl From<$k> for $e {
+ fn from(e: $k) -> Self {
+ $e($crate::ChainError::new(e, None, None))
+ }
+ }
+
+ impl From<ChainError<$k>> for $e {
+ fn from(e: $crate::ChainError<$k>) -> Self {
+ $e(e)
+ }
+ }
+
+ impl From<&$e> for $k
+ where
+ $k: Clone,
+ {
+ fn from(e: &$e) -> Self {
+ e.kind().clone()
+ }
+ }
+
+ impl $crate::ChainErrorFrom<$e> for $k
+ where
+ $k: Clone,
+ {
+ #[inline]
+ fn chain_error_from(
+ t: $e,
+ line_filename: Option<&'static str>,
+ ) -> $crate::ChainError<$k> {
+ $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename)
+ }
+ }
+
+ impl std::error::Error for $e {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.0.source()
+ }
+ }
+
+ impl std::fmt::Display for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+ }
+
+ impl std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Debug::fmt(&self.0, f)
+ }
+ }
+ };
+}
+}
If you compare the output to the previous example, you will see, that:
@@ -409,23 +2789,23 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box<Error + Send + Sync>> { +fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } -fn func2() -> Result<(), Box<Error + Send + Sync>> { +fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } -fn func1() -> Result<(), Box<Error + Send + Sync>> { +fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } -fn main() -> Result<(), Box<Error + Send + Sync>> { +fn main() -> Result<(), Box<dyn Error + Send + Sync>> { if let Err(e) = func1() { eprintln!("{:?}", e); } @@ -433,8 +2813,1198 @@ fn main() -> Result<(), Box<Error + Send + Sync>> { } #[allow(dead_code)] mod chainerror { -{{#includecomment ../src/lib.rs}} -} +//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your +//! binaries, you still have the error backtrace. +//! +//! `chainerror` has no dependencies! +//! +//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace. +//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally. +//! +//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing. +//! +//! ## Features +//! +//! `no-fileline` +//! : completely turn off storing filename and line +//! +//! `display-cause` +//! : turn on printing a backtrace of the errors in `Display` +//! +//! `no-debug-cause` +//! : turn off printing a backtrace of the errors in `Debug` +//! +//! +//! # Tutorial +//! +//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) +//! +//! # Examples +//! +//! ```rust +//! use chainerror::*; +//! 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().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +//! func2().map_err(mstrerr!("func1 error"))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! if let Err(e) = func1() { +//! #[cfg(not(windows))] +//! assert_eq!( +//! format!("\n{:?}\n", e), r#" +//! src/lib.rs:20: func1 error +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ``` +//! +//! +//! ```rust +//! use chainerror::*; +//! 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().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! derive_str_cherr!(Func2Error); +//! +//! fn func2() -> ChainResult<(), Func2Error> { +//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; +//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?; +//! let filename = String::from("bar.txt"); +//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! 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:47: func1 error calling func2 +//! Caused by: +//! src/lib.rs:22: Func2Error(func2 error: calling func3) +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ``` + +#![deny( + warnings, + absolute_paths_not_starting_with_crate, + deprecated_in_future, + keyword_idents, + macro_use_extern_crate, + missing_debug_implementations, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_results, + unused_labels, + unused_lifetimes, + unstable_features, + unreachable_pub, + future_incompatible, + missing_copy_implementations, + missing_doc_code_examples, + rust_2018_idioms, + rust_2018_compatibility +)] + +use std::any::TypeId; +use std::error::Error; +use std::fmt::{Debug, Display, Formatter, Result}; + +/// chains an inner error kind `T` with a causing error +pub struct ChainError<T> { + #[cfg(not(feature = "no-fileline"))] + occurrence: Option<&'static str>, + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, +} + +/// convenience type alias +pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>; + +impl<T: 'static + Display + Debug> ChainError<T> { + #[cfg(not(feature = "no-fileline"))] + /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] + pub fn new( + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, + occurrence: Option<&'static str>, + ) -> Self { + Self { + occurrence, + kind, + error_cause, + } + } + + #[cfg(feature = "no-fileline")] + /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] + pub fn new( + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, + _occurrence: Option<&'static str>, + ) -> Self { + Self { kind, error_cause } + } + + /// return the root cause of the error chain, if any exists + pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { + self.iter().last() + } + + /// Find the first error cause of type U, if any exists + /// + /// # Examples + /// + /// ```rust + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// + /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func1Error); + /// + /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { + /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { + /// + /// assert!(f1err.find_cause::<io::Error>().is_some()); + /// + /// assert!(f1err.find_chain_cause::<Func2Error>().is_some()); + /// } + /// # else { + /// # panic!(); + /// # } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ``` + #[inline] + pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { + self.iter().filter_map(Error::downcast_ref::<U>).next() + } + + /// Find the first error cause of type `ChainError<U>`, if any exists + /// + /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals + /// + /// # Examples + /// + /// ```rust + /// # use chainerror::*; + /// # derive_str_cherr!(FooError); + /// # let err = ChainError::new(String::new(), None, None); + /// // Instead of writing + /// err.find_cause::<ChainError<FooError>>(); + /// + /// // leave out the ChainError<FooError> implementation detail + /// err.find_chain_cause::<FooError>(); + /// ``` + #[inline] + pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { + self.iter() + .filter_map(Error::downcast_ref::<ChainError<U>>) + .next() + } + + /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U` + /// + /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals + /// + /// # Examples + /// + /// ```rust + /// # use chainerror::*; + /// # derive_str_cherr!(FooErrorKind); + /// # let err = ChainError::new(String::new(), None, None); + /// // Instead of writing + /// err.find_cause::<ChainError<FooErrorKind>>(); + /// // and/or + /// err.find_chain_cause::<FooErrorKind>(); + /// // and/or + /// err.find_cause::<FooErrorKind>(); + /// + /// // leave out the ChainError<FooErrorKind> implementation detail + /// err.find_kind_or_cause::<FooErrorKind>(); + /// ``` + #[inline] + pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> { + self.iter() + .filter_map(|e| { + e.downcast_ref::<ChainError<U>>() + .map(|e| e.kind()) + .or_else(|| e.downcast_ref::<U>()) + }) + .next() + } + + /// Return a reference to T of `ChainError<T>` + /// + /// # Examples + /// + /// ```rust + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// + /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// #[derive(Debug)] + /// enum Func1ErrorKind { + /// Func2, + /// IO(String), + /// } + /// + /// /// impl ::std::fmt::Display for Func1ErrorKind {…} + /// # impl ::std::fmt::Display for Func1ErrorKind { + /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + /// # match self { + /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"), + /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + /// # } + /// # } + /// # } + /// + /// fn func1() -> ChainResult<(), Func1ErrorKind> { + /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// match e.kind() { + /// Func1ErrorKind::Func2 => {} + /// Func1ErrorKind::IO(filename) => panic!(), + /// } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ``` + #[inline] + pub fn kind(&self) -> &T { + &self.kind + } + + /// Returns an Iterator over all error causes/sources + /// + /// # Example + /// + /// + #[inline] + pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> { + ErrorIter { + current: Some(self), + } + } +} + +struct ErrorIter<'a> { + current: Option<&'a (dyn Error + 'static)>, +} + +impl<'a> Iterator for ErrorIter<'a> { + type Item = &'a (dyn Error + 'static); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let current = self.current; + self.current = self.current.and_then(Error::source); + current + } +} + +impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.kind + } +} + +/// Convenience trait to hide the `ChainError<T>` implementation internals +pub trait ChainErrorDown { + /// Test if of type `ChainError<T>` + fn is_chain<T: 'static + Display + Debug>(&self) -> bool; + /// Downcast to a reference of `ChainError<T>` + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; + /// Downcast to a mutable reference of `ChainError<T>` + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; + /// Downcast to T of `ChainError<T>` + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>; + /// Downcast to T mutable reference of `ChainError<T>` + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>; +} + +impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + TypeId::of::<T>() == TypeId::of::<U>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&*(self as *const dyn Error as *const &ChainError<T>)) + } + } else { + None + } + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) + } + } else { + None + } + } + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind) + } + } else { + None + } + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind) + } + } else { + None + } + } +} + +impl ChainErrorDown for dyn Error + 'static { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl ChainErrorDown for dyn Error + 'static + Send { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl ChainErrorDown for dyn Error + 'static + Send + Sync { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl<T: 'static + Display + Debug> Error for ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Error for &ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Display for ChainError<T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.kind)?; + + #[cfg(feature = "display-cause")] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Display::fmt(&e, f)?; + } + } + Ok(()) + } +} + +impl<T: 'static + Display + Debug> Debug for ChainError<T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + #[cfg(not(feature = "no-fileline"))] + { + if let Some(ref o) = self.occurrence { + Display::fmt(o, f)?; + } + } + + if self.is_chain::<String>() { + Display::fmt(&self.kind, f)?; + } else { + Debug::fmt(&self.kind, f)?; + } + + #[cfg(not(feature = "no-debug-cause"))] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Debug::fmt(&e, f)?; + } + } + Ok(()) + } +} + +/// `ChainErrorFrom<T>` is similar to `From<T>` +pub trait ChainErrorFrom<T>: Sized { + /// similar to From<T>::from() + fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>; +} + +/// `IntoChainError<T>` is similar to `Into<T>` +pub trait IntoChainError<T>: Sized { + /// similar to Into<T>::into() + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>; +} + +impl<T, U> IntoChainError<U> for T +where + U: ChainErrorFrom<T>, +{ + #[inline] + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> { + U::chain_error_from(self, line_filename) + } +} + +impl<T, U> ChainErrorFrom<T> for U +where + T: Into<U>, + U: 'static + Display + Debug, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> { + let e: U = t.into(); + ChainError::new(e, None, line_filename) + } +} + +/* +impl<T, U> ChainErrorFrom<T> for U + where + T: 'static + Error + Into<Box<T>> + Clone, + U: 'static + Display + Debug + From<T>, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> { + ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename) + } +} +*/ + +/// map into `ChainError<T>` with `T::from(err)` +/// +/// adds `line!()` and `file!()` information +#[macro_export] +macro_rules! minto_cherr { + ( $k:ident ) => ( + |e| $crate::cherr!(e, $k::from(&e)) + ); + ( $enum:ident $(:: $enum_path:ident)* ) => ( + |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e)) + ); +} + +/// Creates a new `ChainError<T>` +/// +/// # Examples +/// +/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`. +/// ```rust +/// # use chainerror::*; +/// # #[derive(Debug)] +/// enum FooError { +/// Bar, +/// Baz(&'static str), +/// } +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// +/// // impl ::std::fmt::Display for FooError +/// +/// fn do_some_stuff() -> bool { +/// false +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// if ! do_some_stuff() { +/// Err(cherr!(FooError::Baz("Error")))?; +/// } +/// Ok(()) +/// } +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {} +/// # _ => panic!(), +/// # } +/// # } +/// ``` +/// +/// Additionally an error cause can be added. +/// +/// ```rust +/// # use chainerror::*; +/// # use std::io; +/// # use std::error::Error; +/// # #[derive(Debug)] +/// # enum FooError { +/// # Bar, +/// # Baz(&'static str), +/// # } +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> { +/// Err(io::Error::from(io::ErrorKind::NotFound))?; +/// Ok(()) +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// do_some_stuff().map_err( +/// |e| cherr!(e, FooError::Baz("Error")) +/// )?; +/// Ok(()) +/// } +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {} +/// # _ => panic!(), +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! cherr { + ( $k:expr ) => ({ + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + }); + ( None, $k:expr ) => ({ + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + }); + ( None, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!(None, format!($fmt, $($arg)+ )) + }); + ( None, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!(None, format!($fmt, $($arg)+ )) + }); + ( $e:path, $k:expr ) => ({ + $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) + }); + ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!($e, format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro for `|e| cherr!(e, format!(…))` +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +/// func2().map_err(mstrerr!("func1 error"))?; +/// Ok(()) +/// } +/// +/// # fn main() { +/// # if let Err(e) = func1() { +/// # #[cfg(not(windows))] +/// # assert_eq!( +/// # format!("\n{:?}\n", e), r#" +/// # src/lib.rs:18: func1 error +/// # Caused by: +/// # src/lib.rs:13: Error reading 'foo.txt' +/// # Caused by: +/// # Kind(NotFound) +/// # "# +/// # ); +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +/// +/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with +/// `derive_str_cherr!(T)` +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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(()) +/// # } +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! mstrerr { + ( $t:path, $msg:expr ) => ({ + |e| $crate::cherr!(e, $t ($msg.to_string())) + }); + ( $t:path, $msg:expr, ) => ({ + |e| $crate::cherr!(e, $t ($msg.to_string())) + }); + ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ + |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ ))) + }); + ($msg:expr) => ({ + |e| $crate::cherr!(e, $msg.to_string()) + }); + ($msg:expr, ) => ({ + |e| $crate::cherr!(e, $msg.to_string()) + }); + ($fmt:expr, $($arg:tt)+) => ({ + |e| $crate::cherr!(e, format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)` +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # use std::error::Error; +/// # use std::result::Result; +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// Err(strerr!(Func2Error, "Error reading '{}'", filename)) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! strerr { + ( $t:path, $msg:expr ) => ({ + $crate::cherr!($t ($msg.to_string())) + }); + ( $t:path, $msg:expr, ) => ({ + $crate::cherr!($t ($msg.to_string())) + }); + ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!($t (format!($fmt, $($arg)+ ))) + }); + ($msg:expr) => ({ + $crate::cherr!($msg.to_string()) + }); + ($msg:expr, ) => ({ + $crate::cherr!($msg.to_string()) + }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::cherr!(format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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(()) +/// # } +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! derive_str_cherr { + ($e:ident) => { + #[derive(Clone)] + pub struct $e(pub String); + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", self.0) + } + } + impl ::std::fmt::Debug for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}({})", stringify!($e), self.0) + } + } + impl ::std::error::Error for $e {} + }; +} + +/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method +/// +/// It basically hides `ChainError` to the outside and only exposes the `kind()` +/// method. +/// +/// Error::kind() returns the ErrorKind +/// Error::source() returns the parent error +/// +/// # Examples +/// +/// ```rust +/// use std::io; +/// use chainerror::*; +/// +/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { +/// return Err(io::Error::from(io::ErrorKind::NotFound)); +/// } +/// +/// #[derive(Debug, Clone)] +/// pub enum ErrorKind { +/// IO(String), +/// FatalError(String), +/// Unknown, +/// } +/// +/// derive_err_kind!(Error, ErrorKind); +/// +/// impl std::fmt::Display for ErrorKind { +/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { +/// match self { +/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e), +/// ErrorKind::Unknown => write!(f, "unknown error"), +/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), +/// } +/// } +/// } +/// +/// impl ErrorKind { +/// fn from_io_error(e: &io::Error, f: String) -> Self { +/// match e.kind() { +/// io::ErrorKind::BrokenPipe => panic!("Should not happen"), +/// io::ErrorKind::ConnectionReset => { +/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e)) +/// } +/// _ => ErrorKind::IO(f), +/// } +/// } +/// } +/// +/// impl From<&io::Error> for ErrorKind { +/// fn from(e: &io::Error) -> Self { +/// ErrorKind::IO(format!("{}", e)) +/// } +/// } +/// +/// pub fn func1() -> std::result::Result<(), Error> { +/// let filename = "bar.txt"; +/// +/// do_some_io(filename) +/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; +/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! derive_err_kind { + ($e:ident, $k:ident) => { + pub struct $e($crate::ChainError<$k>); + + impl $e { + pub fn kind(&self) -> &$k { + self.0.kind() + } + } + + impl From<$k> for $e { + fn from(e: $k) -> Self { + $e($crate::ChainError::new(e, None, None)) + } + } + + impl From<ChainError<$k>> for $e { + fn from(e: $crate::ChainError<$k>) -> Self { + $e(e) + } + } + + impl From<&$e> for $k + where + $k: Clone, + { + fn from(e: &$e) -> Self { + e.kind().clone() + } + } + + impl $crate::ChainErrorFrom<$e> for $k + where + $k: Clone, + { + #[inline] + fn chain_error_from( + t: $e, + line_filename: Option<&'static str>, + ) -> $crate::ChainError<$k> { + $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename) + } + } + + impl std::error::Error for $e { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } + } + + impl std::fmt::Display for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } + } + + impl std::fmt::Debug for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } + } + }; +} +}Sometimes you want to inspect the source()
of an Error
.
@@ -444,18 +4014,18 @@ use std::error::Error;
use std::io;
use std::result::Result;
-fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
+fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
-fn func2() -> Result<(), Box<Error + Send + Sync>> {
+fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt";
do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?;
Ok(())
}
-fn func1() -> Result<(), Box<Error + Send + Sync>> {
+fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func2() {
if let Some(s) = e.source() {
eprintln!("func2 failed because of '{}'", s);
@@ -465,7 +4035,7 @@ fn func1() -> Result<(), Box<Error + Send + Sync>> {
Ok(())
}
-fn main() -> Result<(), Box<Error + Send + Sync>> {
+fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func1() {
eprintln!("{}", e);
}
@@ -473,8 +4043,1198 @@ fn main() -> Result<(), Box<Error + Send + Sync>> {
}
#[allow(dead_code)]
mod chainerror {
-{{#includecomment ../src/lib.rs}}
-}
+//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your
+//! binaries, you still have the error backtrace.
+//!
+//! `chainerror` has no dependencies!
+//!
+//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace.
+//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally.
+//!
+//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing.
+//!
+//! ## Features
+//!
+//! `no-fileline`
+//! : completely turn off storing filename and line
+//!
+//! `display-cause`
+//! : turn on printing a backtrace of the errors in `Display`
+//!
+//! `no-debug-cause`
+//! : turn off printing a backtrace of the errors in `Debug`
+//!
+//!
+//! # Tutorial
+//!
+//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
+//!
+//! # Examples
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+//! func2().map_err(mstrerr!("func1 error"))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! if let Err(e) = func1() {
+//! #[cfg(not(windows))]
+//! assert_eq!(
+//! format!("\n{:?}\n", e), r#"
+//! src/lib.rs:20: func1 error
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+//!
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! derive_str_cherr!(Func2Error);
+//!
+//! fn func2() -> ChainResult<(), Func2Error> {
+//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?;
+//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?;
+//! let filename = String::from("bar.txt");
+//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! 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:47: func1 error calling func2
+//! Caused by:
+//! src/lib.rs:22: Func2Error(func2 error: calling func3)
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+
+#![deny(
+ warnings,
+ absolute_paths_not_starting_with_crate,
+ deprecated_in_future,
+ keyword_idents,
+ macro_use_extern_crate,
+ missing_debug_implementations,
+ trivial_numeric_casts,
+ unused_extern_crates,
+ unused_import_braces,
+ unused_qualifications,
+ unused_results,
+ unused_labels,
+ unused_lifetimes,
+ unstable_features,
+ unreachable_pub,
+ future_incompatible,
+ missing_copy_implementations,
+ missing_doc_code_examples,
+ rust_2018_idioms,
+ rust_2018_compatibility
+)]
+
+use std::any::TypeId;
+use std::error::Error;
+use std::fmt::{Debug, Display, Formatter, Result};
+
+/// chains an inner error kind `T` with a causing error
+pub struct ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ occurrence: Option<&'static str>,
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+}
+
+/// convenience type alias
+pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>;
+
+impl<T: 'static + Display + Debug> ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ occurrence: Option<&'static str>,
+ ) -> Self {
+ Self {
+ occurrence,
+ kind,
+ error_cause,
+ }
+ }
+
+ #[cfg(feature = "no-fileline")]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ _occurrence: Option<&'static str>,
+ ) -> Self {
+ Self { kind, error_cause }
+ }
+
+ /// return the root cause of the error chain, if any exists
+ pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> {
+ self.iter().last()
+ }
+
+ /// Find the first error cause of type U, if any exists
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func1Error);
+ ///
+ /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+ ///
+ /// assert!(f1err.find_cause::<io::Error>().is_some());
+ ///
+ /// assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+ /// }
+ /// # else {
+ /// # panic!();
+ /// # }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter().filter_map(Error::downcast_ref::<U>).next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>`, if any exists
+ ///
+ /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooError);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooError>>();
+ ///
+ /// // leave out the ChainError<FooError> implementation detail
+ /// err.find_chain_cause::<FooError>();
+ /// ```
+ #[inline]
+ pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> {
+ self.iter()
+ .filter_map(Error::downcast_ref::<ChainError<U>>)
+ .next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U`
+ ///
+ /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooErrorKind);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooErrorKind>>();
+ /// // and/or
+ /// err.find_chain_cause::<FooErrorKind>();
+ /// // and/or
+ /// err.find_cause::<FooErrorKind>();
+ ///
+ /// // leave out the ChainError<FooErrorKind> implementation detail
+ /// err.find_kind_or_cause::<FooErrorKind>();
+ /// ```
+ #[inline]
+ pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter()
+ .filter_map(|e| {
+ e.downcast_ref::<ChainError<U>>()
+ .map(|e| e.kind())
+ .or_else(|| e.downcast_ref::<U>())
+ })
+ .next()
+ }
+
+ /// Return a reference to T of `ChainError<T>`
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// #[derive(Debug)]
+ /// enum Func1ErrorKind {
+ /// Func2,
+ /// IO(String),
+ /// }
+ ///
+ /// /// impl ::std::fmt::Display for Func1ErrorKind {…}
+ /// # impl ::std::fmt::Display for Func1ErrorKind {
+ /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ /// # match self {
+ /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"),
+ /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+ /// # }
+ /// # }
+ /// # }
+ ///
+ /// fn func1() -> ChainResult<(), Func1ErrorKind> {
+ /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?;
+ /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// match e.kind() {
+ /// Func1ErrorKind::Func2 => {}
+ /// Func1ErrorKind::IO(filename) => panic!(),
+ /// }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn kind(&self) -> &T {
+ &self.kind
+ }
+
+ /// Returns an Iterator over all error causes/sources
+ ///
+ /// # Example
+ ///
+ ///
+ #[inline]
+ pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> {
+ ErrorIter {
+ current: Some(self),
+ }
+ }
+}
+
+struct ErrorIter<'a> {
+ current: Option<&'a (dyn Error + 'static)>,
+}
+
+impl<'a> Iterator for ErrorIter<'a> {
+ type Item = &'a (dyn Error + 'static);
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ let current = self.current;
+ self.current = self.current.and_then(Error::source);
+ current
+ }
+}
+
+impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.kind
+ }
+}
+
+/// Convenience trait to hide the `ChainError<T>` implementation internals
+pub trait ChainErrorDown {
+ /// Test if of type `ChainError<T>`
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool;
+ /// Downcast to a reference of `ChainError<T>`
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>;
+ /// Downcast to a mutable reference of `ChainError<T>`
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>;
+ /// Downcast to T of `ChainError<T>`
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>;
+ /// Downcast to T mutable reference of `ChainError<T>`
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>;
+}
+
+impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ TypeId::of::<T>() == TypeId::of::<U>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&*(self as *const dyn Error as *const &ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send + Sync {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &mut ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Display for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ write!(f, "{}", self.kind)?;
+
+ #[cfg(feature = "display-cause")]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Display::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<T: 'static + Display + Debug> Debug for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ #[cfg(not(feature = "no-fileline"))]
+ {
+ if let Some(ref o) = self.occurrence {
+ Display::fmt(o, f)?;
+ }
+ }
+
+ if self.is_chain::<String>() {
+ Display::fmt(&self.kind, f)?;
+ } else {
+ Debug::fmt(&self.kind, f)?;
+ }
+
+ #[cfg(not(feature = "no-debug-cause"))]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Debug::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// `ChainErrorFrom<T>` is similar to `From<T>`
+pub trait ChainErrorFrom<T>: Sized {
+ /// similar to From<T>::from()
+ fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>;
+}
+
+/// `IntoChainError<T>` is similar to `Into<T>`
+pub trait IntoChainError<T>: Sized {
+ /// similar to Into<T>::into()
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>;
+}
+
+impl<T, U> IntoChainError<U> for T
+where
+ U: ChainErrorFrom<T>,
+{
+ #[inline]
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> {
+ U::chain_error_from(self, line_filename)
+ }
+}
+
+impl<T, U> ChainErrorFrom<T> for U
+where
+ T: Into<U>,
+ U: 'static + Display + Debug,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ let e: U = t.into();
+ ChainError::new(e, None, line_filename)
+ }
+}
+
+/*
+impl<T, U> ChainErrorFrom<T> for U
+ where
+ T: 'static + Error + Into<Box<T>> + Clone,
+ U: 'static + Display + Debug + From<T>,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename)
+ }
+}
+*/
+
+/// map into `ChainError<T>` with `T::from(err)`
+///
+/// adds `line!()` and `file!()` information
+#[macro_export]
+macro_rules! minto_cherr {
+ ( $k:ident ) => (
+ |e| $crate::cherr!(e, $k::from(&e))
+ );
+ ( $enum:ident $(:: $enum_path:ident)* ) => (
+ |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e))
+ );
+}
+
+/// Creates a new `ChainError<T>`
+///
+/// # Examples
+///
+/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`.
+/// ```rust
+/// # use chainerror::*;
+/// # #[derive(Debug)]
+/// enum FooError {
+/// Bar,
+/// Baz(&'static str),
+/// }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+///
+/// // impl ::std::fmt::Display for FooError
+///
+/// fn do_some_stuff() -> bool {
+/// false
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// if ! do_some_stuff() {
+/// Err(cherr!(FooError::Baz("Error")))?;
+/// }
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+///
+/// Additionally an error cause can be added.
+///
+/// ```rust
+/// # use chainerror::*;
+/// # use std::io;
+/// # use std::error::Error;
+/// # #[derive(Debug)]
+/// # enum FooError {
+/// # Bar,
+/// # Baz(&'static str),
+/// # }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// Err(io::Error::from(io::ErrorKind::NotFound))?;
+/// Ok(())
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// do_some_stuff().map_err(
+/// |e| cherr!(e, FooError::Baz("Error"))
+/// )?;
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! cherr {
+ ( $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( $e:path, $k:expr ) => ({
+ $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `|e| cherr!(e, format!(…))`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!("func1 error"))?;
+/// Ok(())
+/// }
+///
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # #[cfg(not(windows))]
+/// # assert_eq!(
+/// # format!("\n{:?}\n", e), r#"
+/// # src/lib.rs:18: func1 error
+/// # Caused by:
+/// # src/lib.rs:13: Error reading 'foo.txt'
+/// # Caused by:
+/// # Kind(NotFound)
+/// # "#
+/// # );
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+///
+/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with
+/// `derive_str_cherr!(T)`
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! mstrerr {
+ ( $t:path, $msg:expr ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ |e| $crate::cherr!(e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # use std::error::Error;
+/// # use std::result::Result;
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// Err(strerr!(Func2Error, "Error reading '{}'", filename))
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! strerr {
+ ( $t:path, $msg:expr ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ $crate::cherr!(format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! derive_str_cherr {
+ ($e:ident) => {
+ #[derive(Clone)]
+ pub struct $e(pub String);
+ impl ::std::fmt::Display for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+ impl ::std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}({})", stringify!($e), self.0)
+ }
+ }
+ impl ::std::error::Error for $e {}
+ };
+}
+
+/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method
+///
+/// It basically hides `ChainError` to the outside and only exposes the `kind()`
+/// method.
+///
+/// Error::kind() returns the ErrorKind
+/// Error::source() returns the parent error
+///
+/// # Examples
+///
+/// ```rust
+/// use std::io;
+/// use chainerror::*;
+///
+/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
+/// return Err(io::Error::from(io::ErrorKind::NotFound));
+/// }
+///
+/// #[derive(Debug, Clone)]
+/// pub enum ErrorKind {
+/// IO(String),
+/// FatalError(String),
+/// Unknown,
+/// }
+///
+/// derive_err_kind!(Error, ErrorKind);
+///
+/// impl std::fmt::Display for ErrorKind {
+/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
+/// match self {
+/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e),
+/// ErrorKind::Unknown => write!(f, "unknown error"),
+/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+/// }
+/// }
+/// }
+///
+/// impl ErrorKind {
+/// fn from_io_error(e: &io::Error, f: String) -> Self {
+/// match e.kind() {
+/// io::ErrorKind::BrokenPipe => panic!("Should not happen"),
+/// io::ErrorKind::ConnectionReset => {
+/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e))
+/// }
+/// _ => ErrorKind::IO(f),
+/// }
+/// }
+/// }
+///
+/// impl From<&io::Error> for ErrorKind {
+/// fn from(e: &io::Error) -> Self {
+/// ErrorKind::IO(format!("{}", e))
+/// }
+/// }
+///
+/// pub fn func1() -> std::result::Result<(), Error> {
+/// let filename = "bar.txt";
+///
+/// do_some_io(filename)
+/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?;
+/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?;
+/// Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! derive_err_kind {
+ ($e:ident, $k:ident) => {
+ pub struct $e($crate::ChainError<$k>);
+
+ impl $e {
+ pub fn kind(&self) -> &$k {
+ self.0.kind()
+ }
+ }
+
+ impl From<$k> for $e {
+ fn from(e: $k) -> Self {
+ $e($crate::ChainError::new(e, None, None))
+ }
+ }
+
+ impl From<ChainError<$k>> for $e {
+ fn from(e: $crate::ChainError<$k>) -> Self {
+ $e(e)
+ }
+ }
+
+ impl From<&$e> for $k
+ where
+ $k: Clone,
+ {
+ fn from(e: &$e) -> Self {
+ e.kind().clone()
+ }
+ }
+
+ impl $crate::ChainErrorFrom<$e> for $k
+ where
+ $k: Clone,
+ {
+ #[inline]
+ fn chain_error_from(
+ t: $e,
+ line_filename: Option<&'static str>,
+ ) -> $crate::ChainError<$k> {
+ $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename)
+ }
+ }
+
+ impl std::error::Error for $e {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.0.source()
+ }
+ }
+
+ impl std::fmt::Display for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+ }
+
+ impl std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Debug::fmt(&self.0, f)
+ }
+ }
+ };
+}
+}
Note, that because we changed the output of the error in main()
from
Debug
to Display
, we don't see the error backtrace with filename and line number.
chainerror
also has some helper methods:
To distinguish the errors occuring in various places, we can define named string errors with the @@ -615,14 +7755,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box<Error + Send + Sync>> { +fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box<Error + Send + Sync>> { +fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -630,12 +7770,12 @@ fn func2() -> Result<(), Box<Error + Send + Sync>> { derive_str_cherr!(Func1Error); -fn func1() -> Result<(), Box<Error + Send + Sync>> { +fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { func2().map_err(mstrerr!(Func1Error, "func1 error"))?; Ok(()) } -fn main() -> Result<(), Box<Error + Send + Sync>> { +fn main() -> Result<(), Box<dyn Error + Send + Sync>> { if let Err(e) = func1() { if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { eprintln!("Func1Error: {}", f1err); @@ -653,8 +7793,1198 @@ fn main() -> Result<(), Box<Error + Send + Sync>> { } #[allow(dead_code)] mod chainerror { -{{#includecomment ../src/lib.rs}} -} +//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your +//! binaries, you still have the error backtrace. +//! +//! `chainerror` has no dependencies! +//! +//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace. +//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally. +//! +//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing. +//! +//! ## Features +//! +//! `no-fileline` +//! : completely turn off storing filename and line +//! +//! `display-cause` +//! : turn on printing a backtrace of the errors in `Display` +//! +//! `no-debug-cause` +//! : turn off printing a backtrace of the errors in `Debug` +//! +//! +//! # Tutorial +//! +//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) +//! +//! # Examples +//! +//! ```rust +//! use chainerror::*; +//! 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().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +//! func2().map_err(mstrerr!("func1 error"))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! if let Err(e) = func1() { +//! #[cfg(not(windows))] +//! assert_eq!( +//! format!("\n{:?}\n", e), r#" +//! src/lib.rs:20: func1 error +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ``` +//! +//! +//! ```rust +//! use chainerror::*; +//! 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().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! derive_str_cherr!(Func2Error); +//! +//! fn func2() -> ChainResult<(), Func2Error> { +//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; +//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?; +//! let filename = String::from("bar.txt"); +//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! 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:47: func1 error calling func2 +//! Caused by: +//! src/lib.rs:22: Func2Error(func2 error: calling func3) +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ``` + +#![deny( + warnings, + absolute_paths_not_starting_with_crate, + deprecated_in_future, + keyword_idents, + macro_use_extern_crate, + missing_debug_implementations, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_results, + unused_labels, + unused_lifetimes, + unstable_features, + unreachable_pub, + future_incompatible, + missing_copy_implementations, + missing_doc_code_examples, + rust_2018_idioms, + rust_2018_compatibility +)] + +use std::any::TypeId; +use std::error::Error; +use std::fmt::{Debug, Display, Formatter, Result}; + +/// chains an inner error kind `T` with a causing error +pub struct ChainError<T> { + #[cfg(not(feature = "no-fileline"))] + occurrence: Option<&'static str>, + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, +} + +/// convenience type alias +pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>; + +impl<T: 'static + Display + Debug> ChainError<T> { + #[cfg(not(feature = "no-fileline"))] + /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] + pub fn new( + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, + occurrence: Option<&'static str>, + ) -> Self { + Self { + occurrence, + kind, + error_cause, + } + } + + #[cfg(feature = "no-fileline")] + /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] + pub fn new( + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, + _occurrence: Option<&'static str>, + ) -> Self { + Self { kind, error_cause } + } + + /// return the root cause of the error chain, if any exists + pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { + self.iter().last() + } + + /// Find the first error cause of type U, if any exists + /// + /// # Examples + /// + /// ```rust + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// + /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func1Error); + /// + /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { + /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { + /// + /// assert!(f1err.find_cause::<io::Error>().is_some()); + /// + /// assert!(f1err.find_chain_cause::<Func2Error>().is_some()); + /// } + /// # else { + /// # panic!(); + /// # } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ``` + #[inline] + pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { + self.iter().filter_map(Error::downcast_ref::<U>).next() + } + + /// Find the first error cause of type `ChainError<U>`, if any exists + /// + /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals + /// + /// # Examples + /// + /// ```rust + /// # use chainerror::*; + /// # derive_str_cherr!(FooError); + /// # let err = ChainError::new(String::new(), None, None); + /// // Instead of writing + /// err.find_cause::<ChainError<FooError>>(); + /// + /// // leave out the ChainError<FooError> implementation detail + /// err.find_chain_cause::<FooError>(); + /// ``` + #[inline] + pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { + self.iter() + .filter_map(Error::downcast_ref::<ChainError<U>>) + .next() + } + + /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U` + /// + /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals + /// + /// # Examples + /// + /// ```rust + /// # use chainerror::*; + /// # derive_str_cherr!(FooErrorKind); + /// # let err = ChainError::new(String::new(), None, None); + /// // Instead of writing + /// err.find_cause::<ChainError<FooErrorKind>>(); + /// // and/or + /// err.find_chain_cause::<FooErrorKind>(); + /// // and/or + /// err.find_cause::<FooErrorKind>(); + /// + /// // leave out the ChainError<FooErrorKind> implementation detail + /// err.find_kind_or_cause::<FooErrorKind>(); + /// ``` + #[inline] + pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> { + self.iter() + .filter_map(|e| { + e.downcast_ref::<ChainError<U>>() + .map(|e| e.kind()) + .or_else(|| e.downcast_ref::<U>()) + }) + .next() + } + + /// Return a reference to T of `ChainError<T>` + /// + /// # Examples + /// + /// ```rust + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// + /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// #[derive(Debug)] + /// enum Func1ErrorKind { + /// Func2, + /// IO(String), + /// } + /// + /// /// impl ::std::fmt::Display for Func1ErrorKind {…} + /// # impl ::std::fmt::Display for Func1ErrorKind { + /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + /// # match self { + /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"), + /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + /// # } + /// # } + /// # } + /// + /// fn func1() -> ChainResult<(), Func1ErrorKind> { + /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// match e.kind() { + /// Func1ErrorKind::Func2 => {} + /// Func1ErrorKind::IO(filename) => panic!(), + /// } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ``` + #[inline] + pub fn kind(&self) -> &T { + &self.kind + } + + /// Returns an Iterator over all error causes/sources + /// + /// # Example + /// + /// + #[inline] + pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> { + ErrorIter { + current: Some(self), + } + } +} + +struct ErrorIter<'a> { + current: Option<&'a (dyn Error + 'static)>, +} + +impl<'a> Iterator for ErrorIter<'a> { + type Item = &'a (dyn Error + 'static); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let current = self.current; + self.current = self.current.and_then(Error::source); + current + } +} + +impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.kind + } +} + +/// Convenience trait to hide the `ChainError<T>` implementation internals +pub trait ChainErrorDown { + /// Test if of type `ChainError<T>` + fn is_chain<T: 'static + Display + Debug>(&self) -> bool; + /// Downcast to a reference of `ChainError<T>` + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; + /// Downcast to a mutable reference of `ChainError<T>` + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; + /// Downcast to T of `ChainError<T>` + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>; + /// Downcast to T mutable reference of `ChainError<T>` + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>; +} + +impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + TypeId::of::<T>() == TypeId::of::<U>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&*(self as *const dyn Error as *const &ChainError<T>)) + } + } else { + None + } + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) + } + } else { + None + } + } + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind) + } + } else { + None + } + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind) + } + } else { + None + } + } +} + +impl ChainErrorDown for dyn Error + 'static { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl ChainErrorDown for dyn Error + 'static + Send { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl ChainErrorDown for dyn Error + 'static + Send + Sync { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl<T: 'static + Display + Debug> Error for ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Error for &ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Display for ChainError<T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.kind)?; + + #[cfg(feature = "display-cause")] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Display::fmt(&e, f)?; + } + } + Ok(()) + } +} + +impl<T: 'static + Display + Debug> Debug for ChainError<T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + #[cfg(not(feature = "no-fileline"))] + { + if let Some(ref o) = self.occurrence { + Display::fmt(o, f)?; + } + } + + if self.is_chain::<String>() { + Display::fmt(&self.kind, f)?; + } else { + Debug::fmt(&self.kind, f)?; + } + + #[cfg(not(feature = "no-debug-cause"))] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Debug::fmt(&e, f)?; + } + } + Ok(()) + } +} + +/// `ChainErrorFrom<T>` is similar to `From<T>` +pub trait ChainErrorFrom<T>: Sized { + /// similar to From<T>::from() + fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>; +} + +/// `IntoChainError<T>` is similar to `Into<T>` +pub trait IntoChainError<T>: Sized { + /// similar to Into<T>::into() + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>; +} + +impl<T, U> IntoChainError<U> for T +where + U: ChainErrorFrom<T>, +{ + #[inline] + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> { + U::chain_error_from(self, line_filename) + } +} + +impl<T, U> ChainErrorFrom<T> for U +where + T: Into<U>, + U: 'static + Display + Debug, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> { + let e: U = t.into(); + ChainError::new(e, None, line_filename) + } +} + +/* +impl<T, U> ChainErrorFrom<T> for U + where + T: 'static + Error + Into<Box<T>> + Clone, + U: 'static + Display + Debug + From<T>, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> { + ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename) + } +} +*/ + +/// map into `ChainError<T>` with `T::from(err)` +/// +/// adds `line!()` and `file!()` information +#[macro_export] +macro_rules! minto_cherr { + ( $k:ident ) => ( + |e| $crate::cherr!(e, $k::from(&e)) + ); + ( $enum:ident $(:: $enum_path:ident)* ) => ( + |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e)) + ); +} + +/// Creates a new `ChainError<T>` +/// +/// # Examples +/// +/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`. +/// ```rust +/// # use chainerror::*; +/// # #[derive(Debug)] +/// enum FooError { +/// Bar, +/// Baz(&'static str), +/// } +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// +/// // impl ::std::fmt::Display for FooError +/// +/// fn do_some_stuff() -> bool { +/// false +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// if ! do_some_stuff() { +/// Err(cherr!(FooError::Baz("Error")))?; +/// } +/// Ok(()) +/// } +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {} +/// # _ => panic!(), +/// # } +/// # } +/// ``` +/// +/// Additionally an error cause can be added. +/// +/// ```rust +/// # use chainerror::*; +/// # use std::io; +/// # use std::error::Error; +/// # #[derive(Debug)] +/// # enum FooError { +/// # Bar, +/// # Baz(&'static str), +/// # } +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> { +/// Err(io::Error::from(io::ErrorKind::NotFound))?; +/// Ok(()) +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// do_some_stuff().map_err( +/// |e| cherr!(e, FooError::Baz("Error")) +/// )?; +/// Ok(()) +/// } +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {} +/// # _ => panic!(), +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! cherr { + ( $k:expr ) => ({ + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + }); + ( None, $k:expr ) => ({ + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + }); + ( None, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!(None, format!($fmt, $($arg)+ )) + }); + ( None, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!(None, format!($fmt, $($arg)+ )) + }); + ( $e:path, $k:expr ) => ({ + $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) + }); + ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!($e, format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro for `|e| cherr!(e, format!(…))` +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +/// func2().map_err(mstrerr!("func1 error"))?; +/// Ok(()) +/// } +/// +/// # fn main() { +/// # if let Err(e) = func1() { +/// # #[cfg(not(windows))] +/// # assert_eq!( +/// # format!("\n{:?}\n", e), r#" +/// # src/lib.rs:18: func1 error +/// # Caused by: +/// # src/lib.rs:13: Error reading 'foo.txt' +/// # Caused by: +/// # Kind(NotFound) +/// # "# +/// # ); +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +/// +/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with +/// `derive_str_cherr!(T)` +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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(()) +/// # } +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! mstrerr { + ( $t:path, $msg:expr ) => ({ + |e| $crate::cherr!(e, $t ($msg.to_string())) + }); + ( $t:path, $msg:expr, ) => ({ + |e| $crate::cherr!(e, $t ($msg.to_string())) + }); + ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ + |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ ))) + }); + ($msg:expr) => ({ + |e| $crate::cherr!(e, $msg.to_string()) + }); + ($msg:expr, ) => ({ + |e| $crate::cherr!(e, $msg.to_string()) + }); + ($fmt:expr, $($arg:tt)+) => ({ + |e| $crate::cherr!(e, format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)` +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # use std::error::Error; +/// # use std::result::Result; +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// Err(strerr!(Func2Error, "Error reading '{}'", filename)) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! strerr { + ( $t:path, $msg:expr ) => ({ + $crate::cherr!($t ($msg.to_string())) + }); + ( $t:path, $msg:expr, ) => ({ + $crate::cherr!($t ($msg.to_string())) + }); + ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!($t (format!($fmt, $($arg)+ ))) + }); + ($msg:expr) => ({ + $crate::cherr!($msg.to_string()) + }); + ($msg:expr, ) => ({ + $crate::cherr!($msg.to_string()) + }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::cherr!(format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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(()) +/// # } +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! derive_str_cherr { + ($e:ident) => { + #[derive(Clone)] + pub struct $e(pub String); + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", self.0) + } + } + impl ::std::fmt::Debug for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}({})", stringify!($e), self.0) + } + } + impl ::std::error::Error for $e {} + }; +} + +/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method +/// +/// It basically hides `ChainError` to the outside and only exposes the `kind()` +/// method. +/// +/// Error::kind() returns the ErrorKind +/// Error::source() returns the parent error +/// +/// # Examples +/// +/// ```rust +/// use std::io; +/// use chainerror::*; +/// +/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { +/// return Err(io::Error::from(io::ErrorKind::NotFound)); +/// } +/// +/// #[derive(Debug, Clone)] +/// pub enum ErrorKind { +/// IO(String), +/// FatalError(String), +/// Unknown, +/// } +/// +/// derive_err_kind!(Error, ErrorKind); +/// +/// impl std::fmt::Display for ErrorKind { +/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { +/// match self { +/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e), +/// ErrorKind::Unknown => write!(f, "unknown error"), +/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), +/// } +/// } +/// } +/// +/// impl ErrorKind { +/// fn from_io_error(e: &io::Error, f: String) -> Self { +/// match e.kind() { +/// io::ErrorKind::BrokenPipe => panic!("Should not happen"), +/// io::ErrorKind::ConnectionReset => { +/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e)) +/// } +/// _ => ErrorKind::IO(f), +/// } +/// } +/// } +/// +/// impl From<&io::Error> for ErrorKind { +/// fn from(e: &io::Error) -> Self { +/// ErrorKind::IO(format!("{}", e)) +/// } +/// } +/// +/// pub fn func1() -> std::result::Result<(), Error> { +/// let filename = "bar.txt"; +/// +/// do_some_io(filename) +/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; +/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! derive_err_kind { + ($e:ident, $k:ident) => { + pub struct $e($crate::ChainError<$k>); + + impl $e { + pub fn kind(&self) -> &$k { + self.0.kind() + } + } + + impl From<$k> for $e { + fn from(e: $k) -> Self { + $e($crate::ChainError::new(e, None, None)) + } + } + + impl From<ChainError<$k>> for $e { + fn from(e: $crate::ChainError<$k>) -> Self { + $e(e) + } + } + + impl From<&$e> for $k + where + $k: Clone, + { + fn from(e: &$e) -> Self { + e.kind().clone() + } + } + + impl $crate::ChainErrorFrom<$e> for $k + where + $k: Clone, + { + #[inline] + fn chain_error_from( + t: $e, + line_filename: Option<&'static str>, + ) -> $crate::ChainError<$k> { + $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename) + } + } + + impl std::error::Error for $e { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } + } + + impl std::fmt::Display for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } + } + + impl std::fmt::Debug for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } + } + }; +} +}
What about functions returning different Error types?
@@ -680,14 +9010,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box<Error + Send + Sync>> { +fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box<Error + Send + Sync>> { +fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -696,14 +9026,14 @@ fn func2() -> Result<(), Box<Error + Send + Sync>> { derive_str_cherr!(Func1ErrorFunc2); derive_str_cherr!(Func1ErrorIO); -fn func1() -> Result<(), Box<Error + Send + Sync>> { +fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { func2().map_err(mstrerr!(Func1ErrorFunc2, "func1 error calling func2"))?; let filename = "bar.txt"; do_some_io().map_err(mstrerr!(Func1ErrorIO, "Error reading '{}'", filename))?; Ok(()) } -fn main() -> Result<(), Box<Error + Send + Sync>> { +fn main() -> Result<(), Box<dyn Error + Send + Sync>> { if let Err(e) = func1() { if let Some(s) = e.downcast_ref::<ChainError<Func1ErrorIO>>() { eprintln!("Func1ErrorIO:\n{:?}", s); @@ -717,8 +9047,1198 @@ fn main() -> Result<(), Box<Error + Send + Sync>> { } #[allow(dead_code)] mod chainerror { -{{#includecomment ../src/lib.rs}} -} +//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your +//! binaries, you still have the error backtrace. +//! +//! `chainerror` has no dependencies! +//! +//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace. +//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally. +//! +//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing. +//! +//! ## Features +//! +//! `no-fileline` +//! : completely turn off storing filename and line +//! +//! `display-cause` +//! : turn on printing a backtrace of the errors in `Display` +//! +//! `no-debug-cause` +//! : turn off printing a backtrace of the errors in `Debug` +//! +//! +//! # Tutorial +//! +//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) +//! +//! # Examples +//! +//! ```rust +//! use chainerror::*; +//! 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().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +//! func2().map_err(mstrerr!("func1 error"))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! if let Err(e) = func1() { +//! #[cfg(not(windows))] +//! assert_eq!( +//! format!("\n{:?}\n", e), r#" +//! src/lib.rs:20: func1 error +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ``` +//! +//! +//! ```rust +//! use chainerror::*; +//! 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().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! derive_str_cherr!(Func2Error); +//! +//! fn func2() -> ChainResult<(), Func2Error> { +//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; +//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?; +//! let filename = String::from("bar.txt"); +//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! 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:47: func1 error calling func2 +//! Caused by: +//! src/lib.rs:22: Func2Error(func2 error: calling func3) +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ``` + +#![deny( + warnings, + absolute_paths_not_starting_with_crate, + deprecated_in_future, + keyword_idents, + macro_use_extern_crate, + missing_debug_implementations, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_results, + unused_labels, + unused_lifetimes, + unstable_features, + unreachable_pub, + future_incompatible, + missing_copy_implementations, + missing_doc_code_examples, + rust_2018_idioms, + rust_2018_compatibility +)] + +use std::any::TypeId; +use std::error::Error; +use std::fmt::{Debug, Display, Formatter, Result}; + +/// chains an inner error kind `T` with a causing error +pub struct ChainError<T> { + #[cfg(not(feature = "no-fileline"))] + occurrence: Option<&'static str>, + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, +} + +/// convenience type alias +pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>; + +impl<T: 'static + Display + Debug> ChainError<T> { + #[cfg(not(feature = "no-fileline"))] + /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] + pub fn new( + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, + occurrence: Option<&'static str>, + ) -> Self { + Self { + occurrence, + kind, + error_cause, + } + } + + #[cfg(feature = "no-fileline")] + /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] + pub fn new( + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, + _occurrence: Option<&'static str>, + ) -> Self { + Self { kind, error_cause } + } + + /// return the root cause of the error chain, if any exists + pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { + self.iter().last() + } + + /// Find the first error cause of type U, if any exists + /// + /// # Examples + /// + /// ```rust + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// + /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func1Error); + /// + /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { + /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { + /// + /// assert!(f1err.find_cause::<io::Error>().is_some()); + /// + /// assert!(f1err.find_chain_cause::<Func2Error>().is_some()); + /// } + /// # else { + /// # panic!(); + /// # } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ``` + #[inline] + pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { + self.iter().filter_map(Error::downcast_ref::<U>).next() + } + + /// Find the first error cause of type `ChainError<U>`, if any exists + /// + /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals + /// + /// # Examples + /// + /// ```rust + /// # use chainerror::*; + /// # derive_str_cherr!(FooError); + /// # let err = ChainError::new(String::new(), None, None); + /// // Instead of writing + /// err.find_cause::<ChainError<FooError>>(); + /// + /// // leave out the ChainError<FooError> implementation detail + /// err.find_chain_cause::<FooError>(); + /// ``` + #[inline] + pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { + self.iter() + .filter_map(Error::downcast_ref::<ChainError<U>>) + .next() + } + + /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U` + /// + /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals + /// + /// # Examples + /// + /// ```rust + /// # use chainerror::*; + /// # derive_str_cherr!(FooErrorKind); + /// # let err = ChainError::new(String::new(), None, None); + /// // Instead of writing + /// err.find_cause::<ChainError<FooErrorKind>>(); + /// // and/or + /// err.find_chain_cause::<FooErrorKind>(); + /// // and/or + /// err.find_cause::<FooErrorKind>(); + /// + /// // leave out the ChainError<FooErrorKind> implementation detail + /// err.find_kind_or_cause::<FooErrorKind>(); + /// ``` + #[inline] + pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> { + self.iter() + .filter_map(|e| { + e.downcast_ref::<ChainError<U>>() + .map(|e| e.kind()) + .or_else(|| e.downcast_ref::<U>()) + }) + .next() + } + + /// Return a reference to T of `ChainError<T>` + /// + /// # Examples + /// + /// ```rust + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// + /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// #[derive(Debug)] + /// enum Func1ErrorKind { + /// Func2, + /// IO(String), + /// } + /// + /// /// impl ::std::fmt::Display for Func1ErrorKind {…} + /// # impl ::std::fmt::Display for Func1ErrorKind { + /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + /// # match self { + /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"), + /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + /// # } + /// # } + /// # } + /// + /// fn func1() -> ChainResult<(), Func1ErrorKind> { + /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// match e.kind() { + /// Func1ErrorKind::Func2 => {} + /// Func1ErrorKind::IO(filename) => panic!(), + /// } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ``` + #[inline] + pub fn kind(&self) -> &T { + &self.kind + } + + /// Returns an Iterator over all error causes/sources + /// + /// # Example + /// + /// + #[inline] + pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> { + ErrorIter { + current: Some(self), + } + } +} + +struct ErrorIter<'a> { + current: Option<&'a (dyn Error + 'static)>, +} + +impl<'a> Iterator for ErrorIter<'a> { + type Item = &'a (dyn Error + 'static); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let current = self.current; + self.current = self.current.and_then(Error::source); + current + } +} + +impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.kind + } +} + +/// Convenience trait to hide the `ChainError<T>` implementation internals +pub trait ChainErrorDown { + /// Test if of type `ChainError<T>` + fn is_chain<T: 'static + Display + Debug>(&self) -> bool; + /// Downcast to a reference of `ChainError<T>` + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; + /// Downcast to a mutable reference of `ChainError<T>` + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; + /// Downcast to T of `ChainError<T>` + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>; + /// Downcast to T mutable reference of `ChainError<T>` + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>; +} + +impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + TypeId::of::<T>() == TypeId::of::<U>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&*(self as *const dyn Error as *const &ChainError<T>)) + } + } else { + None + } + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) + } + } else { + None + } + } + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind) + } + } else { + None + } + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind) + } + } else { + None + } + } +} + +impl ChainErrorDown for dyn Error + 'static { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl ChainErrorDown for dyn Error + 'static + Send { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl ChainErrorDown for dyn Error + 'static + Send + Sync { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl<T: 'static + Display + Debug> Error for ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Error for &ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Display for ChainError<T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.kind)?; + + #[cfg(feature = "display-cause")] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Display::fmt(&e, f)?; + } + } + Ok(()) + } +} + +impl<T: 'static + Display + Debug> Debug for ChainError<T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + #[cfg(not(feature = "no-fileline"))] + { + if let Some(ref o) = self.occurrence { + Display::fmt(o, f)?; + } + } + + if self.is_chain::<String>() { + Display::fmt(&self.kind, f)?; + } else { + Debug::fmt(&self.kind, f)?; + } + + #[cfg(not(feature = "no-debug-cause"))] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Debug::fmt(&e, f)?; + } + } + Ok(()) + } +} + +/// `ChainErrorFrom<T>` is similar to `From<T>` +pub trait ChainErrorFrom<T>: Sized { + /// similar to From<T>::from() + fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>; +} + +/// `IntoChainError<T>` is similar to `Into<T>` +pub trait IntoChainError<T>: Sized { + /// similar to Into<T>::into() + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>; +} + +impl<T, U> IntoChainError<U> for T +where + U: ChainErrorFrom<T>, +{ + #[inline] + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> { + U::chain_error_from(self, line_filename) + } +} + +impl<T, U> ChainErrorFrom<T> for U +where + T: Into<U>, + U: 'static + Display + Debug, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> { + let e: U = t.into(); + ChainError::new(e, None, line_filename) + } +} + +/* +impl<T, U> ChainErrorFrom<T> for U + where + T: 'static + Error + Into<Box<T>> + Clone, + U: 'static + Display + Debug + From<T>, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> { + ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename) + } +} +*/ + +/// map into `ChainError<T>` with `T::from(err)` +/// +/// adds `line!()` and `file!()` information +#[macro_export] +macro_rules! minto_cherr { + ( $k:ident ) => ( + |e| $crate::cherr!(e, $k::from(&e)) + ); + ( $enum:ident $(:: $enum_path:ident)* ) => ( + |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e)) + ); +} + +/// Creates a new `ChainError<T>` +/// +/// # Examples +/// +/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`. +/// ```rust +/// # use chainerror::*; +/// # #[derive(Debug)] +/// enum FooError { +/// Bar, +/// Baz(&'static str), +/// } +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// +/// // impl ::std::fmt::Display for FooError +/// +/// fn do_some_stuff() -> bool { +/// false +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// if ! do_some_stuff() { +/// Err(cherr!(FooError::Baz("Error")))?; +/// } +/// Ok(()) +/// } +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {} +/// # _ => panic!(), +/// # } +/// # } +/// ``` +/// +/// Additionally an error cause can be added. +/// +/// ```rust +/// # use chainerror::*; +/// # use std::io; +/// # use std::error::Error; +/// # #[derive(Debug)] +/// # enum FooError { +/// # Bar, +/// # Baz(&'static str), +/// # } +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> { +/// Err(io::Error::from(io::ErrorKind::NotFound))?; +/// Ok(()) +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// do_some_stuff().map_err( +/// |e| cherr!(e, FooError::Baz("Error")) +/// )?; +/// Ok(()) +/// } +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {} +/// # _ => panic!(), +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! cherr { + ( $k:expr ) => ({ + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + }); + ( None, $k:expr ) => ({ + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + }); + ( None, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!(None, format!($fmt, $($arg)+ )) + }); + ( None, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!(None, format!($fmt, $($arg)+ )) + }); + ( $e:path, $k:expr ) => ({ + $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) + }); + ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!($e, format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro for `|e| cherr!(e, format!(…))` +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +/// func2().map_err(mstrerr!("func1 error"))?; +/// Ok(()) +/// } +/// +/// # fn main() { +/// # if let Err(e) = func1() { +/// # #[cfg(not(windows))] +/// # assert_eq!( +/// # format!("\n{:?}\n", e), r#" +/// # src/lib.rs:18: func1 error +/// # Caused by: +/// # src/lib.rs:13: Error reading 'foo.txt' +/// # Caused by: +/// # Kind(NotFound) +/// # "# +/// # ); +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +/// +/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with +/// `derive_str_cherr!(T)` +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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(()) +/// # } +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! mstrerr { + ( $t:path, $msg:expr ) => ({ + |e| $crate::cherr!(e, $t ($msg.to_string())) + }); + ( $t:path, $msg:expr, ) => ({ + |e| $crate::cherr!(e, $t ($msg.to_string())) + }); + ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ + |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ ))) + }); + ($msg:expr) => ({ + |e| $crate::cherr!(e, $msg.to_string()) + }); + ($msg:expr, ) => ({ + |e| $crate::cherr!(e, $msg.to_string()) + }); + ($fmt:expr, $($arg:tt)+) => ({ + |e| $crate::cherr!(e, format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)` +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # use std::error::Error; +/// # use std::result::Result; +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// Err(strerr!(Func2Error, "Error reading '{}'", filename)) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! strerr { + ( $t:path, $msg:expr ) => ({ + $crate::cherr!($t ($msg.to_string())) + }); + ( $t:path, $msg:expr, ) => ({ + $crate::cherr!($t ($msg.to_string())) + }); + ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!($t (format!($fmt, $($arg)+ ))) + }); + ($msg:expr) => ({ + $crate::cherr!($msg.to_string()) + }); + ($msg:expr, ) => ({ + $crate::cherr!($msg.to_string()) + }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::cherr!(format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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(()) +/// # } +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! derive_str_cherr { + ($e:ident) => { + #[derive(Clone)] + pub struct $e(pub String); + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", self.0) + } + } + impl ::std::fmt::Debug for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}({})", stringify!($e), self.0) + } + } + impl ::std::error::Error for $e {} + }; +} + +/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method +/// +/// It basically hides `ChainError` to the outside and only exposes the `kind()` +/// method. +/// +/// Error::kind() returns the ErrorKind +/// Error::source() returns the parent error +/// +/// # Examples +/// +/// ```rust +/// use std::io; +/// use chainerror::*; +/// +/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { +/// return Err(io::Error::from(io::ErrorKind::NotFound)); +/// } +/// +/// #[derive(Debug, Clone)] +/// pub enum ErrorKind { +/// IO(String), +/// FatalError(String), +/// Unknown, +/// } +/// +/// derive_err_kind!(Error, ErrorKind); +/// +/// impl std::fmt::Display for ErrorKind { +/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { +/// match self { +/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e), +/// ErrorKind::Unknown => write!(f, "unknown error"), +/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), +/// } +/// } +/// } +/// +/// impl ErrorKind { +/// fn from_io_error(e: &io::Error, f: String) -> Self { +/// match e.kind() { +/// io::ErrorKind::BrokenPipe => panic!("Should not happen"), +/// io::ErrorKind::ConnectionReset => { +/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e)) +/// } +/// _ => ErrorKind::IO(f), +/// } +/// } +/// } +/// +/// impl From<&io::Error> for ErrorKind { +/// fn from(e: &io::Error) -> Self { +/// ErrorKind::IO(format!("{}", e)) +/// } +/// } +/// +/// pub fn func1() -> std::result::Result<(), Error> { +/// let filename = "bar.txt"; +/// +/// do_some_io(filename) +/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; +/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! derive_err_kind { + ($e:ident, $k:ident) => { + pub struct $e($crate::ChainError<$k>); + + impl $e { + pub fn kind(&self) -> &$k { + self.0.kind() + } + } + + impl From<$k> for $e { + fn from(e: $k) -> Self { + $e($crate::ChainError::new(e, None, None)) + } + } + + impl From<ChainError<$k>> for $e { + fn from(e: $crate::ChainError<$k>) -> Self { + $e(e) + } + } + + impl From<&$e> for $k + where + $k: Clone, + { + fn from(e: &$e) -> Self { + e.kind().clone() + } + } + + impl $crate::ChainErrorFrom<$e> for $k + where + $k: Clone, + { + #[inline] + fn chain_error_from( + t: $e, + line_filename: Option<&'static str>, + ) -> $crate::ChainError<$k> { + $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename) + } + } + + impl std::error::Error for $e { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } + } + + impl std::fmt::Display for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } + } + + impl std::fmt::Debug for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } + } + }; +} +}To cope with different kind of errors, we introduce the kind of an error Func1ErrorKind
with an enum.
One small improvement at the end of the tutorial is to fix the debug output of @@ -815,14 +11525,14 @@ use std::error::Error; use std::io; use std::result::Result; -fn do_some_io() -> Result<(), Box<Error + Send + Sync>> { +fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); -fn func2() -> Result<(), Box<Error + Send + Sync>> { +fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) @@ -857,7 +11567,7 @@ fn func1() -> ChainResult<(), Func1ErrorKind> { Ok(()) } -fn main() -> Result<(), Box<Error + Send + Sync>> { +fn main() -> Result<(), Box<dyn Error + Send + Sync>> { if let Err(e) = func1() { match e.kind() { Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), @@ -876,8 +11586,1198 @@ fn main() -> Result<(), Box<Error + Send + Sync>> { } #[allow(dead_code)] mod chainerror { -{{#includecomment ../src/lib.rs}} -} +//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your +//! binaries, you still have the error backtrace. +//! +//! `chainerror` has no dependencies! +//! +//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace. +//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally. +//! +//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing. +//! +//! ## Features +//! +//! `no-fileline` +//! : completely turn off storing filename and line +//! +//! `display-cause` +//! : turn on printing a backtrace of the errors in `Display` +//! +//! `no-debug-cause` +//! : turn off printing a backtrace of the errors in `Debug` +//! +//! +//! # Tutorial +//! +//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) +//! +//! # Examples +//! +//! ```rust +//! use chainerror::*; +//! 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().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +//! func2().map_err(mstrerr!("func1 error"))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! if let Err(e) = func1() { +//! #[cfg(not(windows))] +//! assert_eq!( +//! format!("\n{:?}\n", e), r#" +//! src/lib.rs:20: func1 error +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ``` +//! +//! +//! ```rust +//! use chainerror::*; +//! 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().map_err(mstrerr!("Error reading '{}'", filename))?; +//! Ok(()) +//! } +//! +//! derive_str_cherr!(Func2Error); +//! +//! fn func2() -> ChainResult<(), Func2Error> { +//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?; +//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?; +//! let filename = String::from("bar.txt"); +//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; +//! Ok(()) +//! } +//! +//! fn main() { +//! 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:47: func1 error calling func2 +//! Caused by: +//! src/lib.rs:22: Func2Error(func2 error: calling func3) +//! Caused by: +//! src/lib.rs:15: Error reading 'foo.txt' +//! Caused by: +//! Kind(NotFound) +//! "# +//! ); +//! } +//! # else { +//! # unreachable!(); +//! # } +//! } +//! ``` + +#![deny( + warnings, + absolute_paths_not_starting_with_crate, + deprecated_in_future, + keyword_idents, + macro_use_extern_crate, + missing_debug_implementations, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_results, + unused_labels, + unused_lifetimes, + unstable_features, + unreachable_pub, + future_incompatible, + missing_copy_implementations, + missing_doc_code_examples, + rust_2018_idioms, + rust_2018_compatibility +)] + +use std::any::TypeId; +use std::error::Error; +use std::fmt::{Debug, Display, Formatter, Result}; + +/// chains an inner error kind `T` with a causing error +pub struct ChainError<T> { + #[cfg(not(feature = "no-fileline"))] + occurrence: Option<&'static str>, + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, +} + +/// convenience type alias +pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>; + +impl<T: 'static + Display + Debug> ChainError<T> { + #[cfg(not(feature = "no-fileline"))] + /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] + pub fn new( + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, + occurrence: Option<&'static str>, + ) -> Self { + Self { + occurrence, + kind, + error_cause, + } + } + + #[cfg(feature = "no-fileline")] + /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly + #[inline] + pub fn new( + kind: T, + error_cause: Option<Box<dyn Error + 'static + Send + Sync>>, + _occurrence: Option<&'static str>, + ) -> Self { + Self { kind, error_cause } + } + + /// return the root cause of the error chain, if any exists + pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { + self.iter().last() + } + + /// Find the first error cause of type U, if any exists + /// + /// # Examples + /// + /// ```rust + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// + /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func1Error); + /// + /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { + /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { + /// + /// assert!(f1err.find_cause::<io::Error>().is_some()); + /// + /// assert!(f1err.find_chain_cause::<Func2Error>().is_some()); + /// } + /// # else { + /// # panic!(); + /// # } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ``` + #[inline] + pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { + self.iter().filter_map(Error::downcast_ref::<U>).next() + } + + /// Find the first error cause of type `ChainError<U>`, if any exists + /// + /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals + /// + /// # Examples + /// + /// ```rust + /// # use chainerror::*; + /// # derive_str_cherr!(FooError); + /// # let err = ChainError::new(String::new(), None, None); + /// // Instead of writing + /// err.find_cause::<ChainError<FooError>>(); + /// + /// // leave out the ChainError<FooError> implementation detail + /// err.find_chain_cause::<FooError>(); + /// ``` + #[inline] + pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { + self.iter() + .filter_map(Error::downcast_ref::<ChainError<U>>) + .next() + } + + /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U` + /// + /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals + /// + /// # Examples + /// + /// ```rust + /// # use chainerror::*; + /// # derive_str_cherr!(FooErrorKind); + /// # let err = ChainError::new(String::new(), None, None); + /// // Instead of writing + /// err.find_cause::<ChainError<FooErrorKind>>(); + /// // and/or + /// err.find_chain_cause::<FooErrorKind>(); + /// // and/or + /// err.find_cause::<FooErrorKind>(); + /// + /// // leave out the ChainError<FooErrorKind> implementation detail + /// err.find_kind_or_cause::<FooErrorKind>(); + /// ``` + #[inline] + pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> { + self.iter() + .filter_map(|e| { + e.downcast_ref::<ChainError<U>>() + .map(|e| e.kind()) + .or_else(|| e.downcast_ref::<U>()) + }) + .next() + } + + /// Return a reference to T of `ChainError<T>` + /// + /// # Examples + /// + /// ```rust + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// + /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { + /// Err(io::Error::from(io::ErrorKind::NotFound))?; + /// Ok(()) + /// } + /// + /// derive_str_cherr!(Func2Error); + /// + /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { + /// let filename = "foo.txt"; + /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; + /// Ok(()) + /// } + /// + /// #[derive(Debug)] + /// enum Func1ErrorKind { + /// Func2, + /// IO(String), + /// } + /// + /// /// impl ::std::fmt::Display for Func1ErrorKind {…} + /// # impl ::std::fmt::Display for Func1ErrorKind { + /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + /// # match self { + /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"), + /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + /// # } + /// # } + /// # } + /// + /// fn func1() -> ChainResult<(), Func1ErrorKind> { + /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; + /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?; + /// Ok(()) + /// } + /// + /// fn main() { + /// if let Err(e) = func1() { + /// match e.kind() { + /// Func1ErrorKind::Func2 => {} + /// Func1ErrorKind::IO(filename) => panic!(), + /// } + /// } + /// # else { + /// # unreachable!(); + /// # } + /// } + /// ``` + #[inline] + pub fn kind(&self) -> &T { + &self.kind + } + + /// Returns an Iterator over all error causes/sources + /// + /// # Example + /// + /// + #[inline] + pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> { + ErrorIter { + current: Some(self), + } + } +} + +struct ErrorIter<'a> { + current: Option<&'a (dyn Error + 'static)>, +} + +impl<'a> Iterator for ErrorIter<'a> { + type Item = &'a (dyn Error + 'static); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let current = self.current; + self.current = self.current.and_then(Error::source); + current + } +} + +impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.kind + } +} + +/// Convenience trait to hide the `ChainError<T>` implementation internals +pub trait ChainErrorDown { + /// Test if of type `ChainError<T>` + fn is_chain<T: 'static + Display + Debug>(&self) -> bool; + /// Downcast to a reference of `ChainError<T>` + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; + /// Downcast to a mutable reference of `ChainError<T>` + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; + /// Downcast to T of `ChainError<T>` + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>; + /// Downcast to T mutable reference of `ChainError<T>` + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>; +} + +impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + TypeId::of::<T>() == TypeId::of::<U>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&*(self as *const dyn Error as *const &ChainError<T>)) + } + } else { + None + } + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) + } + } else { + None + } + } + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind) + } + } else { + None + } + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is_chain::<T>() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind) + } + } else { + None + } + } +} + +impl ChainErrorDown for dyn Error + 'static { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl ChainErrorDown for dyn Error + 'static + Send { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl ChainErrorDown for dyn Error + 'static + Send + Sync { + #[inline] + fn is_chain<T: 'static + Display + Debug>(&self) -> bool { + self.is::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { + self.downcast_ref::<ChainError<T>>() + } + + #[inline] + fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { + self.downcast_mut::<ChainError<T>>() + } + + #[inline] + fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> { + self.downcast_ref::<T>() + .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + return self.downcast_mut::<T>(); + } + + self.downcast_mut::<ChainError<T>>() + .and_then(|e| e.downcast_inner_mut::<T>()) + } +} + +impl<T: 'static + Display + Debug> Error for ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Error for &ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) + } +} + +impl<T: 'static + Display + Debug> Display for ChainError<T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.kind)?; + + #[cfg(feature = "display-cause")] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Display::fmt(&e, f)?; + } + } + Ok(()) + } +} + +impl<T: 'static + Display + Debug> Debug for ChainError<T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + #[cfg(not(feature = "no-fileline"))] + { + if let Some(ref o) = self.occurrence { + Display::fmt(o, f)?; + } + } + + if self.is_chain::<String>() { + Display::fmt(&self.kind, f)?; + } else { + Debug::fmt(&self.kind, f)?; + } + + #[cfg(not(feature = "no-debug-cause"))] + { + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + Debug::fmt(&e, f)?; + } + } + Ok(()) + } +} + +/// `ChainErrorFrom<T>` is similar to `From<T>` +pub trait ChainErrorFrom<T>: Sized { + /// similar to From<T>::from() + fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>; +} + +/// `IntoChainError<T>` is similar to `Into<T>` +pub trait IntoChainError<T>: Sized { + /// similar to Into<T>::into() + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>; +} + +impl<T, U> IntoChainError<U> for T +where + U: ChainErrorFrom<T>, +{ + #[inline] + fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> { + U::chain_error_from(self, line_filename) + } +} + +impl<T, U> ChainErrorFrom<T> for U +where + T: Into<U>, + U: 'static + Display + Debug, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> { + let e: U = t.into(); + ChainError::new(e, None, line_filename) + } +} + +/* +impl<T, U> ChainErrorFrom<T> for U + where + T: 'static + Error + Into<Box<T>> + Clone, + U: 'static + Display + Debug + From<T>, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> { + ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename) + } +} +*/ + +/// map into `ChainError<T>` with `T::from(err)` +/// +/// adds `line!()` and `file!()` information +#[macro_export] +macro_rules! minto_cherr { + ( $k:ident ) => ( + |e| $crate::cherr!(e, $k::from(&e)) + ); + ( $enum:ident $(:: $enum_path:ident)* ) => ( + |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e)) + ); +} + +/// Creates a new `ChainError<T>` +/// +/// # Examples +/// +/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`. +/// ```rust +/// # use chainerror::*; +/// # #[derive(Debug)] +/// enum FooError { +/// Bar, +/// Baz(&'static str), +/// } +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// +/// // impl ::std::fmt::Display for FooError +/// +/// fn do_some_stuff() -> bool { +/// false +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// if ! do_some_stuff() { +/// Err(cherr!(FooError::Baz("Error")))?; +/// } +/// Ok(()) +/// } +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {} +/// # _ => panic!(), +/// # } +/// # } +/// ``` +/// +/// Additionally an error cause can be added. +/// +/// ```rust +/// # use chainerror::*; +/// # use std::io; +/// # use std::error::Error; +/// # #[derive(Debug)] +/// # enum FooError { +/// # Bar, +/// # Baz(&'static str), +/// # } +/// # impl ::std::fmt::Display for FooError { +/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +/// # match self { +/// # FooError::Bar => write!(f, "Bar Error"), +/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s), +/// # } +/// # } +/// # } +/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> { +/// Err(io::Error::from(io::ErrorKind::NotFound))?; +/// Ok(()) +/// } +/// +/// fn func() -> ChainResult<(), FooError> { +/// do_some_stuff().map_err( +/// |e| cherr!(e, FooError::Baz("Error")) +/// )?; +/// Ok(()) +/// } +/// # pub fn main() { +/// # match func().unwrap_err().kind() { +/// # FooError::Baz(s) if s == &"Error" => {} +/// # _ => panic!(), +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! cherr { + ( $k:expr ) => ({ + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + }); + ( None, $k:expr ) => ({ + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + }); + ( None, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!(None, format!($fmt, $($arg)+ )) + }); + ( None, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!(None, format!($fmt, $($arg)+ )) + }); + ( $e:path, $k:expr ) => ({ + $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) + }); + ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!($e, format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro for `|e| cherr!(e, format!(…))` +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +/// func2().map_err(mstrerr!("func1 error"))?; +/// Ok(()) +/// } +/// +/// # fn main() { +/// # if let Err(e) = func1() { +/// # #[cfg(not(windows))] +/// # assert_eq!( +/// # format!("\n{:?}\n", e), r#" +/// # src/lib.rs:18: func1 error +/// # Caused by: +/// # src/lib.rs:13: Error reading 'foo.txt' +/// # Caused by: +/// # Kind(NotFound) +/// # "# +/// # ); +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +/// +/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with +/// `derive_str_cherr!(T)` +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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(()) +/// # } +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! mstrerr { + ( $t:path, $msg:expr ) => ({ + |e| $crate::cherr!(e, $t ($msg.to_string())) + }); + ( $t:path, $msg:expr, ) => ({ + |e| $crate::cherr!(e, $t ($msg.to_string())) + }); + ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ + |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ ))) + }); + ($msg:expr) => ({ + |e| $crate::cherr!(e, $msg.to_string()) + }); + ($msg:expr, ) => ({ + |e| $crate::cherr!(e, $msg.to_string()) + }); + ($fmt:expr, $($arg:tt)+) => ({ + |e| $crate::cherr!(e, format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)` +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # use std::error::Error; +/// # use std::result::Result; +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// Err(strerr!(Func2Error, "Error reading '{}'", filename)) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! strerr { + ( $t:path, $msg:expr ) => ({ + $crate::cherr!($t ($msg.to_string())) + }); + ( $t:path, $msg:expr, ) => ({ + $crate::cherr!($t ($msg.to_string())) + }); + ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ + $crate::cherr!($t (format!($fmt, $($arg)+ ))) + }); + ($msg:expr) => ({ + $crate::cherr!($msg.to_string()) + }); + ($msg:expr, ) => ({ + $crate::cherr!($msg.to_string()) + }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::cherr!(format!($fmt, $($arg)+ )) + }); +} + +/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T +/// +/// # Examples +/// +/// ```rust +/// # use crate::chainerror::*; +/// # 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(()) +/// # } +/// derive_str_cherr!(Func2Error); +/// +/// fn func2() -> ChainResult<(), Func2Error> { +/// let filename = "foo.txt"; +/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; +/// Ok(()) +/// } +/// +/// derive_str_cherr!(Func1Error); +/// +/// fn func1() -> Result<(), Box<dyn Error>> { +/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; +/// Ok(()) +/// } +/// # fn main() { +/// # if let Err(e) = func1() { +/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { +/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some()); +/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some()); +/// # } else { +/// # panic!(); +/// # } +/// # } else { +/// # unreachable!(); +/// # } +/// # } +/// ``` +#[macro_export] +macro_rules! derive_str_cherr { + ($e:ident) => { + #[derive(Clone)] + pub struct $e(pub String); + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", self.0) + } + } + impl ::std::fmt::Debug for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}({})", stringify!($e), self.0) + } + } + impl ::std::error::Error for $e {} + }; +} + +/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method +/// +/// It basically hides `ChainError` to the outside and only exposes the `kind()` +/// method. +/// +/// Error::kind() returns the ErrorKind +/// Error::source() returns the parent error +/// +/// # Examples +/// +/// ```rust +/// use std::io; +/// use chainerror::*; +/// +/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { +/// return Err(io::Error::from(io::ErrorKind::NotFound)); +/// } +/// +/// #[derive(Debug, Clone)] +/// pub enum ErrorKind { +/// IO(String), +/// FatalError(String), +/// Unknown, +/// } +/// +/// derive_err_kind!(Error, ErrorKind); +/// +/// impl std::fmt::Display for ErrorKind { +/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { +/// match self { +/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e), +/// ErrorKind::Unknown => write!(f, "unknown error"), +/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), +/// } +/// } +/// } +/// +/// impl ErrorKind { +/// fn from_io_error(e: &io::Error, f: String) -> Self { +/// match e.kind() { +/// io::ErrorKind::BrokenPipe => panic!("Should not happen"), +/// io::ErrorKind::ConnectionReset => { +/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e)) +/// } +/// _ => ErrorKind::IO(f), +/// } +/// } +/// } +/// +/// impl From<&io::Error> for ErrorKind { +/// fn from(e: &io::Error) -> Self { +/// ErrorKind::IO(format!("{}", e)) +/// } +/// } +/// +/// pub fn func1() -> std::result::Result<(), Error> { +/// let filename = "bar.txt"; +/// +/// do_some_io(filename) +/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; +/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! derive_err_kind { + ($e:ident, $k:ident) => { + pub struct $e($crate::ChainError<$k>); + + impl $e { + pub fn kind(&self) -> &$k { + self.0.kind() + } + } + + impl From<$k> for $e { + fn from(e: $k) -> Self { + $e($crate::ChainError::new(e, None, None)) + } + } + + impl From<ChainError<$k>> for $e { + fn from(e: $crate::ChainError<$k>) -> Self { + $e(e) + } + } + + impl From<&$e> for $k + where + $k: Clone, + { + fn from(e: &$e) -> Self { + e.kind().clone() + } + } + + impl $crate::ChainErrorFrom<$e> for $k + where + $k: Clone, + { + #[inline] + fn chain_error_from( + t: $e, + line_filename: Option<&'static str>, + ) -> $crate::ChainError<$k> { + $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename) + } + } + + impl std::error::Error for $e { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } + } + + impl std::fmt::Display for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } + } + + impl std::fmt::Debug for $e { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } + } + }; +} +}
Because ChainError I would advise to only expose an *e
instead of e.kind()
@@ -887,14 +12787,14 @@ use std::error::Error;
use std::io;
use std::result::Result;
-fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
+fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
derive_str_cherr!(Func2Error);
-fn func2() -> Result<(), Box<Error + Send + Sync>> {
+fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
let filename = "foo.txt";
do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
Ok(())
@@ -938,7 +12838,7 @@ fn handle_func1errorkind(e: &Func1ErrorKind) {
}
}
-fn main() -> Result<(), Box<Error + Send + Sync>> {
+fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if let Err(e) = func1() {
match *e {
Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"),
@@ -959,8 +12859,1198 @@ fn main() -> Result<(), Box<Error + Send + Sync>> {
}
#[allow(dead_code)]
mod chainerror {
-{{#includecomment ../src/lib.rs}}
-}
+//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your
+//! binaries, you still have the error backtrace.
+//!
+//! `chainerror` has no dependencies!
+//!
+//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace.
+//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally.
+//!
+//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing.
+//!
+//! ## Features
+//!
+//! `no-fileline`
+//! : completely turn off storing filename and line
+//!
+//! `display-cause`
+//! : turn on printing a backtrace of the errors in `Display`
+//!
+//! `no-debug-cause`
+//! : turn off printing a backtrace of the errors in `Debug`
+//!
+//!
+//! # Tutorial
+//!
+//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
+//!
+//! # Examples
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+//! func2().map_err(mstrerr!("func1 error"))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! if let Err(e) = func1() {
+//! #[cfg(not(windows))]
+//! assert_eq!(
+//! format!("\n{:?}\n", e), r#"
+//! src/lib.rs:20: func1 error
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+//!
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! derive_str_cherr!(Func2Error);
+//!
+//! fn func2() -> ChainResult<(), Func2Error> {
+//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?;
+//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?;
+//! let filename = String::from("bar.txt");
+//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! 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:47: func1 error calling func2
+//! Caused by:
+//! src/lib.rs:22: Func2Error(func2 error: calling func3)
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+
+#![deny(
+ warnings,
+ absolute_paths_not_starting_with_crate,
+ deprecated_in_future,
+ keyword_idents,
+ macro_use_extern_crate,
+ missing_debug_implementations,
+ trivial_numeric_casts,
+ unused_extern_crates,
+ unused_import_braces,
+ unused_qualifications,
+ unused_results,
+ unused_labels,
+ unused_lifetimes,
+ unstable_features,
+ unreachable_pub,
+ future_incompatible,
+ missing_copy_implementations,
+ missing_doc_code_examples,
+ rust_2018_idioms,
+ rust_2018_compatibility
+)]
+
+use std::any::TypeId;
+use std::error::Error;
+use std::fmt::{Debug, Display, Formatter, Result};
+
+/// chains an inner error kind `T` with a causing error
+pub struct ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ occurrence: Option<&'static str>,
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+}
+
+/// convenience type alias
+pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>;
+
+impl<T: 'static + Display + Debug> ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ occurrence: Option<&'static str>,
+ ) -> Self {
+ Self {
+ occurrence,
+ kind,
+ error_cause,
+ }
+ }
+
+ #[cfg(feature = "no-fileline")]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ _occurrence: Option<&'static str>,
+ ) -> Self {
+ Self { kind, error_cause }
+ }
+
+ /// return the root cause of the error chain, if any exists
+ pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> {
+ self.iter().last()
+ }
+
+ /// Find the first error cause of type U, if any exists
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func1Error);
+ ///
+ /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+ ///
+ /// assert!(f1err.find_cause::<io::Error>().is_some());
+ ///
+ /// assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+ /// }
+ /// # else {
+ /// # panic!();
+ /// # }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter().filter_map(Error::downcast_ref::<U>).next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>`, if any exists
+ ///
+ /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooError);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooError>>();
+ ///
+ /// // leave out the ChainError<FooError> implementation detail
+ /// err.find_chain_cause::<FooError>();
+ /// ```
+ #[inline]
+ pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> {
+ self.iter()
+ .filter_map(Error::downcast_ref::<ChainError<U>>)
+ .next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U`
+ ///
+ /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooErrorKind);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooErrorKind>>();
+ /// // and/or
+ /// err.find_chain_cause::<FooErrorKind>();
+ /// // and/or
+ /// err.find_cause::<FooErrorKind>();
+ ///
+ /// // leave out the ChainError<FooErrorKind> implementation detail
+ /// err.find_kind_or_cause::<FooErrorKind>();
+ /// ```
+ #[inline]
+ pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter()
+ .filter_map(|e| {
+ e.downcast_ref::<ChainError<U>>()
+ .map(|e| e.kind())
+ .or_else(|| e.downcast_ref::<U>())
+ })
+ .next()
+ }
+
+ /// Return a reference to T of `ChainError<T>`
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// #[derive(Debug)]
+ /// enum Func1ErrorKind {
+ /// Func2,
+ /// IO(String),
+ /// }
+ ///
+ /// /// impl ::std::fmt::Display for Func1ErrorKind {…}
+ /// # impl ::std::fmt::Display for Func1ErrorKind {
+ /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ /// # match self {
+ /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"),
+ /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+ /// # }
+ /// # }
+ /// # }
+ ///
+ /// fn func1() -> ChainResult<(), Func1ErrorKind> {
+ /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?;
+ /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// match e.kind() {
+ /// Func1ErrorKind::Func2 => {}
+ /// Func1ErrorKind::IO(filename) => panic!(),
+ /// }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn kind(&self) -> &T {
+ &self.kind
+ }
+
+ /// Returns an Iterator over all error causes/sources
+ ///
+ /// # Example
+ ///
+ ///
+ #[inline]
+ pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> {
+ ErrorIter {
+ current: Some(self),
+ }
+ }
+}
+
+struct ErrorIter<'a> {
+ current: Option<&'a (dyn Error + 'static)>,
+}
+
+impl<'a> Iterator for ErrorIter<'a> {
+ type Item = &'a (dyn Error + 'static);
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ let current = self.current;
+ self.current = self.current.and_then(Error::source);
+ current
+ }
+}
+
+impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.kind
+ }
+}
+
+/// Convenience trait to hide the `ChainError<T>` implementation internals
+pub trait ChainErrorDown {
+ /// Test if of type `ChainError<T>`
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool;
+ /// Downcast to a reference of `ChainError<T>`
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>;
+ /// Downcast to a mutable reference of `ChainError<T>`
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>;
+ /// Downcast to T of `ChainError<T>`
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>;
+ /// Downcast to T mutable reference of `ChainError<T>`
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>;
+}
+
+impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ TypeId::of::<T>() == TypeId::of::<U>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&*(self as *const dyn Error as *const &ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send + Sync {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &mut ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Display for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ write!(f, "{}", self.kind)?;
+
+ #[cfg(feature = "display-cause")]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Display::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<T: 'static + Display + Debug> Debug for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ #[cfg(not(feature = "no-fileline"))]
+ {
+ if let Some(ref o) = self.occurrence {
+ Display::fmt(o, f)?;
+ }
+ }
+
+ if self.is_chain::<String>() {
+ Display::fmt(&self.kind, f)?;
+ } else {
+ Debug::fmt(&self.kind, f)?;
+ }
+
+ #[cfg(not(feature = "no-debug-cause"))]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Debug::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// `ChainErrorFrom<T>` is similar to `From<T>`
+pub trait ChainErrorFrom<T>: Sized {
+ /// similar to From<T>::from()
+ fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>;
+}
+
+/// `IntoChainError<T>` is similar to `Into<T>`
+pub trait IntoChainError<T>: Sized {
+ /// similar to Into<T>::into()
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>;
+}
+
+impl<T, U> IntoChainError<U> for T
+where
+ U: ChainErrorFrom<T>,
+{
+ #[inline]
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> {
+ U::chain_error_from(self, line_filename)
+ }
+}
+
+impl<T, U> ChainErrorFrom<T> for U
+where
+ T: Into<U>,
+ U: 'static + Display + Debug,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ let e: U = t.into();
+ ChainError::new(e, None, line_filename)
+ }
+}
+
+/*
+impl<T, U> ChainErrorFrom<T> for U
+ where
+ T: 'static + Error + Into<Box<T>> + Clone,
+ U: 'static + Display + Debug + From<T>,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename)
+ }
+}
+*/
+
+/// map into `ChainError<T>` with `T::from(err)`
+///
+/// adds `line!()` and `file!()` information
+#[macro_export]
+macro_rules! minto_cherr {
+ ( $k:ident ) => (
+ |e| $crate::cherr!(e, $k::from(&e))
+ );
+ ( $enum:ident $(:: $enum_path:ident)* ) => (
+ |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e))
+ );
+}
+
+/// Creates a new `ChainError<T>`
+///
+/// # Examples
+///
+/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`.
+/// ```rust
+/// # use chainerror::*;
+/// # #[derive(Debug)]
+/// enum FooError {
+/// Bar,
+/// Baz(&'static str),
+/// }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+///
+/// // impl ::std::fmt::Display for FooError
+///
+/// fn do_some_stuff() -> bool {
+/// false
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// if ! do_some_stuff() {
+/// Err(cherr!(FooError::Baz("Error")))?;
+/// }
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+///
+/// Additionally an error cause can be added.
+///
+/// ```rust
+/// # use chainerror::*;
+/// # use std::io;
+/// # use std::error::Error;
+/// # #[derive(Debug)]
+/// # enum FooError {
+/// # Bar,
+/// # Baz(&'static str),
+/// # }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// Err(io::Error::from(io::ErrorKind::NotFound))?;
+/// Ok(())
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// do_some_stuff().map_err(
+/// |e| cherr!(e, FooError::Baz("Error"))
+/// )?;
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! cherr {
+ ( $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( $e:path, $k:expr ) => ({
+ $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `|e| cherr!(e, format!(…))`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!("func1 error"))?;
+/// Ok(())
+/// }
+///
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # #[cfg(not(windows))]
+/// # assert_eq!(
+/// # format!("\n{:?}\n", e), r#"
+/// # src/lib.rs:18: func1 error
+/// # Caused by:
+/// # src/lib.rs:13: Error reading 'foo.txt'
+/// # Caused by:
+/// # Kind(NotFound)
+/// # "#
+/// # );
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+///
+/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with
+/// `derive_str_cherr!(T)`
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! mstrerr {
+ ( $t:path, $msg:expr ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ |e| $crate::cherr!(e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # use std::error::Error;
+/// # use std::result::Result;
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// Err(strerr!(Func2Error, "Error reading '{}'", filename))
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! strerr {
+ ( $t:path, $msg:expr ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ $crate::cherr!(format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! derive_str_cherr {
+ ($e:ident) => {
+ #[derive(Clone)]
+ pub struct $e(pub String);
+ impl ::std::fmt::Display for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+ impl ::std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}({})", stringify!($e), self.0)
+ }
+ }
+ impl ::std::error::Error for $e {}
+ };
+}
+
+/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method
+///
+/// It basically hides `ChainError` to the outside and only exposes the `kind()`
+/// method.
+///
+/// Error::kind() returns the ErrorKind
+/// Error::source() returns the parent error
+///
+/// # Examples
+///
+/// ```rust
+/// use std::io;
+/// use chainerror::*;
+///
+/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
+/// return Err(io::Error::from(io::ErrorKind::NotFound));
+/// }
+///
+/// #[derive(Debug, Clone)]
+/// pub enum ErrorKind {
+/// IO(String),
+/// FatalError(String),
+/// Unknown,
+/// }
+///
+/// derive_err_kind!(Error, ErrorKind);
+///
+/// impl std::fmt::Display for ErrorKind {
+/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
+/// match self {
+/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e),
+/// ErrorKind::Unknown => write!(f, "unknown error"),
+/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+/// }
+/// }
+/// }
+///
+/// impl ErrorKind {
+/// fn from_io_error(e: &io::Error, f: String) -> Self {
+/// match e.kind() {
+/// io::ErrorKind::BrokenPipe => panic!("Should not happen"),
+/// io::ErrorKind::ConnectionReset => {
+/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e))
+/// }
+/// _ => ErrorKind::IO(f),
+/// }
+/// }
+/// }
+///
+/// impl From<&io::Error> for ErrorKind {
+/// fn from(e: &io::Error) -> Self {
+/// ErrorKind::IO(format!("{}", e))
+/// }
+/// }
+///
+/// pub fn func1() -> std::result::Result<(), Error> {
+/// let filename = "bar.txt";
+///
+/// do_some_io(filename)
+/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?;
+/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?;
+/// Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! derive_err_kind {
+ ($e:ident, $k:ident) => {
+ pub struct $e($crate::ChainError<$k>);
+
+ impl $e {
+ pub fn kind(&self) -> &$k {
+ self.0.kind()
+ }
+ }
+
+ impl From<$k> for $e {
+ fn from(e: $k) -> Self {
+ $e($crate::ChainError::new(e, None, None))
+ }
+ }
+
+ impl From<ChainError<$k>> for $e {
+ fn from(e: $crate::ChainError<$k>) -> Self {
+ $e(e)
+ }
+ }
+
+ impl From<&$e> for $k
+ where
+ $k: Clone,
+ {
+ fn from(e: &$e) -> Self {
+ e.kind().clone()
+ }
+ }
+
+ impl $crate::ChainErrorFrom<$e> for $k
+ where
+ $k: Clone,
+ {
+ #[inline]
+ fn chain_error_from(
+ t: $e,
+ line_filename: Option<&'static str>,
+ ) -> $crate::ChainError<$k> {
+ $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename)
+ }
+ }
+
+ impl std::error::Error for $e {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.0.source()
+ }
+ }
+
+ impl std::fmt::Display for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+ }
+
+ impl std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Debug::fmt(&self.0, f)
+ }
+ }
+ };
+}
+}
Writing a library
mycrate::ErrorKind
and type alias mycrate::Error
to ChainError<mycrate::ErrorKind>
@@ -970,20 +14060,1210 @@ have to change much or anything.#[allow(dead_code)]
#[macro_use]
pub mod chainerror {
-{{#includecomment ../src/lib.rs}}
-}
+//! `chainerror` provides an error backtrace without doing a real backtrace, so even after you `strip` your
+//! binaries, you still have the error backtrace.
+//!
+//! `chainerror` has no dependencies!
+//!
+//! `chainerror` uses `.source()` of `std::error::Error` along with `line()!` and `file()!` to provide a nice debug error backtrace.
+//! It encapsulates all types, which have `Display + Debug` and can store the error cause internally.
+//!
+//! Along with the `ChainError<T>` struct, `chainerror` comes with some useful helper macros to save a lot of typing.
+//!
+//! ## Features
+//!
+//! `no-fileline`
+//! : completely turn off storing filename and line
+//!
+//! `display-cause`
+//! : turn on printing a backtrace of the errors in `Display`
+//!
+//! `no-debug-cause`
+//! : turn off printing a backtrace of the errors in `Debug`
+//!
+//!
+//! # Tutorial
+//!
+//! Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html)
+//!
+//! # Examples
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+//! func2().map_err(mstrerr!("func1 error"))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! if let Err(e) = func1() {
+//! #[cfg(not(windows))]
+//! assert_eq!(
+//! format!("\n{:?}\n", e), r#"
+//! src/lib.rs:20: func1 error
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+//!
+//!
+//! ```rust
+//! use chainerror::*;
+//! 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+//! Ok(())
+//! }
+//!
+//! derive_str_cherr!(Func2Error);
+//!
+//! fn func2() -> ChainResult<(), Func2Error> {
+//! func3().map_err(mstrerr!(Func2Error, "func2 error: calling func3"))?;
+//! 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().map_err(|e| cherr!(e, Func1Error::Func2))?;
+//! let filename = String::from("bar.txt");
+//! do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?;
+//! Ok(())
+//! }
+//!
+//! fn main() {
+//! 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:47: func1 error calling func2
+//! Caused by:
+//! src/lib.rs:22: Func2Error(func2 error: calling func3)
+//! Caused by:
+//! src/lib.rs:15: Error reading 'foo.txt'
+//! Caused by:
+//! Kind(NotFound)
+//! "#
+//! );
+//! }
+//! # else {
+//! # unreachable!();
+//! # }
+//! }
+//! ```
+
+#![deny(
+ warnings,
+ absolute_paths_not_starting_with_crate,
+ deprecated_in_future,
+ keyword_idents,
+ macro_use_extern_crate,
+ missing_debug_implementations,
+ trivial_numeric_casts,
+ unused_extern_crates,
+ unused_import_braces,
+ unused_qualifications,
+ unused_results,
+ unused_labels,
+ unused_lifetimes,
+ unstable_features,
+ unreachable_pub,
+ future_incompatible,
+ missing_copy_implementations,
+ missing_doc_code_examples,
+ rust_2018_idioms,
+ rust_2018_compatibility
+)]
+
+use std::any::TypeId;
+use std::error::Error;
+use std::fmt::{Debug, Display, Formatter, Result};
+
+/// chains an inner error kind `T` with a causing error
+pub struct ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ occurrence: Option<&'static str>,
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+}
+
+/// convenience type alias
+pub type ChainResult<O, E> = std::result::Result<O, ChainError<E>>;
+
+impl<T: 'static + Display + Debug> ChainError<T> {
+ #[cfg(not(feature = "no-fileline"))]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ occurrence: Option<&'static str>,
+ ) -> Self {
+ Self {
+ occurrence,
+ kind,
+ error_cause,
+ }
+ }
+
+ #[cfg(feature = "no-fileline")]
+ /// Use the `cherr!()` or `mstrerr!()` macro instead of calling this directly
+ #[inline]
+ pub fn new(
+ kind: T,
+ error_cause: Option<Box<dyn Error + 'static + Send + Sync>>,
+ _occurrence: Option<&'static str>,
+ ) -> Self {
+ Self { kind, error_cause }
+ }
+
+ /// return the root cause of the error chain, if any exists
+ pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> {
+ self.iter().last()
+ }
+
+ /// Find the first error cause of type U, if any exists
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func1Error);
+ ///
+ /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+ ///
+ /// assert!(f1err.find_cause::<io::Error>().is_some());
+ ///
+ /// assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+ /// }
+ /// # else {
+ /// # panic!();
+ /// # }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter().filter_map(Error::downcast_ref::<U>).next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>`, if any exists
+ ///
+ /// Same as `find_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooError);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooError>>();
+ ///
+ /// // leave out the ChainError<FooError> implementation detail
+ /// err.find_chain_cause::<FooError>();
+ /// ```
+ #[inline]
+ pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> {
+ self.iter()
+ .filter_map(Error::downcast_ref::<ChainError<U>>)
+ .next()
+ }
+
+ /// Find the first error cause of type `ChainError<U>` or `U`, if any exists and return `U`
+ ///
+ /// Same as `find_cause` and `find_chain_cause`, but hides the `ChainError<U>` implementation internals
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use chainerror::*;
+ /// # derive_str_cherr!(FooErrorKind);
+ /// # let err = ChainError::new(String::new(), None, None);
+ /// // Instead of writing
+ /// err.find_cause::<ChainError<FooErrorKind>>();
+ /// // and/or
+ /// err.find_chain_cause::<FooErrorKind>();
+ /// // and/or
+ /// err.find_cause::<FooErrorKind>();
+ ///
+ /// // leave out the ChainError<FooErrorKind> implementation detail
+ /// err.find_kind_or_cause::<FooErrorKind>();
+ /// ```
+ #[inline]
+ pub fn find_kind_or_cause<U: Error + 'static>(&self) -> Option<&U> {
+ self.iter()
+ .filter_map(|e| {
+ e.downcast_ref::<ChainError<U>>()
+ .map(|e| e.kind())
+ .or_else(|| e.downcast_ref::<U>())
+ })
+ .next()
+ }
+
+ /// Return a reference to T of `ChainError<T>`
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use chainerror::*;
+ /// use std::error::Error;
+ /// use std::io;
+ ///
+ /// fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// Err(io::Error::from(io::ErrorKind::NotFound))?;
+ /// Ok(())
+ /// }
+ ///
+ /// derive_str_cherr!(Func2Error);
+ ///
+ /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+ /// let filename = "foo.txt";
+ /// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+ /// Ok(())
+ /// }
+ ///
+ /// #[derive(Debug)]
+ /// enum Func1ErrorKind {
+ /// Func2,
+ /// IO(String),
+ /// }
+ ///
+ /// /// impl ::std::fmt::Display for Func1ErrorKind {…}
+ /// # impl ::std::fmt::Display for Func1ErrorKind {
+ /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ /// # match self {
+ /// # Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"),
+ /// # Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+ /// # }
+ /// # }
+ /// # }
+ ///
+ /// fn func1() -> ChainResult<(), Func1ErrorKind> {
+ /// func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?;
+ /// do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO("bar.txt".into())))?;
+ /// Ok(())
+ /// }
+ ///
+ /// fn main() {
+ /// if let Err(e) = func1() {
+ /// match e.kind() {
+ /// Func1ErrorKind::Func2 => {}
+ /// Func1ErrorKind::IO(filename) => panic!(),
+ /// }
+ /// }
+ /// # else {
+ /// # unreachable!();
+ /// # }
+ /// }
+ /// ```
+ #[inline]
+ pub fn kind(&self) -> &T {
+ &self.kind
+ }
+
+ /// Returns an Iterator over all error causes/sources
+ ///
+ /// # Example
+ ///
+ ///
+ #[inline]
+ pub fn iter(&self) -> impl Iterator<Item = &(dyn Error + 'static)> {
+ ErrorIter {
+ current: Some(self),
+ }
+ }
+}
+
+struct ErrorIter<'a> {
+ current: Option<&'a (dyn Error + 'static)>,
+}
+
+impl<'a> Iterator for ErrorIter<'a> {
+ type Item = &'a (dyn Error + 'static);
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ let current = self.current;
+ self.current = self.current.and_then(Error::source);
+ current
+ }
+}
+
+impl<T: 'static + Display + Debug> std::ops::Deref for ChainError<T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.kind
+ }
+}
+
+/// Convenience trait to hide the `ChainError<T>` implementation internals
+pub trait ChainErrorDown {
+ /// Test if of type `ChainError<T>`
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool;
+ /// Downcast to a reference of `ChainError<T>`
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>;
+ /// Downcast to a mutable reference of `ChainError<T>`
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>;
+ /// Downcast to T of `ChainError<T>`
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>;
+ /// Downcast to T mutable reference of `ChainError<T>`
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>;
+}
+
+impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ TypeId::of::<T>() == TypeId::of::<U>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&*(self as *const dyn Error as *const &ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>))
+ }
+ } else {
+ None
+ }
+ }
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is_chain::<T>() {
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe {
+ #[allow(trivial_casts)]
+ Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind)
+ }
+ } else {
+ None
+ }
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl ChainErrorDown for dyn Error + 'static + Send + Sync {
+ #[inline]
+ fn is_chain<T: 'static + Display + Debug>(&self) -> bool {
+ self.is::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
+ self.downcast_ref::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
+ self.downcast_mut::<ChainError<T>>()
+ }
+
+ #[inline]
+ fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
+ self.downcast_ref::<T>()
+ .or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
+ }
+
+ #[inline]
+ fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
+ if self.is::<T>() {
+ return self.downcast_mut::<T>();
+ }
+
+ self.downcast_mut::<ChainError<T>>()
+ .and_then(|e| e.downcast_inner_mut::<T>())
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Error for &mut ChainError<T> {
+ #[inline]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.error_cause
+ .as_ref()
+ .map(|e| e.as_ref() as &(dyn Error + 'static))
+ }
+}
+
+impl<T: 'static + Display + Debug> Display for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ write!(f, "{}", self.kind)?;
+
+ #[cfg(feature = "display-cause")]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Display::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<T: 'static + Display + Debug> Debug for ChainError<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ #[cfg(not(feature = "no-fileline"))]
+ {
+ if let Some(ref o) = self.occurrence {
+ Display::fmt(o, f)?;
+ }
+ }
+
+ if self.is_chain::<String>() {
+ Display::fmt(&self.kind, f)?;
+ } else {
+ Debug::fmt(&self.kind, f)?;
+ }
+
+ #[cfg(not(feature = "no-debug-cause"))]
+ {
+ if let Some(e) = self.source() {
+ writeln!(f, "\nCaused by:")?;
+ Debug::fmt(&e, f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// `ChainErrorFrom<T>` is similar to `From<T>`
+pub trait ChainErrorFrom<T>: Sized {
+ /// similar to From<T>::from()
+ fn chain_error_from(from: T, line_filename: Option<&'static str>) -> ChainError<Self>;
+}
+
+/// `IntoChainError<T>` is similar to `Into<T>`
+pub trait IntoChainError<T>: Sized {
+ /// similar to Into<T>::into()
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<T>;
+}
+
+impl<T, U> IntoChainError<U> for T
+where
+ U: ChainErrorFrom<T>,
+{
+ #[inline]
+ fn into_chain_error(self, line_filename: Option<&'static str>) -> ChainError<U> {
+ U::chain_error_from(self, line_filename)
+ }
+}
+
+impl<T, U> ChainErrorFrom<T> for U
+where
+ T: Into<U>,
+ U: 'static + Display + Debug,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ let e: U = t.into();
+ ChainError::new(e, None, line_filename)
+ }
+}
+
+/*
+impl<T, U> ChainErrorFrom<T> for U
+ where
+ T: 'static + Error + Into<Box<T>> + Clone,
+ U: 'static + Display + Debug + From<T>,
+{
+ #[inline]
+ fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
+ ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename)
+ }
+}
+*/
+
+/// map into `ChainError<T>` with `T::from(err)`
+///
+/// adds `line!()` and `file!()` information
+#[macro_export]
+macro_rules! minto_cherr {
+ ( $k:ident ) => (
+ |e| $crate::cherr!(e, $k::from(&e))
+ );
+ ( $enum:ident $(:: $enum_path:ident)* ) => (
+ |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e))
+ );
+}
+
+/// Creates a new `ChainError<T>`
+///
+/// # Examples
+///
+/// Create a new ChainError<FooError>, where `FooError` must implement `Display` and `Debug`.
+/// ```rust
+/// # use chainerror::*;
+/// # #[derive(Debug)]
+/// enum FooError {
+/// Bar,
+/// Baz(&'static str),
+/// }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+///
+/// // impl ::std::fmt::Display for FooError
+///
+/// fn do_some_stuff() -> bool {
+/// false
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// if ! do_some_stuff() {
+/// Err(cherr!(FooError::Baz("Error")))?;
+/// }
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+///
+/// Additionally an error cause can be added.
+///
+/// ```rust
+/// # use chainerror::*;
+/// # use std::io;
+/// # use std::error::Error;
+/// # #[derive(Debug)]
+/// # enum FooError {
+/// # Bar,
+/// # Baz(&'static str),
+/// # }
+/// # impl ::std::fmt::Display for FooError {
+/// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+/// # match self {
+/// # FooError::Bar => write!(f, "Bar Error"),
+/// # FooError::Baz(s) => write!(f, "Baz Error: '{}'", s),
+/// # }
+/// # }
+/// # }
+/// fn do_some_stuff() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// Err(io::Error::from(io::ErrorKind::NotFound))?;
+/// Ok(())
+/// }
+///
+/// fn func() -> ChainResult<(), FooError> {
+/// do_some_stuff().map_err(
+/// |e| cherr!(e, FooError::Baz("Error"))
+/// )?;
+/// Ok(())
+/// }
+/// # pub fn main() {
+/// # match func().unwrap_err().kind() {
+/// # FooError::Baz(s) if s == &"Error" => {}
+/// # _ => panic!(),
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! cherr {
+ ( $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $k:expr ) => ({
+ $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( None, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!(None, format!($fmt, $($arg)+ ))
+ });
+ ( $e:path, $k:expr ) => ({
+ $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
+ });
+ ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `|e| cherr!(e, format!(…))`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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().map_err(mstrerr!("Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!("func1 error"))?;
+/// Ok(())
+/// }
+///
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # #[cfg(not(windows))]
+/// # assert_eq!(
+/// # format!("\n{:?}\n", e), r#"
+/// # src/lib.rs:18: func1 error
+/// # Caused by:
+/// # src/lib.rs:13: Error reading 'foo.txt'
+/// # Caused by:
+/// # Kind(NotFound)
+/// # "#
+/// # );
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+///
+/// `mstrerr!()` can also be used to map a new `ChainError<T>`, where T was defined with
+/// `derive_str_cherr!(T)`
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! mstrerr {
+ ( $t:path, $msg:expr ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ |e| $crate::cherr!(e, $t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ |e| $crate::cherr!(e, $msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ |e| $crate::cherr!(e, format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro for `cherr!(T(format!(…)))` where `T(String)`
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # use std::error::Error;
+/// # use std::result::Result;
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// Err(strerr!(Func2Error, "Error reading '{}'", filename))
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! strerr {
+ ( $t:path, $msg:expr ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $msg:expr, ) => ({
+ $crate::cherr!($t ($msg.to_string()))
+ });
+ ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
+ $crate::cherr!($t (format!($fmt, $($arg)+ )))
+ });
+ ($msg:expr) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($msg:expr, ) => ({
+ $crate::cherr!($msg.to_string())
+ });
+ ($fmt:expr, $($arg:tt)+) => ({
+ $crate::cherr!(format!($fmt, $($arg)+ ))
+ });
+}
+
+/// Convenience macro to create a "new type" T(String) and implement Display + Debug for T
+///
+/// # Examples
+///
+/// ```rust
+/// # use crate::chainerror::*;
+/// # 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(())
+/// # }
+/// derive_str_cherr!(Func2Error);
+///
+/// fn func2() -> ChainResult<(), Func2Error> {
+/// let filename = "foo.txt";
+/// do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
+/// Ok(())
+/// }
+///
+/// derive_str_cherr!(Func1Error);
+///
+/// fn func1() -> Result<(), Box<dyn Error>> {
+/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
+/// Ok(())
+/// }
+/// # fn main() {
+/// # if let Err(e) = func1() {
+/// # if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
+/// # assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
+/// # assert!(f1err.find_chain_cause::<Func2Error>().is_some());
+/// # } else {
+/// # panic!();
+/// # }
+/// # } else {
+/// # unreachable!();
+/// # }
+/// # }
+/// ```
+#[macro_export]
+macro_rules! derive_str_cherr {
+ ($e:ident) => {
+ #[derive(Clone)]
+ pub struct $e(pub String);
+ impl ::std::fmt::Display for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+ impl ::std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(f, "{}({})", stringify!($e), self.0)
+ }
+ }
+ impl ::std::error::Error for $e {}
+ };
+}
+
+/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method
+///
+/// It basically hides `ChainError` to the outside and only exposes the `kind()`
+/// method.
+///
+/// Error::kind() returns the ErrorKind
+/// Error::source() returns the parent error
+///
+/// # Examples
+///
+/// ```rust
+/// use std::io;
+/// use chainerror::*;
+///
+/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
+/// return Err(io::Error::from(io::ErrorKind::NotFound));
+/// }
+///
+/// #[derive(Debug, Clone)]
+/// pub enum ErrorKind {
+/// IO(String),
+/// FatalError(String),
+/// Unknown,
+/// }
+///
+/// derive_err_kind!(Error, ErrorKind);
+///
+/// impl std::fmt::Display for ErrorKind {
+/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
+/// match self {
+/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e),
+/// ErrorKind::Unknown => write!(f, "unknown error"),
+/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
+/// }
+/// }
+/// }
+///
+/// impl ErrorKind {
+/// fn from_io_error(e: &io::Error, f: String) -> Self {
+/// match e.kind() {
+/// io::ErrorKind::BrokenPipe => panic!("Should not happen"),
+/// io::ErrorKind::ConnectionReset => {
+/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e))
+/// }
+/// _ => ErrorKind::IO(f),
+/// }
+/// }
+/// }
+///
+/// impl From<&io::Error> for ErrorKind {
+/// fn from(e: &io::Error) -> Self {
+/// ErrorKind::IO(format!("{}", e))
+/// }
+/// }
+///
+/// pub fn func1() -> std::result::Result<(), Error> {
+/// let filename = "bar.txt";
+///
+/// do_some_io(filename)
+/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?;
+/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?;
+/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?;
+/// Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! derive_err_kind {
+ ($e:ident, $k:ident) => {
+ pub struct $e($crate::ChainError<$k>);
+
+ impl $e {
+ pub fn kind(&self) -> &$k {
+ self.0.kind()
+ }
+ }
+
+ impl From<$k> for $e {
+ fn from(e: $k) -> Self {
+ $e($crate::ChainError::new(e, None, None))
+ }
+ }
+
+ impl From<ChainError<$k>> for $e {
+ fn from(e: $crate::ChainError<$k>) -> Self {
+ $e(e)
+ }
+ }
+
+ impl From<&$e> for $k
+ where
+ $k: Clone,
+ {
+ fn from(e: &$e) -> Self {
+ e.kind().clone()
+ }
+ }
+
+ impl $crate::ChainErrorFrom<$e> for $k
+ where
+ $k: Clone,
+ {
+ #[inline]
+ fn chain_error_from(
+ t: $e,
+ line_filename: Option<&'static str>,
+ ) -> $crate::ChainError<$k> {
+ $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename)
+ }
+ }
+
+ impl std::error::Error for $e {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.0.source()
+ }
+ }
+
+ impl std::fmt::Display for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+ }
+
+ impl std::fmt::Debug for $e {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Debug::fmt(&self.0, f)
+ }
+ }
+ };
+}
+}
pub mod mycrate {
use crate::chainerror::*; // omit the `crate::` part
use std::io;
- fn do_some_io() -> std::result::Result<(), Box<std::error::Error + Send + Sync>> {
+ fn do_some_io() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
derive_str_cherr!(Func2Error);
- fn func2() -> std::result::Result<(), Box<std::error::Error + Send + Sync>> {
+ fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filename = "foo.txt";
do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
Ok(())
@@ -1016,7 +15296,7 @@ have to change much or anything.