charon_error/error_formating/
error_fmt.rs1use crate::{ERGlobalSettings, LinkDebugIde, ResultER, SourceLocation};
8use colored::ColoredString;
9use indexmap::IndexMap;
10use tracing::instrument;
11
12pub trait ErrorFmt {
18 fn create_format_obj(&self, settings: ErrorFmtSettings) -> ResultER<ErrorFormatObj>;
20
21 fn stringify(&self, settings: ErrorFmtSettings) -> ResultER<String> {
23 let format_obj = self.create_format_obj(settings)?;
24
25 format_obj.stringify(settings)
26 }
27
28 fn stringify_obj(format_obj: ErrorFormatObj, settings: ErrorFmtSettings) -> ResultER<String> {
30 format_obj.stringify(settings)
31 }
32}
33
34#[derive(Debug, Clone)]
40pub enum ErrorFormatObj {
41 Null,
43 Bool(bool),
45 Number(i64),
47 String(String),
49 Url(url::Url),
51 DateTime(chrono::DateTime<chrono::Utc>),
53 SourceLocation(SourceLocation),
55 FormatString(Vec<ErrorFormatObj>),
57 ColorString(ColoredString),
59 Array(Vec<ErrorFormatObj>),
61 Object(IndexMap<String, ErrorFormatObj>),
63}
64
65#[derive(thiserror::Error, Debug, Clone)]
67pub enum FilterObjectError {
68 #[error("The key `{0}` provided does not exist in the Object. It might be mistyped.")]
70 KeyDoesNotExist(String),
71}
72
73impl ErrorFormatObj {
74 #[instrument]
77 pub fn filter_object_fields(&self, list: Vec<&str>) -> Result<Self, FilterObjectError> {
78 let mut new_self = self.clone();
79 if let ErrorFormatObj::Object(obj) = &mut new_self {
80 for item in &list {
82 if !obj.contains_key(*item) {
83 return Err(FilterObjectError::KeyDoesNotExist(item.to_string()));
84 }
85 }
86 obj.retain(|key, _value| list.contains(&key.as_str()));
87 }
88 Ok(new_self)
89 }
90
91 pub fn stringify(&self, settings: ErrorFmtSettings) -> ResultER<String> {
93 Ok(match self {
94 Self::Null => "null".to_owned(),
95 Self::Bool(b) => {
96 if *b {
97 "true".to_owned()
98 } else {
99 "false".to_owned()
100 }
101 }
102 Self::Number(n) => format!("{n}"),
103 Self::String(s) => {
104 if settings.inside_format_string {
105 s.to_string()
106 } else {
107 format!("'{s}'")
108 }
109 }
110 Self::Url(url) => format!("'{url}'"),
111 Self::DateTime(date_time) => date_time.to_rfc3339(),
112 Self::SourceLocation(sl) => sl.display_location(Some(settings.link_format)),
113 Self::FormatString(fs) => fs
114 .iter()
115 .map(|s| s.stringify(settings.inside_fs()))
116 .collect::<ResultER<Vec<_>>>()?
117 .join(""),
118 Self::ColorString(cs) => {
119 let text = if settings.enable_color {
120 cs.to_string()
121 } else {
122 cs.input.to_string()
123 };
124 if settings.inside_format_string {
125 text.to_string()
126 } else {
127 format!("'{text}'")
128 }
129 }
130 Self::Array(list) => match settings.level_of_detail {
131 ErrorFmtLoD::Compact => format!(
132 "[{}]",
133 list.iter()
134 .map(|s| s.stringify(settings.reset_inside_fs()))
135 .collect::<ResultER<Vec<_>>>()?
136 .join(",")
137 ),
138 ErrorFmtLoD::SubmitReport
139 | ErrorFmtLoD::Medium
140 | ErrorFmtLoD::Full
141 | ErrorFmtLoD::Debug => {
142 if list.is_empty() {
143 "[]".to_owned()
144 } else {
145 let indented_s = settings.add_indent().reset_inside_fs();
146 format!(
147 "[\n{items}{tab}]",
148 tab = settings.indent(),
149 items = list
150 .iter()
151 .map(|s| Ok(format!(
152 "{}{},\n",
153 indented_s.indent(),
154 s.stringify(indented_s)?
155 )))
156 .collect::<ResultER<Vec<_>>>()?
157 .join("")
158 )
159 }
160 }
161 },
162 Self::Object(obj) => match settings.level_of_detail {
163 ErrorFmtLoD::Compact => format!(
164 "{{{}}}",
165 obj.iter()
166 .map(|(k, v)| Ok(format!(
167 "{} -> {}",
168 k,
169 v.stringify(settings.reset_inside_fs())?
170 )))
171 .collect::<ResultER<Vec<_>>>()?
172 .join(","),
173 ),
174 ErrorFmtLoD::SubmitReport
175 | ErrorFmtLoD::Medium
176 | ErrorFmtLoD::Full
177 | ErrorFmtLoD::Debug => {
178 if obj.is_empty() {
179 "{}".to_owned()
180 } else {
181 let indented_s = settings.add_indent().reset_inside_fs();
182 format!(
183 "{{\n{items}{tab}}}",
184 tab = settings.indent(),
185 items = obj
186 .iter()
187 .map(|(k, v)| Ok(format!(
188 "{}{}: {},\n",
189 indented_s.indent(),
190 k,
191 v.stringify(indented_s)?
192 )))
193 .collect::<ResultER<Vec<_>>>()?
194 .join(""),
195 )
196 }
197 }
198 },
199 })
200 }
201}
202
203#[derive(Debug, Clone, Copy)]
221pub struct ErrorFmtSettings {
222 pub level_of_detail: ErrorFmtLoD,
224 pub frame_limit: Option<usize>,
226 pub enable_color: bool,
228 pub link_format: LinkDebugIde,
230 pub indentation_style: IndentationStyle,
232 pub indentation: usize,
234 pub inside_format_string: bool,
236 pub skip_first_indentations: usize,
238}
239
240impl ErrorFmtSettings {
241 fn add_indent(self) -> Self {
242 let mut copy_self = self;
243 if copy_self.skip_first_indentations > 0 {
244 copy_self.skip_first_indentations -= 1;
245 } else {
246 copy_self.indentation += 1;
247 }
248 copy_self
249 }
250
251 fn indent(self) -> String {
252 match self.indentation_style {
253 IndentationStyle::Tab => (0..self.indentation).map(|_| "\t").collect::<String>(),
254 IndentationStyle::TwoSpaces => (0..self.indentation).map(|_| " ").collect::<String>(),
255 IndentationStyle::FourSpaces => {
256 (0..self.indentation).map(|_| " ").collect::<String>()
257 }
258 }
259 }
260
261 fn inside_fs(self) -> Self {
262 let mut copy_self = self;
263 copy_self.inside_format_string = true;
264 copy_self
265 }
266
267 fn reset_inside_fs(self) -> Self {
268 let mut copy_self = self;
269 copy_self.inside_format_string = false;
270 copy_self
271 }
272}
273
274impl Default for ErrorFmtSettings {
275 fn default() -> Self {
276 Self {
277 level_of_detail: ErrorFmtLoD::Medium,
278 frame_limit: None,
279 enable_color: true,
280 link_format: LinkDebugIde::File,
281 indentation: 0,
282 indentation_style: IndentationStyle::TwoSpaces,
283 inside_format_string: false,
284 skip_first_indentations: 0,
285 }
286 }
287}
288
289impl From<ERGlobalSettings> for ErrorFmtSettings {
290 fn from(settings: ERGlobalSettings) -> Self {
291 ErrorFmtSettings {
292 link_format: settings.link_format,
293 ..Default::default()
294 }
295 }
296}
297
298impl From<&ERGlobalSettings> for ErrorFmtSettings {
299 fn from(settings: &ERGlobalSettings) -> Self {
300 ErrorFmtSettings {
301 link_format: settings.link_format,
302 ..Default::default()
303 }
304 }
305}
306
307#[derive(Debug, Clone, Copy)]
309pub enum IndentationStyle {
310 Tab,
312 TwoSpaces,
314 FourSpaces,
316}
317
318#[derive(Debug, Clone, Copy, Default)]
320pub enum ErrorFmtLoD {
321 Compact,
323 SubmitReport,
325 #[default]
327 Medium,
328 Full,
330 Debug,
332}
333
334#[macro_export]
338macro_rules! map {
339 ($($key:expr => $val:expr),* $(,)*) => ({
340 #[allow(unused_mut)]
341 let mut map = indexmap::IndexMap::new();
342 $( map.insert($key.to_string(), $val); )*
343 map
344 });
345}
346
347#[macro_export]
352macro_rules! format_string {
353 ($($val:expr),* $(,)*) => ({
354 #[allow(unused_mut)]
355 let mut list = Vec::new();
356 $( list.push(ErrorFormatObj::String($val.to_string())); )*
357 ErrorFormatObj::FormatString(list)
358 });
359}