1use backtrace::Backtrace;
2use chrono::{DateTime, Utc};
3use colored::Colorize;
4use regex::Regex;
5use std::fmt::Display;
6use std::{collections::HashMap, error::Error};
7use tracing::{Metadata, Span, instrument};
8use tracing_error::SpanTrace;
9
10use crate::{
11 ErrorFmt, ErrorFmtLoD, ErrorFmtSettings, ErrorFormatObj, ResultER, ResultExt, SourceLocation,
12 StringError, map,
13};
14
15#[derive(Debug)]
27pub struct ErrorFrame {
28 pub message: ErrorSensitivityLabel<String>,
30 pub error: Box<dyn Error + Send + Sync + 'static>,
32 pub source: SourceLocation,
34 pub date_time: DateTime<Utc>,
36 pub suggestions: Vec<ErrorSensitivityLabel<String>>,
38 pub context: ErrorSensitivityLabel<String>,
40 pub attachments: HashMap<String, Box<ErrorSensitivityLabel<ErrorAttachment>>>,
42 pub backtrace: Backtrace,
45 pub span: Span,
49 pub span_trace: SpanTrace,
51}
52
53impl ErrorFmt for ErrorFrame {
54 #[instrument]
55 fn create_format_obj(&self, settings: ErrorFmtSettings) -> ResultER<ErrorFormatObj> {
56 let object = ErrorFormatObj::Object(map! {
57 "message" => ErrorFormatObj::ColorString(self.message.to_string().bright_red().bold()),
58 "source_location" => ErrorFormatObj::SourceLocation(self.source.clone()),
59 "span_trace" => self.span_trace.create_format_obj(settings)?,
60 "attachments" => self.attachments.create_format_obj(settings)?,
61 "date_time" => self.date_time.create_format_obj(settings)?,
62 });
63 Ok(match settings.level_of_detail {
64 ErrorFmtLoD::Compact => object.filter_object_fields(vec![
65 "message",
66 "source_location",
67 "span_trace",
68 "attachments",
69 ])?,
70 ErrorFmtLoD::SubmitReport => object.filter_object_fields(vec![
71 "message",
72 "source_location",
73 "span_trace",
74 "attachments",
75 ])?,
76 ErrorFmtLoD::Medium => object,
77 ErrorFmtLoD::Full | ErrorFmtLoD::Debug => object,
78 })
79 }
80}
81
82impl ErrorFrame {
83 #[must_use]
87 #[track_caller]
88 pub fn new<E: Error + Send + Sync + 'static>(error: E) -> Self {
89 Self {
90 message: ErrorSensitivityLabel::Public(error.to_string()),
91 error: Box::new(error),
92 source: SourceLocation::from_caller_location(),
93 date_time: Utc::now(),
94 suggestions: Vec::new(),
95 context: ErrorSensitivityLabel::Public(String::new()),
96 attachments: HashMap::new(),
97 backtrace: Backtrace::new_unresolved(),
98 span: Span::current(),
99 span_trace: SpanTrace::capture(),
100 }
101 }
102
103 #[must_use]
105 #[track_caller]
106 pub fn from_dyn_error(error: Box<dyn Error + Send + Sync + 'static>) -> Self {
107 Self {
108 message: ErrorSensitivityLabel::Public(error.to_string()),
109 error,
110 source: SourceLocation::from_caller_location(),
111 date_time: Utc::now(),
112 suggestions: Vec::new(),
113 context: ErrorSensitivityLabel::Public(String::new()),
114 attachments: HashMap::new(),
115 backtrace: Backtrace::new_unresolved(),
116 span: Span::current(),
117 span_trace: SpanTrace::capture(),
118 }
119 }
120
121 #[must_use]
125 #[track_caller]
126 pub fn from_message<S: Into<String>>(msg: S) -> Self {
127 let error = StringError::new(msg);
128 Self {
129 message: ErrorSensitivityLabel::Internal(error.to_string()),
130 error: Box::new(error),
131 source: SourceLocation::from_caller_location(),
132 date_time: Utc::now(),
133 suggestions: Vec::new(),
134 context: ErrorSensitivityLabel::Internal(String::new()),
135 attachments: HashMap::new(),
136 backtrace: Backtrace::new_unresolved(),
137 span: Span::current(),
138 span_trace: SpanTrace::capture(),
139 }
140 }
141
142 #[must_use]
144 pub fn get_error_title(&self) -> String {
145 self.message.to_string()
146 }
147
148 #[must_use]
150 pub fn get_span_list(&self) -> Vec<&'static Metadata<'static>> {
151 let mut span_list = Vec::new();
152 self.span_trace.with_spans(|meta_data, _field_values| {
153 span_list.push(meta_data);
154 true
155 });
156 span_list
157 }
158
159 #[must_use]
164 pub fn create_backtrace_string(&self) -> String {
165 let mut bt = self.backtrace.clone();
166 bt.resolve();
167 let frames = bt.frames();
168 let mut backtrace_string = String::new();
169 let max_line = 11;
170 let current_folder: String = std::env::current_dir().unwrap_error().display().to_string();
171 let cargo_registry_folder =
172 Regex::new(r"\/.cargo\/registry\/[^/]*\/[^/]*\/").unwrap_error();
173 let rustlib_folder =
174 Regex::new(r"\/.rustup\/toolchains\/[^/]*\/lib\/rustlib\/src\/").unwrap_error();
175 for (frame, line) in frames.iter().zip(0..max_line) {
176 let symbol = frame.symbols().first().unwrap_error();
177 let mut name = String::new();
178 if let Some(name_value) = &symbol.name() {
179 let full_name = name_value.to_string();
180 let parts: Vec<&str> = full_name.split("::").collect();
181 if parts.len() >= 3 {
182 name = format!(
183 "{}::{}",
184 parts.get(parts.len() - 3).unwrap_or(&""),
185 parts.get(parts.len() - 2).unwrap_or(&""),
186 );
187 }
188 }
189 let mut filepath = String::new();
192 if let Some(filename) = &symbol.filename() {
193 let line_nr = &symbol.lineno().unwrap_or_default();
194 let path = filename.to_str().unwrap_error();
195 let (_, path) = path.split_at(
197 path.rfind(¤t_folder)
198 .map(|pos| pos + current_folder.len() + 1) .unwrap_or(0),
200 );
201 let (_, path) = path.split_at(
203 cargo_registry_folder
204 .find(path)
205 .map(|m| m.end())
206 .unwrap_or(0),
207 );
208 let (_, path) =
210 path.split_at(rustlib_folder.find(path).map(|m| m.end()).unwrap_or(0));
211 if !path.starts_with("/rustc/") {
212 filepath = format!(" => {path}:{line_nr}");
213 }
214 }
215 if line != 0 {
216 backtrace_string = format!("{backtrace_string}{line}: {name}{filepath}\n");
217 }
218 }
219 backtrace_string
220 }
221}
222
223#[derive(Debug, Clone)]
228pub enum ErrorAttachment {
229 RawData(Vec<u8>),
231 String(String),
233}
234
235impl Display for ErrorAttachment {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 match self {
238 Self::RawData(_data) => write!(f, "<raw_data>"),
239 Self::String(data) => write!(f, "{data}"),
240 }
241 }
242}
243
244#[derive(Debug, Clone)]
262pub enum ErrorSensitivityLabel<T> {
263 Public(T),
265 Private(T),
268 PrivateConfidential(T),
272
273 Internal(T),
276 Confidential(T),
279 HighlyConfidential(T),
286}
287
288impl<T: Display> Display for ErrorSensitivityLabel<T> {
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 let result = match self {
291 ErrorSensitivityLabel::Public(t) => t.to_string(),
292 ErrorSensitivityLabel::Private(_) => "Data is private".to_owned(),
293 ErrorSensitivityLabel::PrivateConfidential(_) => "Data is private".to_owned(),
294 ErrorSensitivityLabel::Internal(_) => "Data is only for internal use".to_owned(),
295 ErrorSensitivityLabel::Confidential(_) => "Data is only for internal use".to_owned(),
296 ErrorSensitivityLabel::HighlyConfidential(_) => {
297 "Data is only for internal use".to_owned()
298 }
299 };
300 write!(f, "{result}")
301 }
302}