charon_error/report/
error_report.rs1use crate::{
2 ErrorAttachment, ErrorFmt, ErrorFmtLoD, ErrorFmtSettings, ErrorFormatObj, ErrorFrame,
3 ErrorSensitivityLabel, IndentationStyle, ResultER, ResultExt, map,
4};
5use crate::{LinkDebugIde, StringError};
6use colored::*;
7use std::error::Error;
8use std::fmt::{Debug, Display};
9use tracing::instrument;
10
11#[derive(Debug)]
49#[must_use = "error reports must be used or returned"]
50pub struct ErrorReport {
51 pub frames: Vec<ErrorFrame>,
54 unique_id: String,
56}
57
58impl ErrorFmt for ErrorReport {
59 #[instrument]
60 fn create_format_obj(&self, settings: ErrorFmtSettings) -> ResultER<ErrorFormatObj> {
61 let object = ErrorFormatObj::Object(map! {
62 "last_error" => ErrorFormatObj::ColorString(self.get_last_error_title().bright_red().bold()),
63 "unique_id" => ErrorFormatObj::String(self.unique_id.clone()),
64 "frames" => ErrorFormatObj::Array(self.frames.iter().map(|f| f.create_format_obj(settings)).collect::<ResultER<Vec<_>>>()?),
65 });
66 Ok(match settings.level_of_detail {
67 ErrorFmtLoD::Compact => object.filter_object_fields(vec!["last_error", "frames"])?,
68 ErrorFmtLoD::Medium | ErrorFmtLoD::SubmitReport => {
69 object.filter_object_fields(vec!["last_error", "frames", "unique_id"])?
70 }
71 ErrorFmtLoD::Full | ErrorFmtLoD::Debug => object,
72 })
73 }
74}
75
76impl ErrorReport {
77 #[track_caller]
82 #[must_use = "the newly created report must be used"]
83 pub fn from_error<E: Error + Send + Sync + 'static>(error: E) -> Self {
84 Self {
85 frames: vec![ErrorFrame::new(error)],
86 unique_id: Self::create_new_unique_id(),
87 }
88 }
89
90 #[track_caller]
92 #[must_use = "the newly created report must be used"]
93 pub fn from_dyn_error(error: Box<dyn Error + Send + Sync + 'static>) -> Self {
94 Self {
95 frames: vec![ErrorFrame::from_dyn_error(error)],
96 unique_id: Self::create_new_unique_id(),
97 }
98 }
99
100 #[track_caller]
104 #[must_use = "the newly created report must be used"]
105 pub fn from_anyhow_error_ref(error: &anyhow::Error) -> Self {
106 let errors: Vec<_> = error.chain().collect();
108 let mut frames: Vec<ErrorFrame> = vec![];
109 for cause in errors {
110 frames.push(ErrorFrame::new(StringError::from_error(cause)));
111 }
112 if frames.is_empty() {
113 std::panic::panic_any(StringError::new(
114 "Calling `ErrorReport::from_anyhow_error_ref()` on anyhow::Error with no causes. \
115 This should not be possible, please report.",
116 ));
117 }
118 Self {
119 frames,
120 unique_id: "".to_owned(),
121 }
122 }
123
124 #[track_caller]
128 #[must_use = "the modified report with the new error must be used"]
129 pub fn push_error<R: Error + Send + Sync + 'static>(mut self, new_error: R) -> Self {
130 self.frames.push(ErrorFrame::new(new_error));
131 self
132 }
133
134 #[track_caller]
143 #[must_use = "the modified report with the attachment must be used"]
144 pub fn attach<S: Into<String>>(
145 mut self,
146 key_name: S,
147 attachment: ErrorSensitivityLabel<ErrorAttachment>,
148 ) -> Self {
149 if self.frames.is_empty() {
150 std::panic::panic_any(self.push_error(StringError::new(
151 "Calling `ErrorReport::attach()` on ErrorReport with no frames.",
152 )));
153 }
154 let last_frame = self.frames.last_mut().unwrap_error();
155 last_frame
156 .attachments
157 .insert(key_name.into(), Box::new(attachment));
158 self
159 }
160
161 #[allow(clippy::borrowed_box)]
168 #[must_use]
169 pub fn get_last_error(&self) -> &Box<dyn Error + Send + Sync + 'static> {
170 if self.frames.is_empty() {
171 std::panic::panic_any(StringError::new(
172 "Calling `ErrorReport::get_last_error()` on ErrorReport with no frames.",
173 )); }
175 &self.frames.last().unwrap_error().error
176 }
177
178 #[must_use]
180 pub fn get_last_error_title(&self) -> String {
181 self.get_last_error().to_string()
182 }
183
184 fn create_new_unique_id() -> String {
186 let amount_chars: usize = 20;
187 use rand::RngExt;
188 let hash: String = rand::rng()
189 .sample_iter(rand::distr::Alphanumeric)
190 .take(amount_chars)
191 .map(char::from)
192 .collect();
193 hash
194 }
195
196 #[must_use]
198 pub fn get_unique_id(&self) -> String {
199 self.unique_id.clone()
200 }
201
202 #[must_use = "the modified report with the attachment must be used"]
204 pub fn attach_public_string<S: Into<String>, A: Display>(
205 self,
206 key_name: S,
207 attachment: A,
208 ) -> Self {
209 self.attach(
210 key_name,
211 ErrorSensitivityLabel::Public(ErrorAttachment::String(attachment.to_string())),
212 )
213 }
214}
215
216impl Display for ErrorReport {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 write!(
219 f,
220 "{}",
221 self.stringify(ErrorFmtSettings {
222 level_of_detail: ErrorFmtLoD::Medium,
223 frame_limit: None,
224 enable_color: true,
225 link_format: LinkDebugIde::Vscode, indentation_style: IndentationStyle::FourSpaces,
227 ..Default::default()
228 })
229 .unwrap_error()
230 )
231 }
232}
233
234impl<E: Error + Send + Sync + 'static> From<E> for ErrorReport {
235 #[track_caller]
236 fn from(error: E) -> Self {
237 Self::from_error(error)
238 }
239}