1use std::borrow::Cow;
2use std::collections::{hash_map::Entry::Vacant, BTreeMap, HashMap};
3
4use serde::ser::Serialize;
5use serde_derive::{Deserialize, Serialize};
6use serde_json::{to_value, Value};
7
8#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
9pub struct ValidationError {
10    pub code: Cow<'static, str>,
11    pub message: Option<Cow<'static, str>>,
12    pub params: HashMap<Cow<'static, str>, Value>,
13}
14
15impl ValidationError {
16    pub fn new(code: &'static str) -> ValidationError {
17        ValidationError { code: Cow::from(code), message: None, params: HashMap::new() }
18    }
19
20    pub fn add_param<T: Serialize>(&mut self, name: Cow<'static, str>, val: &T) {
21        self.params.insert(name, to_value(val).unwrap());
22    }
23
24    pub fn with_message(mut self, message: Cow<'static, str>) -> ValidationError {
27        self.message = Some(message);
28        self
29    }
30}
31
32impl std::error::Error for ValidationError {
33    fn description(&self) -> &str {
34        &self.code
35    }
36    fn cause(&self) -> Option<&dyn std::error::Error> {
37        None
38    }
39}
40
41#[derive(Debug, Serialize, Clone, PartialEq)]
42#[serde(untagged)]
43pub enum ValidationErrorsKind {
44    Struct(Box<ValidationErrors>),
45    List(BTreeMap<usize, Box<ValidationErrors>>),
46    Field(Vec<ValidationError>),
47}
48
49#[derive(Default, Debug, Serialize, Clone, PartialEq)]
50pub struct ValidationErrors(pub HashMap<&'static str, ValidationErrorsKind>);
51
52impl ValidationErrors {
53    pub fn new() -> ValidationErrors {
54        ValidationErrors(HashMap::new())
55    }
56
57    #[must_use]
61    pub fn has_error(result: &Result<(), ValidationErrors>, field: &'static str) -> bool {
62        match result {
63            Ok(()) => false,
64            Err(ref errs) => errs.contains_key(field),
65        }
66    }
67
68    pub fn merge_self(
69        &mut self,
70        field: &'static str,
71        child: Result<(), ValidationErrors>,
72    ) -> &mut ValidationErrors {
73        match child {
74            Ok(()) => self,
75            Err(errors) => {
76                for (_, e) in errors.0 {
77                    self.add_nested(field, e);
78                }
79                self
80            }
81        }
82    }
83
84    pub fn merge(
87        parent: Result<(), ValidationErrors>,
88        field: &'static str,
89        child: Result<(), ValidationErrors>,
90    ) -> Result<(), ValidationErrors> {
91        match child {
92            Ok(()) => parent,
93            Err(errors) => {
94                parent.and_then(|_| Err(ValidationErrors::new())).map_err(|mut parent_errors| {
95                    parent_errors.add_nested(field, ValidationErrorsKind::Struct(Box::new(errors)));
96                    parent_errors
97                })
98            }
99        }
100    }
101
102    pub fn merge_all(
105        parent: Result<(), ValidationErrors>,
106        field: &'static str,
107        children: Vec<Result<(), ValidationErrors>>,
108    ) -> Result<(), ValidationErrors> {
109        let errors = children
110            .into_iter()
111            .enumerate()
112            .filter_map(|(i, res)| res.err().map(|mut err| (i, err.remove(field))))
113            .filter_map(|(i, entry)| match entry {
114                Some(ValidationErrorsKind::Struct(errors)) => Some((i, errors)),
115                _ => None,
116            })
117            .collect::<BTreeMap<_, _>>();
118
119        if errors.is_empty() {
120            parent
121        } else {
122            parent.and_then(|_| Err(ValidationErrors::new())).map_err(|mut parent_errors| {
123                parent_errors.add_nested(field, ValidationErrorsKind::List(errors));
124                parent_errors
125            })
126        }
127    }
128
129    pub fn errors(&self) -> &HashMap<&'static str, ValidationErrorsKind> {
132        &self.0
133    }
134
135    pub fn errors_mut(&mut self) -> &mut HashMap<&'static str, ValidationErrorsKind> {
138        &mut self.0
139    }
140
141    pub fn into_errors(self) -> HashMap<&'static str, ValidationErrorsKind> {
143        self.0
144    }
145
146    pub fn field_errors(&self) -> HashMap<&'static str, &Vec<ValidationError>> {
148        self.0
149            .iter()
150            .filter_map(|(k, v)| {
151                if let ValidationErrorsKind::Field(errors) = v {
152                    Some((*k, errors))
153                } else {
154                    None
155                }
156            })
157            .collect::<HashMap<_, _>>()
158    }
159
160    pub fn add(&mut self, field: &'static str, error: ValidationError) {
161        if let ValidationErrorsKind::Field(ref mut vec) =
162            self.0.entry(field).or_insert_with(|| ValidationErrorsKind::Field(vec![]))
163        {
164            vec.push(error);
165        } else {
166            panic!("Attempt to add field validation to a non-Field ValidationErrorsKind instance");
167        }
168    }
169
170    #[must_use]
171    pub fn is_empty(&self) -> bool {
172        self.0.is_empty()
173    }
174
175    fn add_nested(&mut self, field: &'static str, errors: ValidationErrorsKind) {
176        if let Vacant(entry) = self.0.entry(field) {
177            entry.insert(errors);
178        } else {
179            panic!("Attempt to replace non-empty ValidationErrors entry");
180        }
181    }
182
183    #[must_use]
184    fn contains_key(&self, field: &'static str) -> bool {
185        self.0.contains_key(field)
186    }
187
188    fn remove(&mut self, field: &'static str) -> Option<ValidationErrorsKind> {
189        self.0.remove(field)
190    }
191}
192
193impl std::error::Error for ValidationErrors {
194    fn description(&self) -> &str {
195        "Validation failed"
196    }
197    fn cause(&self) -> Option<&dyn std::error::Error> {
198        None
199    }
200}