#![doc(html_root_url = "https://docs.rs/stable-eyre/0.2.2")]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub,
bad_style,
const_err,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
private_in_public,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)]
pub use eyre;
#[doc(hidden)]
pub use eyre::{Report, Result};
use ::backtrace::Backtrace;
use indenter::indented;
use std::{env, error::Error, iter};
pub trait BacktraceExt {
fn backtrace(&self) -> Option<&Backtrace>;
}
impl BacktraceExt for eyre::Report {
fn backtrace(&self) -> Option<&Backtrace> {
self.handler()
.downcast_ref::<crate::Handler>()
.and_then(|handler| handler.backtrace.as_ref())
}
}
#[derive(Debug)]
pub struct Handler {
backtrace: Option<Backtrace>,
}
impl eyre::EyreHandler for Handler {
fn debug(
&self,
error: &(dyn Error + 'static),
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
use core::fmt::Write as _;
if f.alternate() {
return core::fmt::Debug::fmt(error, f);
}
write!(f, "{}", error)?;
if let Some(cause) = error.source() {
write!(f, "\n\nCaused by:")?;
let multiple = cause.source().is_some();
let errors = iter::successors(Some(cause), |e| (*e).source());
for (n, error) in errors.enumerate() {
writeln!(f)?;
if multiple {
write!(indented(f).ind(n), "{}", error)?;
} else {
write!(indented(f), "{}", error)?;
}
}
}
if let Some(backtrace) = &self.backtrace {
write!(f, "\n\nStack backtrace:\n{:?}", backtrace)?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct HookBuilder {
capture_backtrace_by_default: bool,
}
impl HookBuilder {
#[allow(unused_variables)]
fn make_handler(&self, error: &(dyn Error + 'static)) -> Handler {
let backtrace = if self.capture_enabled() {
Some(Backtrace::new())
} else {
None
};
Handler { backtrace }
}
fn capture_enabled(&self) -> bool {
env::var("RUST_LIB_BACKTRACE")
.or_else(|_| env::var("RUST_BACKTRACE"))
.map(|val| val != "0")
.unwrap_or(self.capture_backtrace_by_default)
}
pub fn capture_backtrace_by_default(mut self, cond: bool) -> Self {
self.capture_backtrace_by_default = cond;
self
}
pub fn install(self) -> Result<()> {
crate::eyre::set_hook(Box::new(move |e| Box::new(self.make_handler(e))))?;
Ok(())
}
}
impl Default for HookBuilder {
fn default() -> Self {
Self {
capture_backtrace_by_default: false,
}
}
}
pub fn install() -> Result<()> {
HookBuilder::default().install()
}