trace_error/lib.rs
1//! Extensions to Rust's error system to automatically include backtraces
2//! to the exact location an error originates.
3//!
4//! Consider this a more lightweight and less macro-based alternative to `error_chain` and similar crates. This crate
5//! does not take care of actually defining the errors and their varieties, but only focuses on a thin container
6//! for holding the errors and a backtrace to their origin.
7//!
8//! `Trace` and `TraceResult` should usually be used in place of `Result` using the macros
9//! `throw!`, `try_throw!`, and `try_rethrow!`
10//!
11//! Although the `?` syntax was just introduced, `trace-error` is not yet compatible with it until the `Carrier` trait is stabilized. As a result,
12//! all instances of `try!` and `?` should be replaced with `try_throw!` if you intend to use this crate to its fullest. However, the `?` operator
13//! can be used for `Result<_, Trace<E>>` when the return value is also a `Result` using `Trace<E>`, just because `From` is implemented for types for itself.
14//!
15//! If the `Trace` being returned in a result does **NOT** contain the same error type, but they are convertible, use `try_rethrow!` to convert the inner error type.
16//!
17//! Additionally, if you must use the `Result<T, Trace<E>>` directly instead of immediately returning it, you can use the `trace_error!` macro to create it with the desired error value.
18//!
19//! Example:
20//!
21//! ```
22//! #[macro_use]
23//! extern crate trace_error;
24//!
25//! use std::error::Error;
26//! use std::fmt::{Display, Formatter, Result as FmtResult};
27//! use std::io;
28//! use std::fs::File;
29//!
30//! use trace_error::TraceResult;
31//!
32//! pub type MyResultType<T> = TraceResult<T, MyErrorType>;
33//!
34//! #[derive(Debug)]
35//! pub enum MyErrorType {
36//! Io(io::Error),
37//! ErrorOne,
38//! ErrorTwo,
39//! //etc
40//! }
41//!
42//! impl Display for MyErrorType {
43//! fn fmt(&self, f: &mut Formatter) -> FmtResult {
44//! write!(f, "{}", self.description())
45//! }
46//! }
47//!
48//! impl Error for MyErrorType {
49//! fn description(&self) -> &str {
50//! match *self {
51//! MyErrorType::Io(ref err) => err.description(),
52//! MyErrorType::ErrorOne => "Error One",
53//! MyErrorType::ErrorTwo => "Error Two",
54//! }
55//! }
56//! }
57//!
58//! impl From<io::Error> for MyErrorType {
59//! fn from(err: io::Error) -> MyErrorType {
60//! MyErrorType::Io(err)
61//! }
62//! }
63//!
64//! fn basic() -> MyResultType<i32> {
65//! Ok(42)
66//! }
67//!
68//! fn example() -> MyResultType<()> {
69//! // Note the use of try_rethrow! for TraceResult results
70//! let meaning = try_rethrow!(basic());
71//!
72//! // Prints 42 if `basic` succeeds
73//! println!("{}", meaning);
74//!
75//! // Note the use of try_throw! for non-TraceResult results
76//! let some_file = try_throw!(File::open("Cargo.toml"));
77//!
78//! Ok(())
79//! }
80//!
81//! fn main() {
82//! match example() {
83//! Ok(_) => println!("Success!"),
84//! // Here, err is the Trace<E>, which can be printed normally,
85//! // showing both the error and the backtrace.
86//! Err(err) => println!("Error: {}", err)
87//! }
88//! }
89//! ```
90#![allow(unknown_lints, inline_always)]
91#![deny(missing_docs)]
92
93extern crate backtrace as bt;
94
95pub mod backtrace;
96
97use std::error::Error;
98use std::ops::Deref;
99use std::fmt::{Display, Formatter, Result as FmtResult};
100
101use backtrace::{BacktraceFmt, DefaultBacktraceFmt, SourceBacktrace};
102
103/// Alias to aid in usage with `Result`
104pub type TraceResult<T, E> = Result<T, Trace<E>>;
105
106/// Trace error that encapsulates a backtrace alongside an error value.
107///
108/// Trace itself does not implement `Error`, so they cannot be nested.
109#[derive(Debug)]
110pub struct Trace<E: Error> {
111 error: E,
112 backtrace: Box<SourceBacktrace>,
113}
114
115impl<E: Error> Trace<E> {
116 /// Creates a new `Trace` from the given error and backtrace
117 #[inline]
118 pub fn new(error: E, backtrace: Box<SourceBacktrace>) -> Trace<E> {
119 Trace { error: error, backtrace: backtrace }
120 }
121
122 /// Consume self and return the inner error value
123 #[inline]
124 pub fn into_error(self) -> E {
125 self.error
126 }
127
128 /// Get a reference to the inner backtrace
129 #[inline]
130 pub fn backtrace(&self) -> &SourceBacktrace {
131 &*self.backtrace
132 }
133
134 /// Format the error and backtrace
135 pub fn format<Fmt: BacktraceFmt>(&self, header: bool, reverse: bool) -> String {
136 format!("{}\n{}", self.error, self.backtrace.format::<Fmt>(header, reverse))
137 }
138
139 /// Convert the inner error of type `E` into type `O`
140 #[inline]
141 pub fn convert<O: Error>(self) -> Trace<O> where O: From<E> {
142 Trace {
143 error: From::from(self.error),
144 backtrace: self.backtrace
145 }
146 }
147}
148
149unsafe impl<E: Error> Send for Trace<E> where E: Send {}
150
151impl<E: Error> Deref for Trace<E> {
152 type Target = E;
153
154 #[inline]
155 fn deref(&self) -> &E {
156 &self.error
157 }
158}
159
160impl<E: Error> Display for Trace<E> {
161 fn fmt(&self, f: &mut Formatter) -> FmtResult {
162 write!(f, "{}", self.format::<DefaultBacktraceFmt>(true, false))
163 }
164}
165
166/// Creates a new `Result::Err(Trace<E>)` and immediately returns it.
167///
168/// This relies on the return type of the function to
169/// provide type inference for the `Result::Ok(T)` type.
170#[macro_export]
171macro_rules! throw {
172 ($err:expr) => { return trace_error!($err) }
173}
174
175/// Like `try!`, but invokes `throw!` on the error value if it exists, converting it to `Result::Err(Trace<E>)`
176///
177/// Note that the backtrace will only go as far as the location this macro was invoked
178///
179/// This macro will try to call `From::from` on the error to convert it if necessary, just like `try!`
180///
181/// This relies on the return type of the function to
182/// provide type inference for the `Result::Ok(T)` type.
183#[macro_export]
184macro_rules! try_throw {
185 ($res:expr) => (match $res {
186 ::std::result::Result::Ok(val) => val,
187 ::std::result::Result::Err(err) => { throw!(err) }
188 })
189}
190
191#[doc(hidden)]
192#[inline(always)]
193pub fn _assert_trace_result<T, E: Error>(res: TraceResult<T, E>) -> TraceResult<T, E> {
194 res
195}
196
197/// Like `try_throw!`, but designed for `TraceResult`s, as it keeps the previous trace.
198///
199/// This macro will try to call `Trace::convert` on the trace to convert the inner error if necessary,
200/// similarly to `try!`
201///
202/// This relies on the return type of the function to
203/// provide type inference for the `Result::Ok(T)` type.
204#[macro_export]
205macro_rules! try_rethrow {
206 ($res:expr) => (match $crate::_assert_trace_result($res) {
207 ::std::result::Result::Ok(val) => val,
208 ::std::result::Result::Err(err) => {
209 return ::std::result::Result::Err(err.convert())
210 }
211 })
212}
213
214/// The core macro that creates the `Result::Err(Trace<E>)` value,
215/// but does not return it immediately.
216///
217/// An optional second parameter can be given to indicate the type the `Result`
218/// should be if type inference cannot determine it automatically.
219#[macro_export]
220macro_rules! trace_error {
221 ($err:expr) => {
222 ::std::result::Result::Err($crate::Trace::new(
223 ::std::convert::From::from($err),
224 ::std::boxed::Box::new($crate::backtrace::SourceBacktrace::new(line!(), file!()))
225 ))
226 };
227
228 ($err:expr, $t:ty) => {
229 ::std::convert::From::<$t>::from(::std::result::Result::Err($crate::Trace::new(
230 ::std::convert::From::from($err),
231 ::std::boxed::Box::new($crate::backtrace::SourceBacktrace::new(line!(), file!()))
232 )))
233 };
234}