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 pub fn new() -> Self {
21 ValidationError {
22 field: "".to_string(),
23 errors: vec![],
24 }
25 }
26
27 pub fn set_field_name(&mut self, name: &str) {
29 self.field = name.to_string();
30 }
31
32 pub fn add(&mut self, error: &str) {
34 self.errors.push(error.to_string());
35 }
36
37 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 pub fn is_empty(&self) -> bool {
47 self.errors.is_empty()
48 }
49
50 pub fn len(&self) -> usize {
52 self.errors.len()
53 }
54
55 pub fn has_errors(&self) -> bool {
57 !self.field.is_empty() && !self.errors.is_empty()
58 }
59
60 pub fn get_name(&self) -> String {
62 self.field.clone()
63 }
64
65 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 pub fn new() -> Self {
85 ValidationErrors {
86 errors: HashMap::new(),
87 }
88 }
89
90 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 pub fn is_empty(&self) -> bool {
112 self.errors.is_empty()
113 }
114
115 pub fn len(&self) -> usize {
117 self.errors.len()
118 }
119
120 pub fn has_errors(&self) -> bool {
122 !self.errors.is_empty()
123 }
124
125 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 pub fn get_errors(&self) -> HashMap<String, ValidationError> {
139 self.errors.clone()
140 }
141}
142
143impl std::fmt::Display for ValidationErrors {
145 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
146 write!(f, "{:?}", self)
147 }
148}
149
150impl StdError for ValidationErrors {
152 fn cause(&self) -> Option<&dyn StdError> {
153 Some(self)
154 }
155}
156
157impl ResponseError for ValidationErrors {
159 fn error_response(&self) -> HttpResponse {
160 HttpResponse::UnprocessableEntity().json(self)
161 }
162}
163
164impl Responder for ValidationErrors {
166 type Body = BoxBody;
167
168 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
169 self.error_response()
170 }
171}