#![doc(html_root_url = "https://docs.rs/color-eyre/0.3.2")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(
missing_debug_implementations,
missing_docs,
missing_doc_code_examples,
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
)]
use ansi_term::Color::*;
use backtrace::Backtrace;
pub use color_backtrace::BacktracePrinter;
use eyre::*;
pub use help::Help;
use help::HelpInfo;
use indenter::{indented, Format};
use once_cell::sync::OnceCell;
#[cfg(feature = "capture-spantrace")]
use std::error::Error;
use std::{
env,
fmt::{self, Write as _},
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
#[cfg(feature = "capture-spantrace")]
use tracing_error::{ExtractSpanTrace, SpanTrace, SpanTraceStatus};
mod help;
static CONFIG: OnceCell<BacktracePrinter> = OnceCell::new();
#[derive(Debug)]
pub struct Context {
backtrace: Option<Backtrace>,
#[cfg(feature = "capture-spantrace")]
span_trace: Option<SpanTrace>,
help: Vec<HelpInfo>,
}
#[derive(Debug)]
struct InstallError;
impl Context {
pub fn backtrace(&self) -> Option<&Backtrace> {
self.backtrace.as_ref()
}
#[cfg(feature = "capture-spantrace")]
#[cfg_attr(docsrs, doc(cfg(feature = "capture-spantrace")))]
pub fn span_trace(&self) -> Option<&SpanTrace> {
self.span_trace.as_ref()
}
}
impl EyreContext for Context {
#[allow(unused_variables)]
fn default(error: &(dyn std::error::Error + 'static)) -> Self {
let backtrace = if backtrace_enabled() {
Some(Backtrace::new())
} else {
None
};
#[cfg(feature = "capture-spantrace")]
let span_trace = if get_deepest_spantrace(error).is_none() {
Some(SpanTrace::capture())
} else {
None
};
Self {
backtrace,
#[cfg(feature = "capture-spantrace")]
span_trace,
help: Vec::new(),
}
}
fn debug(
&self,
error: &(dyn std::error::Error + 'static),
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
if f.alternate() {
return core::fmt::Debug::fmt(error, f);
}
#[cfg(feature = "capture-spantrace")]
let errors = Chain::new(error)
.filter(|e| e.span_trace().is_none())
.enumerate();
#[cfg(not(feature = "capture-spantrace"))]
let errors = Chain::new(error).enumerate();
let mut buf = String::new();
for (n, error) in errors {
writeln!(f)?;
buf.clear();
write!(&mut buf, "{}", error).unwrap();
write!(indented(f).ind(n), "{}", Red.paint(&buf))?;
}
#[cfg(feature = "capture-spantrace")]
{
let span_trace = self
.span_trace
.as_ref()
.or_else(|| get_deepest_spantrace(error))
.expect("SpanTrace capture failed");
match span_trace.status() {
SpanTraceStatus::CAPTURED => {
write!(f, "\n\n")?;
write!(indented(f).with_format(Format::Uniform { indentation: " " }), "{}", color_spantrace::colorize(span_trace))?
},
SpanTraceStatus::UNSUPPORTED => write!(f, "\n\nWarning: SpanTrace capture is Unsupported.\nEnsure that you've setup an error layer and the versions match")?,
_ => (),
}
}
if let Some(backtrace) = self.backtrace.as_ref() {
write!(f, "\n\n")?;
let bt_str = CONFIG
.get_or_init(default_printer)
.format_trace_to_string(&backtrace)
.unwrap();
write!(
indented(f).with_format(Format::Uniform { indentation: " " }),
"{}",
bt_str
)?;
} else if !self.help.is_empty() {
writeln!(f)?;
}
for help in &self.help {
write!(f, "\n{}", help)?;
}
Ok(())
}
}
impl fmt::Display for InstallError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("could not install the BacktracePrinter as another was already installed")
}
}
impl std::error::Error for InstallError {}
fn backtrace_enabled() -> bool {
static ENABLED: AtomicUsize = AtomicUsize::new(0);
match ENABLED.load(SeqCst) {
0 => {}
1 => return false,
_ => return true,
}
let enabled = match env::var("RUST_LIB_BACKTRACE") {
Ok(s) => s != "0",
Err(_) => match env::var("RUST_BACKTRACE") {
Ok(s) => s != "0",
Err(_) => false,
},
};
ENABLED.store(enabled as usize + 1, SeqCst);
enabled
}
#[cfg(feature = "capture-spantrace")]
fn get_deepest_spantrace<'a>(error: &'a (dyn Error + 'static)) -> Option<&'a SpanTrace> {
Chain::new(error)
.rev()
.flat_map(|error| error.span_trace())
.next()
}
pub fn install(printer: BacktracePrinter) -> Result<(), impl std::error::Error> {
let printer = add_eyre_filters(printer);
CONFIG.set(printer).map_err(|_| InstallError)
}
fn default_printer() -> BacktracePrinter {
add_eyre_filters(BacktracePrinter::new())
}
fn add_eyre_filters(printer: BacktracePrinter) -> BacktracePrinter {
printer.add_frame_filter(Box::new(|frames| {
let filters = &[
"<color_eyre::Context as eyre::EyreContext>::default",
"eyre::",
"color_eyre::",
];
frames.retain(|frame| {
!filters.iter().any(|f| {
let name = if let Some(name) = frame.name.as_ref() {
name.as_str()
} else {
return true;
};
name.starts_with(f)
})
});
}))
}
pub type Report = eyre::Report<Context>;
pub type Result<T, E = Report> = core::result::Result<T, E>;