1#![warn(missing_docs)]
2use eyre::Report;
17use tracing::Span;
18
19#[derive(Debug)]
20struct Handler {
21 span: Span,
22}
23
24impl eyre::EyreHandler for Handler {
25 fn debug(&self, error: &dyn std::error::Error, f: &mut std::fmt::Formatter) -> std::fmt::Result {
26 std::fmt::Debug::fmt(error, f)
27 }
28
29 #[cfg(feature = "tracing-error")]
30 fn display(&self, e: &dyn std::error::Error, f: &mut std::fmt::Formatter) -> std::fmt::Result {
31 use std::fmt::Write;
32
33 std::fmt::Display::fmt(e, f)?;
34
35 if f.alternate() {
36 let mut s = String::new();
37 tracing_error::SpanTrace::new(self.span.clone())
38 .with_spans(|meta, fields| {
39 write!(s, "\n• {}::{}", meta.target(), meta.name()).unwrap();
40 if !fields.is_empty() {
41 write!(s, "{{{}}}", strip_ansi(fields.to_owned())).unwrap();
42 }
43 true
44 });
45 f.write_str(&s)?;
46 }
47 Ok(())
48 }
49}
50
51fn strip_ansi(mut s: String) -> String {
52 let mut keep = true;
53 s.retain(|c| match c {
54 '\x1B' => { keep = false; false }
55 'm' if !keep => { keep = true; false }
56 _ => keep
57 });
58 s
59}
60
61mod seal {
62 pub trait Sealed {}
63}
64
65impl<T> seal::Sealed for eyre::Result<T> {}
66impl seal::Sealed for Report {}
67
68pub trait ReportSpan: seal::Sealed {
70 fn span(&self) -> &Span;
74}
75
76impl ReportSpan for Report {
77 fn span(&self) -> &Span {
78 &self.handler()
79 .downcast_ref::<Handler>()
80 .expect("eyre-span handler")
81 .span
82 }
83}
84
85pub trait Emit<T>: seal::Sealed {
87 fn emit(self) -> Option<T>;
89}
90
91impl<T> Emit<T> for Result<T, Report> {
92 fn emit(self) -> Option<T> {
93 emit(self)
94 }
95}
96
97pub fn emit<T>(e: Result<T, Report>) -> Option<T> {
101 match e {
102 Ok(v) => Some(v),
103 Err(e) => {
104 e.span().in_scope(|| tracing::error!("{e}"));
105 None
106 }
107 }
108}
109
110pub fn install() -> Result<(), eyre::InstallError> {
112 eyre::set_hook(Box::new(|_| Box::new(Handler { span: tracing::Span::current() })))
113}