clean_base/utils/errors/
base.rs

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/// This enumerator are used to standardize errors codes dispatched during the
11/// `MappedErrors` struct usage.
12#[derive(Debug, Clone, Copy, 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 a fetching error occurs.
32    ///
33    /// Related: CRUD
34    FetchingError,
35
36    /// This error type is used when a deletion error occurs.
37    ///
38    /// Related: CRUD
39    DeletionError,
40
41    /// This error type is used when a use case error occurs.
42    ///
43    /// Related: Use Case
44    UseCaseError,
45
46    /// This error type is used when an execution error occurs. This error type
47    /// is used when the error is not related to a specific action.
48    ///
49    /// Related: Execution
50    ExecutionError,
51
52    /// This error type is used when an invalid data repository error occurs.
53    ///
54    /// Related: Data Repository
55    InvalidRepositoryError,
56
57    /// This error type is used when an invalid argument error occurs.
58    ///
59    /// Related: Argument
60    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    /// This field contains the error message.
135    msg: String,
136
137    /// This field contains the error type. This field is used to standardize
138    /// errors codes.
139    error_type: ErrorType,
140
141    /// If dispatched error is expected or not.
142    expected: bool,
143
144    /// This field contains the error code. This field is used to standardize
145    /// errors evaluation in downstream applications.
146    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    // ? -----------------------------------------------------------------------
176    // ? INSTANCE METHODS
177    //
178    // Getters
179    //
180    // ? -----------------------------------------------------------------------
181
182    /// This method returns the error type of the current error.
183    pub fn error_type(&self) -> ErrorType {
184        self.error_type
185    }
186
187    /// This method returns the error message of the current error.
188    pub fn msg(&self) -> String {
189        self.msg.to_owned()
190    }
191
192    /// This method returns the error code key of the current error.
193    pub fn code(&self) -> ErrorCodes {
194        self.codes.to_owned()
195    }
196
197    /// This method returns the error code key of the current error.
198    pub fn expected(&self) -> bool {
199        self.expected.to_owned()
200    }
201
202    /// This method returns a boolean indicating if the current error is
203    /// expected or not.
204    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    // ? -----------------------------------------------------------------------
227    // ? INSTANCE METHODS
228    //
229    // Modifiers
230    //
231    // ? -----------------------------------------------------------------------
232
233    /// Evoked when a Err return is desired.
234    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    /// Dispatches an log error indicating unexpected error.
245    pub fn with_exp_true(mut self) -> Self {
246        self.expected = true;
247        self
248    }
249
250    /// Set the error code of the current error.
251    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    /// Include previous mapped error in message
271    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    /// Set the error type of the current error.
282    pub fn with_error_type(mut self, error_type: ErrorType) -> Self {
283        self.error_type = error_type;
284        self
285    }
286
287    // ? -----------------------------------------------------------------------
288    // ? STRUCTURAL METHODS
289    // ? -----------------------------------------------------------------------
290
291    /// Build a anemic MappedError instance.
292    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    /// This method returns a new `MappedErrors` struct.
302    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    /// Set the error type of the current error.
335    fn code_key() -> &'static str {
336        "codes"
337    }
338
339    /// Set delimiter of the error codes.
340    pub(self) fn codes_delimiter() -> &'static str {
341        ","
342    }
343
344    /// Set delimiter of mapped errors string parameters.
345    pub(self) fn msg_paras_delimiter() -> &'static str {
346        " "
347    }
348
349    /// Set the error type of the current error.
350    fn error_type_key() -> &'static str {
351        "error_type"
352    }
353
354    /// Remove invalid characters from message.
355    fn sanitize_msg(msg: String) -> String {
356        msg.as_str().replace(";", ",").to_string()
357    }
358
359    /// This method returns a new `MappedErrors` struct from a string.
360    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// * ---------------------------------------------------------------------------
385// * TESTS
386// * ---------------------------------------------------------------------------
387
388#[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}