charon_error/error_formating/
error_fmt.rs1use crate::{LinkDebugIde, ResultER, SourceLocation};
2use colored::ColoredString;
3use indexmap::IndexMap;
4use tracing::instrument;
5
6pub trait ErrorFmt {
12 fn create_format_obj(&self, settings: ErrorFmtSettings) -> ResultER<ErrorFormatObj>;
14
15 fn stringify(&self, settings: ErrorFmtSettings) -> ResultER<String> {
17 let format_obj = self.create_format_obj(settings)?;
18
19 format_obj.stringify(settings)
20 }
21
22 fn stringify_obj(format_obj: ErrorFormatObj, settings: ErrorFmtSettings) -> ResultER<String> {
24 format_obj.stringify(settings)
25 }
26}
27
28#[derive(Debug, Clone)]
34pub enum ErrorFormatObj {
35 Null,
37 Bool(bool),
39 Number(i64),
41 String(String),
43 Url(url::Url),
45 DateTime(chrono::DateTime<chrono::Utc>),
47 SourceLocation(SourceLocation),
49 FormatString(Vec<ErrorFormatObj>),
51 ColorString(ColoredString),
53 Array(Vec<ErrorFormatObj>),
55 Object(IndexMap<String, ErrorFormatObj>),
57}
58
59#[derive(thiserror::Error, Debug, Clone)]
61pub enum FilterObjectError {
62 #[error("The key `{0}` provided does not exist in the Object. It might be mistyped.")]
63 KeyDoesNotExist(String),
64}
65
66impl ErrorFormatObj {
67 #[instrument]
70 pub fn filter_object_fields(&self, list: Vec<&str>) -> Result<Self, FilterObjectError> {
71 let mut new_self = self.clone();
72 if let ErrorFormatObj::Object(obj) = &mut new_self {
73 for item in &list {
75 if !obj.contains_key(*item) {
76 return Err(FilterObjectError::KeyDoesNotExist(item.to_string()));
77 }
78 }
79 obj.retain(|key, _value| list.contains(&key.as_str()));
80 }
81 Ok(new_self)
82 }
83
84 pub fn stringify(&self, settings: ErrorFmtSettings) -> ResultER<String> {
86 Ok(match self {
87 Self::Null => "null".to_owned(),
88 Self::Bool(b) => {
89 if *b {
90 "true".to_owned()
91 } else {
92 "false".to_owned()
93 }
94 }
95 Self::Number(n) => format!("{n}"),
96 Self::String(s) => {
97 if settings.inside_format_string {
98 s.to_string()
99 } else {
100 format!("'{s}'")
101 }
102 }
103 Self::Url(url) => format!("'{url}'"),
104 Self::DateTime(date_time) => date_time.to_rfc3339(),
105 Self::SourceLocation(sl) => sl.display_location(Some(settings.link_format)),
106 Self::FormatString(fs) => fs
107 .iter()
108 .map(|s| s.stringify(settings.inside_fs()))
109 .collect::<ResultER<Vec<_>>>()?
110 .join(""),
111 Self::ColorString(cs) => {
112 let text = if settings.enable_color {
113 cs.to_string()
114 } else {
115 cs.input.to_string()
116 };
117 if settings.inside_format_string {
118 text.to_string()
119 } else {
120 format!("'{text}'")
121 }
122 }
123 Self::Array(list) => match settings.level_of_detail {
124 ErrorFmtLoD::Compact => format!(
125 "[{}]",
126 list.iter()
127 .map(|s| s.stringify(settings.reset_inside_fs()))
128 .collect::<ResultER<Vec<_>>>()?
129 .join(",")
130 ),
131 ErrorFmtLoD::SubmitReport
132 | ErrorFmtLoD::Medium
133 | ErrorFmtLoD::Full
134 | ErrorFmtLoD::Debug => {
135 if list.is_empty() {
136 "[]".to_owned()
137 } else {
138 let indented_s = settings.add_indent().reset_inside_fs();
139 format!(
140 "[\n{items}{tab}]",
141 tab = settings.indent(),
142 items = list
143 .iter()
144 .map(|s| Ok(format!(
145 "{}{},\n",
146 indented_s.indent(),
147 s.stringify(indented_s)?
148 )))
149 .collect::<ResultER<Vec<_>>>()?
150 .join("")
151 )
152 }
153 }
154 },
155 Self::Object(obj) => match settings.level_of_detail {
156 ErrorFmtLoD::Compact => format!(
157 "{{{}}}",
158 obj.iter()
159 .map(|(k, v)| Ok(format!(
160 "{} -> {}",
161 k,
162 v.stringify(settings.reset_inside_fs())?
163 )))
164 .collect::<ResultER<Vec<_>>>()?
165 .join(","),
166 ),
167 ErrorFmtLoD::SubmitReport
168 | ErrorFmtLoD::Medium
169 | ErrorFmtLoD::Full
170 | ErrorFmtLoD::Debug => {
171 if obj.is_empty() {
172 "{}".to_owned()
173 } else {
174 let indented_s = settings.add_indent().reset_inside_fs();
175 format!(
176 "{{\n{items}{tab}}}",
177 tab = settings.indent(),
178 items = obj
179 .iter()
180 .map(|(k, v)| Ok(format!(
181 "{}{}: {},\n",
182 indented_s.indent(),
183 k,
184 v.stringify(indented_s)?
185 )))
186 .collect::<ResultER<Vec<_>>>()?
187 .join(""),
188 )
189 }
190 }
191 },
192 })
193 }
194}
195
196#[derive(Debug, Clone, Copy)]
200#[non_exhaustive]
201pub struct ErrorFmtSettings {
202 pub level_of_detail: ErrorFmtLoD,
204 pub frame_limit: Option<usize>,
206 pub enable_color: bool,
208 pub link_format: LinkDebugIde,
210 pub indentation_style: IndentationStyle,
212 pub indentation: usize,
214 pub inside_format_string: bool,
216 pub skip_first_indentations: usize,
218}
219
220impl ErrorFmtSettings {
221 fn add_indent(self) -> Self {
222 let mut copy_self = self;
223 if copy_self.skip_first_indentations > 0 {
224 copy_self.skip_first_indentations -= 1;
225 } else {
226 copy_self.indentation += 1;
227 }
228 copy_self
229 }
230
231 fn indent(self) -> String {
232 match self.indentation_style {
233 IndentationStyle::Tab => (0..self.indentation).map(|_| "\t").collect::<String>(),
234 IndentationStyle::TwoSpaces => (0..self.indentation).map(|_| " ").collect::<String>(),
235 IndentationStyle::FourSpaces => {
236 (0..self.indentation).map(|_| " ").collect::<String>()
237 }
238 }
239 }
240
241 fn inside_fs(self) -> Self {
242 let mut copy_self = self;
243 copy_self.inside_format_string = true;
244 copy_self
245 }
246
247 fn reset_inside_fs(self) -> Self {
248 let mut copy_self = self;
249 copy_self.inside_format_string = false;
250 copy_self
251 }
252}
253
254impl Default for ErrorFmtSettings {
255 fn default() -> Self {
256 Self {
257 level_of_detail: ErrorFmtLoD::Medium,
258 frame_limit: None,
259 enable_color: true,
260 link_format: LinkDebugIde::Vscode,
261 indentation: 0,
262 indentation_style: IndentationStyle::TwoSpaces,
263 inside_format_string: false,
264 skip_first_indentations: 0,
265 }
266 }
267}
268
269#[derive(Debug, Clone, Copy)]
271pub enum IndentationStyle {
272 Tab,
274 TwoSpaces,
276 FourSpaces,
278}
279
280#[derive(Debug, Clone, Copy, Default)]
282pub enum ErrorFmtLoD {
283 Compact,
285 SubmitReport,
287 #[default]
289 Medium,
290 Full,
292 Debug,
294}
295
296#[macro_export]
300macro_rules! map {
301 ($($key:expr => $val:expr),* $(,)*) => ({
302 #[allow(unused_mut)]
303 let mut map = indexmap::IndexMap::new();
304 $( map.insert($key.to_string(), $val); )*
305 map
306 });
307}
308
309#[macro_export]
314macro_rules! format_string {
315 ($($val:expr),* $(,)*) => ({
316 #[allow(unused_mut)]
317 let mut list = Vec::new();
318 $( list.push(ErrorFormatObj::String($val.to_string())); )*
319 ErrorFormatObj::FormatString(list)
320 });
321}