1use log::{error, warn};
2use regex::Regex;
3use serde::{Deserialize, Serialize};
4use std::{
5 error::Error,
6 fmt::{Display, Formatter, Result as FmtResult},
7 str::FromStr,
8};
9
10#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub enum ErrorType {
15 UndefinedError,
20
21 CreationError,
25
26 UpdatingError,
30
31 FetchingError,
35
36 DeletionError,
40
41 UseCaseError,
45
46 ExecutionError,
51
52 InvalidRepositoryError,
56
57 InvalidArgumentError,
61}
62
63impl ErrorType {
64 fn default() -> Self {
65 Self::UndefinedError
66 }
67}
68
69impl Display for ErrorType {
70 fn fmt(&self, f: &mut Formatter) -> FmtResult {
71 match self {
72 ErrorType::UndefinedError => write!(f, "undefined-error"),
73 ErrorType::CreationError => write!(f, "creation-error"),
74 ErrorType::UpdatingError => write!(f, "updating-error"),
75 ErrorType::FetchingError => write!(f, "fetching-error"),
76 ErrorType::DeletionError => write!(f, "deletion-error"),
77 ErrorType::UseCaseError => write!(f, "use-case-error"),
78 ErrorType::ExecutionError => write!(f, "execution-error"),
79 ErrorType::InvalidRepositoryError => {
80 write!(f, "invalid-repository-error")
81 }
82 ErrorType::InvalidArgumentError => {
83 write!(f, "invalid-argument-error")
84 }
85 }
86 }
87}
88
89impl FromStr for ErrorType {
90 type Err = ();
91
92 fn from_str(s: &str) -> Result<ErrorType, ()> {
93 match s {
94 "undefined-error" => Ok(ErrorType::UndefinedError),
95 "creation-error" => Ok(ErrorType::CreationError),
96 "updating-error" => Ok(ErrorType::UpdatingError),
97 "fetching-error" => Ok(ErrorType::FetchingError),
98 "deletion-error" => Ok(ErrorType::DeletionError),
99 "use-case-error" => Ok(ErrorType::UseCaseError),
100 "execution-error" => Ok(ErrorType::ExecutionError),
101 "invalid-repository-error" => Ok(ErrorType::InvalidRepositoryError),
102 "invalid-argument-error" => Ok(ErrorType::InvalidArgumentError),
103 _ => Err(()),
104 }
105 }
106}
107
108#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
109#[serde(rename_all = "camelCase")]
110pub enum ErrorCodes {
111 Codes(Vec<String>),
112 Unmapped,
113}
114
115impl ErrorCodes {
116 pub fn default() -> ErrorCodes {
117 ErrorCodes::Unmapped
118 }
119}
120
121impl Display for ErrorCodes {
122 fn fmt(&self, f: &mut Formatter) -> FmtResult {
123 match self {
124 ErrorCodes::Codes(codes) => {
125 write!(f, "{}", codes.join(MappedErrors::codes_delimiter()))
126 }
127 ErrorCodes::Unmapped => write!(f, "unmapped"),
128 }
129 }
130}
131
132#[derive(Debug, Deserialize, Serialize, Clone)]
133pub struct MappedErrors {
134 msg: String,
136
137 error_type: ErrorType,
140
141 expected: bool,
143
144 codes: ErrorCodes,
147}
148
149impl Error for MappedErrors {}
150
151impl Display for MappedErrors {
152 fn fmt(&self, f: &mut Formatter) -> FmtResult {
153 let code_key = MappedErrors::code_key();
154 let error_type_key = MappedErrors::error_type_key();
155
156 let code_value = match self.codes.to_owned() {
157 ErrorCodes::Codes(codes) => codes.join(Self::codes_delimiter()),
158 ErrorCodes::Unmapped => String::from("none"),
159 };
160
161 write!(
162 f,
163 "[{}={}{}{}={}] {}",
164 code_key,
165 code_value,
166 Self::msg_paras_delimiter(),
167 error_type_key,
168 self.error_type,
169 self.msg
170 )
171 }
172}
173
174impl MappedErrors {
175 pub fn error_type(&self) -> ErrorType {
184 self.error_type
185 }
186
187 pub fn msg(&self) -> String {
189 self.msg.to_owned()
190 }
191
192 pub fn code(&self) -> ErrorCodes {
194 self.codes.to_owned()
195 }
196
197 pub fn expected(&self) -> bool {
199 self.expected.to_owned()
200 }
201
202 pub fn has_str_code(&self, code: &str) -> bool {
205 if code == "none" {
206 return false;
207 }
208
209 if let ErrorCodes::Codes(inner_code) = &self.codes {
210 return inner_code.into_iter().any(|i| i.as_str() == code);
211 };
212
213 return false;
214 }
215
216 pub fn is_in(&self, codes: Vec<&str>) -> bool {
217 for code in codes {
218 if self.has_str_code(code) {
219 return true;
220 }
221 }
222
223 return false;
224 }
225
226 pub fn as_error<T>(self) -> Result<T, Self> {
235 if let true = self.expected {
236 warn!("{:?}", &self.to_string());
237 } else {
238 error!("{:?}", &self.to_string());
239 }
240
241 Err(self)
242 }
243
244 pub fn with_exp_true(mut self) -> Self {
246 self.expected = true;
247 self
248 }
249
250 pub fn with_code(mut self, code: &str) -> Self {
252 let code = code.to_string();
253 if code == "none" {
254 return self;
255 }
256
257 let mut codes = match self.to_owned().codes {
258 ErrorCodes::Codes(codes) => codes,
259 ErrorCodes::Unmapped => vec![],
260 };
261
262 codes.push(code);
263 codes.sort();
264 codes.dedup();
265
266 self.codes = ErrorCodes::Codes(codes);
267 self
268 }
269
270 pub fn with_previous(mut self, prev: MappedErrors) -> Self {
272 self.msg = format!(
273 "[CURRENT_ERROR] {}; [PRECEDING_ERROR] {}",
274 self.msg,
275 &prev.to_string()
276 );
277
278 self
279 }
280
281 pub fn with_error_type(mut self, error_type: ErrorType) -> Self {
283 self.error_type = error_type;
284 self
285 }
286
287 pub(super) fn default(msg: String) -> Self {
293 Self {
294 msg: Self::sanitize_msg(msg),
295 error_type: ErrorType::default(),
296 expected: false,
297 codes: ErrorCodes::default(),
298 }
299 }
300
301 pub(super) fn new(
303 msg: String,
304 exp: Option<bool>,
305 prev: Option<MappedErrors>,
306 error_type: ErrorType,
307 ) -> Self {
308 let exp = exp.unwrap_or(true);
309
310 if !exp {
311 error!("Unexpected error: ({}){}", &error_type, &msg);
312 } else {
313 warn!("{:?}", &msg);
314 }
315
316 if prev.is_some() {
317 let updated_msg = format!(
318 "[CURRENT_ERROR] {:?}; [PRECEDING_ERROR] {:?}",
319 msg,
320 &prev.unwrap().msg
321 );
322
323 return Self::new(updated_msg, Some(exp), None, error_type);
324 }
325
326 Self {
327 msg,
328 error_type,
329 expected: exp,
330 codes: ErrorCodes::default(),
331 }
332 }
333
334 fn code_key() -> &'static str {
336 "codes"
337 }
338
339 pub(self) fn codes_delimiter() -> &'static str {
341 ","
342 }
343
344 pub(self) fn msg_paras_delimiter() -> &'static str {
346 " "
347 }
348
349 fn error_type_key() -> &'static str {
351 "error_type"
352 }
353
354 fn sanitize_msg(msg: String) -> String {
356 msg.as_str().replace(";", ",").to_string()
357 }
358
359 pub fn from_str_msg(msg: String) -> Self {
361 let pattern = Regex::new(
362 r"^\[codes=([a-zA-Z0-9,]+)\serror_type=([a-zA-Z-]+)\]\s(.+)$",
363 )
364 .unwrap();
365
366 if pattern.is_match(&msg) {
367 let capture = pattern.captures(&msg).unwrap();
368 let code = &capture[1];
369 let msg = capture[3].to_string();
370
371 let error_type = match ErrorType::from_str(&capture[2]) {
372 Ok(error_type) => error_type,
373 Err(_) => ErrorType::UndefinedError,
374 };
375
376 return MappedErrors::new(msg, None, None, error_type)
377 .with_code(code);
378 };
379
380 MappedErrors::new(msg, None, None, ErrorType::UndefinedError)
381 }
382}
383
384#[cfg(test)]
389mod tests {
390
391 #[test]
392 fn test_error_type() {
393 fn error_dispatcher() -> Result<(), super::MappedErrors> {
394 Err(super::MappedErrors::new(
395 "This is a test error".to_string(),
396 Some(true),
397 None,
398 super::ErrorType::UndefinedError,
399 ))
400 }
401
402 fn error_handler() -> Result<(), super::MappedErrors> {
403 error_dispatcher()?;
404 Ok(())
405 }
406
407 let response = error_handler().unwrap_err();
408
409 assert_eq!(response.error_type(), super::ErrorType::UndefinedError);
410 }
411
412 #[test]
413 fn test_error_msg() {
414 fn error_dispatcher() -> Result<(), super::MappedErrors> {
415 Err(super::MappedErrors::new(
416 "This is a test error".to_string(),
417 Some(true),
418 None,
419 super::ErrorType::UndefinedError,
420 ))
421 }
422
423 fn error_handler() -> Result<(), super::MappedErrors> {
424 error_dispatcher()?;
425 Ok(())
426 }
427
428 let response = error_handler().unwrap_err();
429
430 assert_eq!(
431 response.to_string(),
432 format!(
433 "[{}=none{}{}=undefined-error] This is a test error",
434 super::MappedErrors::code_key(),
435 super::MappedErrors::msg_paras_delimiter(),
436 super::MappedErrors::error_type_key()
437 )
438 );
439 }
440
441 #[test]
442 fn test_from_msg() {
443 let msg = format!(
444 "[{}=none{}{}=undefined-error] This is a test error",
445 super::MappedErrors::code_key(),
446 super::MappedErrors::msg_paras_delimiter(),
447 super::MappedErrors::error_type_key()
448 );
449
450 let response = super::MappedErrors::from_str_msg(msg.to_string());
451 let previous = response.to_owned();
452
453 assert_eq!(response.to_string(), msg);
454
455 let with_previous = response.with_previous(previous);
456
457 let from_str_msg =
458 super::MappedErrors::from_str_msg(with_previous.msg());
459
460 assert_eq!(with_previous.msg(), from_str_msg.msg());
461 }
462
463 #[test]
464 fn test_has_str_code() {
465 fn error_dispatcher() -> Result<(), super::MappedErrors> {
466 Err(super::MappedErrors::new(
467 "This is a test error".to_string(),
468 Some(true),
469 None,
470 super::ErrorType::UndefinedError,
471 ))
472 }
473
474 fn error_handler() -> Result<(), super::MappedErrors> {
475 error_dispatcher()?;
476 Ok(())
477 }
478
479 let response = error_handler().unwrap_err();
480
481 assert!(!response.has_str_code("none"));
482 }
483
484 #[test]
485 fn test_is_in() {
486 fn error_dispatcher(
487 codes: Option<Vec<String>>,
488 ) -> Result<(), super::MappedErrors> {
489 if codes.is_some() {
490 let mut errors = super::MappedErrors::new(
491 "This is a test error".to_string(),
492 Some(true),
493 None,
494 super::ErrorType::UndefinedError,
495 );
496
497 for code in codes.unwrap() {
498 errors = errors.with_code(code.as_str());
499 }
500
501 return Err(errors);
502 }
503
504 Err(super::MappedErrors::new(
505 "This is a test error".to_string(),
506 Some(true),
507 None,
508 super::ErrorType::UndefinedError,
509 ))
510 }
511
512 fn error_handler(
513 codes: Option<Vec<String>>,
514 ) -> Result<(), super::MappedErrors> {
515 error_dispatcher(codes)?;
516 Ok(())
517 }
518
519 let none_response = error_handler(None).unwrap_err();
520 let some_response = error_handler(Some(vec![
521 "ID00001".to_string(),
522 "ID00005".to_string(),
523 ]))
524 .unwrap_err();
525
526 assert!(!none_response.is_in(vec!["none", "ID00001"]));
527 assert!(!some_response.is_in(vec!["none", "ID00002"]));
528 assert!(!some_response.is_in(vec!["ID00002", "ID00003"]));
529 assert!(some_response.is_in(vec!["none", "ID00001"]));
530 }
531}