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}