backtrace_error/
lib.rs

1// Copyright 2021-2024 Graydon Hoare <graydon@pobox.com>
2// Licensed under ASL2 or MIT
3
4//!
5//! This is a tiny crate that provides a tiny error-wrapper struct
6//! `BacktraceError` with only two features:
7//!
8//!   - Captures a backtrace on `From`-conversion from its wrapped type (if
9//!     `RUST_BACKTRACE` is on etc.)
10//!   - Pretty-prints that backtrace in its `Display` implementation.
11//!
12//! It also includes an extension trait `ResultExt` that you can `use` to give
13//! you `.unwrap_or_backtrace` and `.expect_or_backtrace` methods on any
14//! `Result<T, BacktraceError<E>>`. These methods do do the same as `unwrap`
15//! or `expect` on `Result` except they pretty-print the backtrace on `Err`,
16//! before panicking.
17//!
18//! Finally, it provides a _dynamic_ variant in case you want to type-erase the
19//! error type, `DynBacktraceError`. This works the same as `BacktraceError<E>`
20//! but wraps a `Box<dyn Error + Send + Sync + 'static>` instead of requiring a
21//! specific error type `E`, so is therefore potentially more expensive but also
22//! more flexible and usable as an "any error" catchall type since it has an
23//! `impl<E:Error + Send + Sync + 'static> From<E>` conversion.
24//!
25//! # Example
26//!
27//! Usage is straightforward: put some existing error type in it. No macros!
28//!
29//! ```should_panic
30//! use backtrace_error::{BacktraceError,ResultExt};
31//! use std::{io,fs};
32//!
33//! type IOError = BacktraceError<io::Error>;
34//!
35//! fn open_file() -> Result<fs::File, IOError> {
36//!    Ok(fs::File::open("/does-not-exist.nope")?)
37//! }
38//!
39//! fn do_stuff() -> Result<fs::File, IOError>
40//! {
41//!     open_file()
42//! }
43//!
44//! fn main()
45//! {
46//!     // This will panic but first print a backtrace of
47//!     // the error site, then a backtrace of the panic site.
48//!     let file = do_stuff().unwrap_or_backtrace();
49//! }
50//! ```
51//!
52//! or dynamically:
53//!
54//! ```should_panic
55//! use backtrace_error::{DynBacktraceError,ResultExt};
56//! use std::{io,fs};
57//!
58//! type AppErr = DynBacktraceError;
59//!
60//! fn open_file() -> Result<fs::File, AppErr> {
61//!    Ok(fs::File::open("/does-not-exist.nope")?)
62//! }
63//!
64//! fn parse_number() -> Result<i32, AppErr> {
65//!    Ok(i32::from_str_radix("not-a-number", 10)?)
66//! }
67//!
68//! fn do_stuff() -> Result<(), AppErr>
69//! {
70//!     open_file()?;
71//!     parse_number()?;
72//!     Ok(())
73//! }
74//!
75//! fn main()
76//! {
77//!     // This will panic but first print a backtrace of
78//!     // the error site, then a backtrace of the panic site.
79//!     do_stuff().unwrap_or_backtrace();
80//! }
81//! ```
82//!
83//! I am very sorry for having written Yet Another Rust Error Crate but
84//! strangely everything I looked at either doesn't capture backtraces, doesn't
85//! print them, only debug-prints them on a failed unwrap (which is illegible),
86//! provides a pile of features I don't want through expensive macros, or some
87//! combination thereof. I don't need any of that, I just want to capture
88//! backtraces for errors when they occur, and print them out sometime later.
89//!
90//! I figured maybe someone out there has the same need, so am publishing it.
91
92use std::{
93    backtrace::Backtrace,
94    error::Error,
95    fmt::{Debug, Display},
96    ops::{Deref, DerefMut},
97};
98
99pub struct BacktraceError<E: Error> {
100    pub inner: E,
101    pub backtrace: Box<Backtrace>,
102}
103
104impl<E: Error> Display for BacktraceError<E> {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        writeln!(f, "Initial error: {:}", self.inner)?;
107        writeln!(f, "Error context:")?;
108        writeln!(f, "{:}", self.backtrace)
109    }
110}
111
112impl<E: Error> Debug for BacktraceError<E> {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        <Self as Display>::fmt(self, f)
115    }
116}
117
118impl<E: Error + 'static> Error for BacktraceError<E> {
119    fn source(&self) -> Option<&(dyn Error + 'static)> {
120        Some(&self.inner)
121    }
122}
123
124// Someday we'll also support the "Provider" API, but not today
125// since it is not stable and I don't want to bother tracking
126// its stability.
127/*
128impl<E:Error + 'static> std::any::Provider for BacktraceError<E> {
129    fn provide<'a>(&'a self, demand: &mut std::any::Demand<'a>) {
130        demand.provide_ref::<Backtrace>(self.backtrace)
131        .provide_value::<Backtrace>(|| self.backtrace)
132    }
133}
134*/
135
136impl<E: Error + 'static> From<E> for BacktraceError<E> {
137    fn from(inner: E) -> Self {
138        let backtrace = Box::new(Backtrace::capture());
139        Self { inner, backtrace }
140    }
141}
142
143pub trait ResultExt: Sized {
144    type T;
145    fn unwrap_or_backtrace(self) -> Self::T {
146        self.expect_or_backtrace("ResultExt::unwrap_or_backtrace found Err")
147    }
148    fn expect_or_backtrace(self, msg: &str) -> Self::T;
149}
150
151impl<T, E: Error> ResultExt for Result<T, BacktraceError<E>> {
152    type T = T;
153    fn expect_or_backtrace(self, msg: &str) -> T {
154        match self {
155            Ok(ok) => ok,
156            Err(bterr) => {
157                eprintln!("{}", msg);
158                eprintln!("");
159                eprintln!("{:}", bterr);
160                panic!("{}", msg);
161            }
162        }
163    }
164}
165
166pub struct DynBacktraceError {
167    inner: Box<dyn Error + Send + Sync + 'static>,
168    backtrace: Box<Backtrace>,
169}
170
171impl<E: Error + Send + Sync + 'static> From<E> for DynBacktraceError {
172    fn from(inner: E) -> Self {
173        let backtrace = Box::new(Backtrace::capture());
174        Self {
175            inner: Box::new(inner),
176            backtrace,
177        }
178    }
179}
180
181impl Deref for DynBacktraceError {
182    type Target = dyn Error + Send + Sync + 'static;
183    fn deref(&self) -> &Self::Target {
184        &*self.inner
185    }
186}
187
188impl DerefMut for DynBacktraceError {
189    fn deref_mut(&mut self) -> &mut Self::Target {
190        &mut *self.inner
191    }
192}
193
194impl Display for DynBacktraceError {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        writeln!(f, "Initial error: {:}", self.inner)?;
197        writeln!(f, "Error context:")?;
198        writeln!(f, "{:}", self.backtrace)
199    }
200}
201
202impl Debug for DynBacktraceError {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        <Self as Display>::fmt(self, f)
205    }
206}
207
208impl ResultExt for Result<(), DynBacktraceError> {
209    type T = ();
210    fn expect_or_backtrace(self, msg: &str) -> () {
211        match self {
212            Ok(()) => (),
213            Err(bterr) => {
214                eprintln!("{}", msg);
215                eprintln!("");
216                eprintln!("{:}", bterr);
217                panic!("{}", msg);
218            }
219        }
220    }
221}