mycelium_base/utils/errors/
base.rs

1use regex::Regex;
2use serde::{Deserialize, Serialize};
3use std::{
4    error::Error,
5    fmt::{Display, Formatter, Result as FmtResult},
6    str::FromStr,
7};
8use tracing::{debug, error, warn};
9
10/// This enumerator are used to standardize errors codes dispatched during the
11/// `MappedErrors` struct usage.
12#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub enum ErrorType {
15    /// This error type is used when the error type is not defined. This is the
16    /// default value for the `ErrorType` enum.
17    ///
18    /// Related: Undefined
19    UndefinedError,
20
21    /// This error type is used when a creation error occurs.
22    ///
23    /// Related: CRUD
24    CreationError,
25
26    /// This error type is used when an updating error occurs.
27    ///
28    /// Related: CRUD
29    UpdatingError,
30
31    /// This error type is used when an updating many error occurs.
32    ///
33    /// Related: CRUD
34    UpdatingManyError,
35
36    /// This error type is used when a fetching error occurs.
37    ///
38    /// Related: CRUD
39    FetchingError,
40
41    /// This error type is used when a deletion error occurs.
42    ///
43    /// Related: CRUD
44    DeletionError,
45
46    /// This error type is used when a use case error occurs.
47    ///
48    /// Related: Use Case
49    UseCaseError,
50
51    /// This error type is used when an execution error occurs. This error type
52    /// is used when the error is not related to a specific action.
53    ///
54    /// Related: Execution
55    ExecutionError,
56
57    /// This error type is used when an invalid data repository error occurs.
58    ///
59    /// Related: Data Repository
60    InvalidRepositoryError,
61
62    /// This error type is used when an invalid argument error occurs.
63    ///
64    /// Related: Argument
65    InvalidArgumentError,
66
67    /// This error type is used when an error occurs in the data transfer layer.
68    ///
69    /// Related: Data Transfer Objects
70    DataTransferLayerError,
71
72    /// This error type is used when a general error occurs.
73    ///
74    /// Related: General
75    GeneralError(String),
76}
77
78impl ErrorType {
79    fn default() -> Self {
80        Self::UndefinedError
81    }
82}
83
84impl Display for ErrorType {
85    fn fmt(&self, f: &mut Formatter) -> FmtResult {
86        match self {
87            ErrorType::UndefinedError => write!(f, "undefined-error"),
88            ErrorType::CreationError => write!(f, "creation-error"),
89            ErrorType::UpdatingError => write!(f, "updating-error"),
90            ErrorType::UpdatingManyError => write!(f, "updating-many-error"),
91            ErrorType::FetchingError => write!(f, "fetching-error"),
92            ErrorType::DeletionError => write!(f, "deletion-error"),
93            ErrorType::UseCaseError => write!(f, "use-case-error"),
94            ErrorType::ExecutionError => write!(f, "execution-error"),
95            ErrorType::InvalidRepositoryError => {
96                write!(f, "invalid-repository-error")
97            }
98            ErrorType::InvalidArgumentError => {
99                write!(f, "invalid-argument-error")
100            }
101            ErrorType::DataTransferLayerError => {
102                write!(f, "data-transfer-layer-error")
103            }
104            ErrorType::GeneralError(msg) => write!(f, "{}", msg),
105        }
106    }
107}
108
109impl FromStr for ErrorType {
110    type Err = ();
111
112    fn from_str(s: &str) -> Result<ErrorType, ()> {
113        match s {
114            "undefined-error" => Ok(ErrorType::UndefinedError),
115            "creation-error" => Ok(ErrorType::CreationError),
116            "updating-error" => Ok(ErrorType::UpdatingError),
117            "updating-many-error" => Ok(ErrorType::UpdatingManyError),
118            "fetching-error" => Ok(ErrorType::FetchingError),
119            "deletion-error" => Ok(ErrorType::DeletionError),
120            "use-case-error" => Ok(ErrorType::UseCaseError),
121            "execution-error" => Ok(ErrorType::ExecutionError),
122            "invalid-repository-error" => Ok(ErrorType::InvalidRepositoryError),
123            "invalid-argument-error" => Ok(ErrorType::InvalidArgumentError),
124            "data-transfer-layer-error" => {
125                Ok(ErrorType::DataTransferLayerError)
126            }
127            other => Ok(ErrorType::GeneralError(other.to_string())),
128        }
129    }
130}
131
132#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
133#[serde(rename_all = "camelCase")]
134pub enum ErrorCodes {
135    Codes(Vec<String>),
136    Unmapped,
137}
138
139impl ErrorCodes {
140    pub fn default() -> ErrorCodes {
141        ErrorCodes::Unmapped
142    }
143}
144
145impl Display for ErrorCodes {
146    fn fmt(&self, f: &mut Formatter) -> FmtResult {
147        match self {
148            ErrorCodes::Codes(codes) => {
149                write!(f, "{}", codes.join(MappedErrors::codes_delimiter()))
150            }
151            ErrorCodes::Unmapped => write!(f, "unmapped"),
152        }
153    }
154}
155
156#[derive(Debug, Deserialize, Serialize, Clone)]
157pub struct MappedErrors {
158    /// This field contains the error message.
159    msg: String,
160
161    /// This field contains the error type. This field is used to standardize
162    /// errors codes.
163    error_type: ErrorType,
164
165    /// If dispatched error is expected or not.
166    expected: bool,
167
168    /// This field contains the error code. This field is used to standardize
169    /// errors evaluation in downstream applications.
170    codes: ErrorCodes,
171}
172
173impl Error for MappedErrors {}
174
175impl Display for MappedErrors {
176    fn fmt(&self, f: &mut Formatter) -> FmtResult {
177        let code_key = MappedErrors::code_key();
178        let error_type_key = MappedErrors::error_type_key();
179
180        let code_value = match self.codes.to_owned() {
181            ErrorCodes::Codes(codes) => codes.join(Self::codes_delimiter()),
182            ErrorCodes::Unmapped => String::from("none"),
183        };
184
185        write!(
186            f,
187            "[{}={}{}{}={}] {}",
188            code_key,
189            code_value,
190            Self::msg_paras_delimiter(),
191            error_type_key,
192            self.error_type,
193            self.msg
194        )
195    }
196}
197
198impl MappedErrors {
199    // ? -----------------------------------------------------------------------
200    // ? INSTANCE METHODS
201    //
202    // Getters
203    //
204    // ? -----------------------------------------------------------------------
205
206    /// This method returns the error type of the current error.
207    pub fn error_type(&self) -> ErrorType {
208        self.error_type.to_owned()
209    }
210
211    /// This method returns the error message of the current error.
212    pub fn msg(&self) -> String {
213        self.msg.to_owned()
214    }
215
216    /// This method returns the error code key of the current error.
217    pub fn code(&self) -> ErrorCodes {
218        self.codes.to_owned()
219    }
220
221    /// This method returns the error code key of the current error.
222    pub fn expected(&self) -> bool {
223        self.expected.to_owned()
224    }
225
226    /// This method returns a boolean indicating if the current error is
227    /// expected or not.
228    pub fn has_str_code(&self, code: &str) -> bool {
229        if code == "none" {
230            return false;
231        }
232
233        if let ErrorCodes::Codes(inner_code) = &self.codes {
234            return inner_code.iter().any(|i| i.as_str() == code);
235        };
236
237        false
238    }
239
240    pub fn is_in<T: ToString>(&self, codes: Vec<T>) -> bool {
241        for code in codes {
242            if self.has_str_code(code.to_string().as_str()) {
243                return true;
244            }
245        }
246
247        false
248    }
249
250    // ? -----------------------------------------------------------------------
251    // ? INSTANCE METHODS
252    //
253    // Modifiers
254    //
255    // ? -----------------------------------------------------------------------
256
257    /// Evoked when a Err return is desired.
258    pub fn as_error<T>(self) -> Result<T, Self> {
259        if self.expected {
260            debug!("{:?}", &self.to_string());
261        } else {
262            error!("{:?}", &self.to_string());
263        }
264
265        Err(self)
266    }
267
268    /// Dispatches an log error indicating unexpected error.
269    pub fn with_exp_true(mut self) -> Self {
270        self.expected = true;
271        self
272    }
273
274    /// Set the error code of the current error.
275    pub fn with_code<T: ToString>(mut self, code: T) -> Self {
276        let binding = code.to_string();
277        let code = binding.as_str();
278        if code == "none" {
279            return self;
280        }
281
282        let mut codes = match self.to_owned().codes {
283            ErrorCodes::Codes(codes) => codes,
284            ErrorCodes::Unmapped => vec![],
285        };
286
287        codes.push(code.to_string());
288        codes.sort();
289        codes.dedup();
290
291        self.codes = ErrorCodes::Codes(codes);
292        self
293    }
294
295    /// Include previous mapped error in message
296    pub fn with_previous(mut self, prev: MappedErrors) -> Self {
297        self.msg = format!(
298            "[CURRENT_ERROR] {}; [PRECEDING_ERROR] {}",
299            self.msg,
300            &prev.to_string()
301        );
302
303        self
304    }
305
306    /// Set the error type of the current error.
307    pub fn with_error_type(mut self, error_type: ErrorType) -> Self {
308        self.error_type = error_type;
309        self
310    }
311
312    // ? -----------------------------------------------------------------------
313    // ? STRUCTURAL METHODS
314    // ? -----------------------------------------------------------------------
315
316    /// Build a anemic MappedError instance.
317    pub(super) fn default(msg: String) -> Self {
318        Self {
319            msg: Self::sanitize_msg(msg),
320            error_type: ErrorType::default(),
321            expected: false,
322            codes: ErrorCodes::default(),
323        }
324    }
325
326    /// This method returns a new `MappedErrors` struct.
327    pub(super) fn new(
328        msg: String,
329        exp: Option<bool>,
330        prev: Option<MappedErrors>,
331        error_type: ErrorType,
332    ) -> Self {
333        let exp = exp.unwrap_or(true);
334
335        if !exp {
336            error!("Unexpected error: ({}){}", &error_type, &msg);
337        } else {
338            warn!("{:?}", &msg);
339        }
340
341        if prev.is_some() {
342            let updated_msg = format!(
343                "[CURRENT_ERROR] {:?}; [PRECEDING_ERROR] {:?}",
344                msg,
345                &prev.unwrap().msg
346            );
347
348            return Self::new(updated_msg, Some(exp), None, error_type);
349        }
350
351        Self {
352            msg,
353            error_type,
354            expected: exp,
355            codes: ErrorCodes::default(),
356        }
357    }
358
359    /// Set the error type of the current error.
360    fn code_key() -> &'static str {
361        "codes"
362    }
363
364    /// Set delimiter of the error codes.
365    pub(self) fn codes_delimiter() -> &'static str {
366        ","
367    }
368
369    /// Set delimiter of mapped errors string parameters.
370    pub(self) fn msg_paras_delimiter() -> &'static str {
371        " "
372    }
373
374    /// Set the error type of the current error.
375    fn error_type_key() -> &'static str {
376        "error_type"
377    }
378
379    /// Remove invalid characters from message.
380    fn sanitize_msg(msg: String) -> String {
381        msg.as_str().replace(";", ",").to_string()
382    }
383
384    /// This method returns a new `MappedErrors` struct from a string.
385    pub fn from_str_msg(msg: String) -> Self {
386        let pattern = Regex::new(
387            r"^\[codes=([a-zA-Z0-9,]+)\serror_type=([a-zA-Z-]+)\]\s(.+)$",
388        )
389        .unwrap();
390
391        if pattern.is_match(&msg) {
392            let capture = pattern.captures(&msg).unwrap();
393            let code = &capture[1];
394            let msg = capture[3].to_string();
395
396            let error_type = match ErrorType::from_str(&capture[2]) {
397                Ok(error_type) => error_type,
398                Err(_) => ErrorType::UndefinedError,
399            };
400
401            return MappedErrors::new(msg, None, None, error_type)
402                .with_code(code);
403        };
404
405        MappedErrors::new(msg, None, None, ErrorType::UndefinedError)
406    }
407}
408
409// * ---------------------------------------------------------------------------
410// * TESTS
411// * ---------------------------------------------------------------------------
412
413#[cfg(test)]
414mod tests {
415
416    #[test]
417    fn test_error_type() {
418        fn error_dispatcher() -> Result<(), super::MappedErrors> {
419            Err(super::MappedErrors::new(
420                "This is a test error".to_string(),
421                Some(true),
422                None,
423                super::ErrorType::UndefinedError,
424            ))
425        }
426
427        fn error_handler() -> Result<(), super::MappedErrors> {
428            error_dispatcher()?;
429            Ok(())
430        }
431
432        let response = error_handler().unwrap_err();
433
434        assert_eq!(response.error_type(), super::ErrorType::UndefinedError);
435    }
436
437    #[test]
438    fn test_error_msg() {
439        fn error_dispatcher() -> Result<(), super::MappedErrors> {
440            Err(super::MappedErrors::new(
441                "This is a test error".to_string(),
442                Some(true),
443                None,
444                super::ErrorType::UndefinedError,
445            ))
446        }
447
448        fn error_handler() -> Result<(), super::MappedErrors> {
449            error_dispatcher()?;
450            Ok(())
451        }
452
453        let response = error_handler().unwrap_err();
454
455        assert_eq!(
456            response.to_string(),
457            format!(
458                "[{}=none{}{}=undefined-error] This is a test error",
459                super::MappedErrors::code_key(),
460                super::MappedErrors::msg_paras_delimiter(),
461                super::MappedErrors::error_type_key()
462            )
463        );
464    }
465
466    #[test]
467    fn test_from_msg() {
468        let msg = format!(
469            "[{}=none{}{}=undefined-error] This is a test error",
470            super::MappedErrors::code_key(),
471            super::MappedErrors::msg_paras_delimiter(),
472            super::MappedErrors::error_type_key()
473        );
474
475        let response = super::MappedErrors::from_str_msg(msg.to_string());
476        let previous = response.to_owned();
477
478        assert_eq!(response.to_string(), msg);
479
480        let with_previous = response.with_previous(previous);
481
482        let from_str_msg =
483            super::MappedErrors::from_str_msg(with_previous.msg());
484
485        assert_eq!(with_previous.msg(), from_str_msg.msg());
486    }
487
488    #[test]
489    fn test_has_str_code() {
490        fn error_dispatcher() -> Result<(), super::MappedErrors> {
491            Err(super::MappedErrors::new(
492                "This is a test error".to_string(),
493                Some(true),
494                None,
495                super::ErrorType::UndefinedError,
496            ))
497        }
498
499        fn error_handler() -> Result<(), super::MappedErrors> {
500            error_dispatcher()?;
501            Ok(())
502        }
503
504        let response = error_handler().unwrap_err();
505
506        assert!(!response.has_str_code("none"));
507    }
508
509    #[test]
510    fn test_is_in() {
511        fn error_dispatcher(
512            codes: Option<Vec<String>>,
513        ) -> Result<(), super::MappedErrors> {
514            if codes.is_some() {
515                let mut errors = super::MappedErrors::new(
516                    "This is a test error".to_string(),
517                    Some(true),
518                    None,
519                    super::ErrorType::UndefinedError,
520                );
521
522                for code in codes.unwrap() {
523                    errors = errors.with_code(code.as_str());
524                }
525
526                return Err(errors);
527            }
528
529            Err(super::MappedErrors::new(
530                "This is a test error".to_string(),
531                Some(true),
532                None,
533                super::ErrorType::UndefinedError,
534            ))
535        }
536
537        fn error_handler(
538            codes: Option<Vec<String>>,
539        ) -> Result<(), super::MappedErrors> {
540            error_dispatcher(codes)?;
541            Ok(())
542        }
543
544        let none_response = error_handler(None).unwrap_err();
545        let some_response = error_handler(Some(vec![
546            "ID00001".to_string(),
547            "ID00005".to_string(),
548        ]))
549        .unwrap_err();
550
551        assert!(!none_response.is_in(vec!["none", "ID00001"]));
552        assert!(!some_response.is_in(vec!["none", "ID00002"]));
553        assert!(!some_response.is_in(vec!["ID00002", "ID00003"]));
554        assert!(some_response.is_in(vec!["none", "ID00001"]));
555    }
556}