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}