#![doc(html_root_url = "https://docs.rs/color-eyre/0.2.3")]
#![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;
use eyre::*;
pub use help::Help;
use help::HelpInfo;
use indenter::{indented, Format};
#[cfg(feature = "capture-spantrace")]
use std::error::Error;
use std::fmt::Write as _;
#[cfg(feature = "capture-spantrace")]
use tracing_error::{ExtractSpanTrace, SpanTrace, SpanTraceStatus};
mod help;
#[derive(Debug)]
pub struct Context {
backtrace: Option<Backtrace>,
#[cfg(feature = "capture-spantrace")]
span_trace: Option<SpanTrace>,
help: Vec<HelpInfo>,
}
impl EyreContext for Context {
#[allow(unused_variables)]
fn default(error: &(dyn std::error::Error + 'static)) -> Self {
let backtrace = if std::env::var("RUST_LIB_BACKTRACE")
.or_else(|_| std::env::var("RUST_BACKTRACE"))
.is_ok()
{
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 = color_backtrace::BacktracePrinter::new()
.add_frame_filter(Box::new(|frames| {
let filters = &[
"<color_eyre::Context as eyre::EyreContext>::default",
"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)
})
});
}))
.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(())
}
}
#[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 type Report = eyre::Report<Context>;
pub type Result<T, E = Report> = core::result::Result<T, E>;