validr/
error.rs

1use actix_web::{body::BoxBody, HttpRequest, HttpResponse, Responder, ResponseError};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::error::Error as StdError;
5
6#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
7pub struct ValidationError {
8    field: String,
9    errors: Vec<String>,
10}
11
12impl Default for ValidationError {
13    fn default() -> Self {
14        Self::new()
15    }
16}
17
18impl ValidationError {
19    /// Get new validation error
20    pub fn new() -> Self {
21        ValidationError {
22            field: "".to_string(),
23            errors: vec![],
24        }
25    }
26
27    /// Set validation error field name
28    pub fn set_field_name(&mut self, name: &str) {
29        self.field = name.to_string();
30    }
31
32    /// Add error code
33    pub fn add(&mut self, error: &str) {
34        self.errors.push(error.to_string());
35    }
36
37    /// Check if it already contains certain error code
38    pub fn contains(&self, error_code: &str) -> bool {
39        matches!(
40            self.errors.iter().position(|e| e.starts_with(error_code)),
41            Some(_)
42        )
43    }
44
45    /// Check if the error is empty
46    pub fn is_empty(&self) -> bool {
47        self.errors.is_empty()
48    }
49
50    /// Get the number of errors
51    pub fn len(&self) -> usize {
52        self.errors.len()
53    }
54
55    /// Check if validation error has anything to show
56    pub fn has_errors(&self) -> bool {
57        !self.field.is_empty() && !self.errors.is_empty()
58    }
59
60    /// Return the error field name
61    pub fn get_name(&self) -> String {
62        self.field.clone()
63    }
64
65    /// Return all the errors
66    pub fn get_errors(&self) -> Vec<String> {
67        self.errors.clone()
68    }
69}
70
71#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
72pub struct ValidationErrors {
73    errors: HashMap<String, ValidationError>,
74}
75
76impl Default for ValidationErrors {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82impl ValidationErrors {
83    /// Create new validation errors holder
84    pub fn new() -> Self {
85        ValidationErrors {
86            errors: HashMap::new(),
87        }
88    }
89
90    /// Add validation error
91    pub fn add(&mut self, error: ValidationError) {
92        let name = error.field.clone();
93        let mut e = error.clone();
94
95        if self.errors.contains_key(&name) {
96            e = self.errors.remove(&name).unwrap();
97
98            for rule in &error.errors {
99                if !e.contains(rule) {
100                    e.add(rule);
101                }
102            }
103        }
104
105        if e.has_errors() {
106            self.errors.insert(name, e);
107        }
108    }
109
110    /// Check if the error is empty
111    pub fn is_empty(&self) -> bool {
112        self.errors.is_empty()
113    }
114
115    /// Get the number of errors
116    pub fn len(&self) -> usize {
117        self.errors.len()
118    }
119
120    /// Check if there are any validation errors
121    pub fn has_errors(&self) -> bool {
122        !self.errors.is_empty()
123    }
124
125    /// Copy single error from the hash map
126    pub fn get_error(&self, key: &str) -> Result<ValidationError, String> {
127        if self.has_errors() && self.errors.contains_key(key) {
128            match self.errors.get_key_value(key) {
129                Some((_k, error)) => Ok(error.clone()),
130                None => Err("no_error".to_string()),
131            }
132        } else {
133            Err("no_error".to_string())
134        }
135    }
136
137    /// Return all the errors
138    pub fn get_errors(&self) -> HashMap<String, ValidationError> {
139        self.errors.clone()
140    }
141}
142
143/// Allow the use of "{}" format specifier
144impl std::fmt::Display for ValidationErrors {
145    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
146        write!(f, "{:?}", self)
147    }
148}
149
150/// Implement std::error::Error for ValidationErrors
151impl StdError for ValidationErrors {
152    fn cause(&self) -> Option<&dyn StdError> {
153        Some(self)
154    }
155}
156
157/// Allow the error to be returned in actix as error response
158impl ResponseError for ValidationErrors {
159    fn error_response(&self) -> HttpResponse {
160        HttpResponse::UnprocessableEntity().json(self)
161    }
162}
163
164/// Allow the error to be returned into responder for actix right away
165impl Responder for ValidationErrors {
166    type Body = BoxBody;
167
168    fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
169        self.error_response()
170    }
171}