1use std::{
4 backtrace::{Backtrace, BacktraceStatus},
5 fmt, process,
6};
7
8use anyhow::{Error, Result};
9use log::{Level, log_enabled};
10use serde::{Serialize, Serializer, ser::SerializeStruct};
11
12use crate::printer::Printer;
13
14pub struct ErrorReport(Error);
17
18impl ErrorReport {
19 pub fn eval<T>(printer: &mut impl Printer, result: Result<T, Error>) -> T {
22 match result {
23 Ok(res) => res,
24 Err(err) => {
25 printer.out(ErrorReport::from(err)).unwrap();
26 process::exit(1);
27 }
28 }
29 }
30
31 fn sources(&self) -> impl Iterator<Item = String> + '_ {
32 self.0.chain().skip(1).map(ToString::to_string)
33 }
34
35 fn suggestions(&self) -> Option<&str> {
36 if !log_enabled!(Level::Debug) || !log_enabled!(Level::Trace) {
37 Some("Run with --log-level to enable more verbose logs")
38 } else {
39 None
40 }
41 }
42
43 fn backtrace(&self) -> Option<&Backtrace> {
44 let backtrace = self.0.backtrace();
45
46 if let BacktraceStatus::Captured = backtrace.status() {
47 Some(backtrace)
48 } else {
49 None
50 }
51 }
52}
53
54impl From<Error> for ErrorReport {
55 fn from(err: Error) -> Self {
56 Self(err)
57 }
58}
59
60impl fmt::Display for ErrorReport {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "Error: {}", self.0)?;
63
64 let mut header_printed = false;
65 for err in self.sources() {
66 if !header_printed {
67 writeln!(f)?;
68 writeln!(f)?;
69 write!(f, "Caused by:")?;
70 header_printed = true;
71 }
72
73 writeln!(f)?;
74 write!(f, " - {}", err.trim())?;
75 }
76
77 if let Some(backtrace) = self.backtrace() {
78 writeln!(f)?;
79 writeln!(f)?;
80 writeln!(f, "Backtrace:")?;
81 write!(f, "{backtrace}")?;
82 }
83
84 if let Some(suggestion) = self.suggestions() {
85 writeln!(f)?;
86 writeln!(f)?;
87 writeln!(f, "Suggestions:")?;
88 write!(f, " - {suggestion}")?;
89 }
90
91 writeln!(f)
92 }
93}
94
95impl Serialize for ErrorReport {
96 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
97 let sources: Vec<_> = self.0.chain().skip(1).map(ToString::to_string).collect();
98 let backtrace = self.backtrace().map(ToString::to_string);
99
100 let mut s = serializer.serialize_struct("ErrorReport", 3)?;
101 s.serialize_field("error", &self.0.to_string())?;
102 s.serialize_field("sources", &sources)?;
103 s.serialize_field("backtrace", &backtrace)?;
104 s.end()
105 }
106}