form_validation/
error.rs

1use std::{
2    fmt::{Debug, Display},
3    rc::Rc,
4};
5
6/// An error associated with a form field.
7pub struct ValidationError<Key> {
8    /// The key for the field that this validation error is associated with.
9    pub key: Key,
10    /// An identifier for the type of error this is.
11    pub type_id: &'static str,
12    /// Function that produces the error message.
13    message: Rc<dyn Fn(&Key) -> String>,
14}
15
16impl<Key> PartialEq for ValidationError<Key>
17where
18    Key: PartialEq,
19{
20    fn eq(&self, other: &Self) -> bool {
21        self.key == other.key
22            && self.type_id == other.type_id
23            && self.get_message() == other.get_message()
24    }
25}
26
27impl<Key> Clone for ValidationError<Key>
28where
29    Key: Clone,
30{
31    fn clone(&self) -> Self {
32        Self {
33            key: self.key.clone(),
34            type_id: self.type_id,
35            message: self.message.clone(),
36        }
37    }
38}
39
40impl<Key> ValidationError<Key> {
41    /// Create a new `ValidationError` with a generic message, and
42    /// specify the [type_id](ValidationError::type_id) which allows
43    /// the error type to be identified programatically.
44    pub fn new(key: Key, type_id: &'static str) -> Self {
45        Self {
46            key,
47            message: Rc::new(|_| "Validation error".to_string()),
48            type_id,
49        }
50    }
51
52    /// Factory method to set the message for this error.
53    pub fn message<S: Into<String>>(mut self, message: S) -> Self {
54        let message_string = message.into();
55        self.message = Rc::new(move |_| message_string.clone());
56        self
57    }
58
59    /// Factory method to set the message for this error from a
60    /// function that returns a `String`.
61    ///
62    /// ## Example
63    /// ```
64    /// use form_validation::ValidationError;
65    ///
66    /// let value = -10;
67    /// let error = ValidationError::new("field1", "NOT_LESS_THAN_0")
68    ///     .with_message(move |key| {
69    ///         format!(
70    ///            "The value of {} ({}) cannot be less than 0",
71    ///             key, value)
72    /// });
73    ///
74    /// assert_eq!("The value of field1 (-10) cannot be less than 0", error.to_string());
75    /// assert_eq!("NOT_LESS_THAN_0", error.type_id);
76    /// ```
77    pub fn with_message<F: Fn(&Key) -> String + 'static>(mut self, message_fn: F) -> Self {
78        self.message = Rc::new(message_fn);
79        self
80    }
81
82    /// Get the message for this error.
83    fn get_message(&self) -> String {
84        (self.message)(&self.key)
85    }
86}
87
88impl<Key> Display for ValidationError<Key> {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        write!(f, "{}", self.get_message())
91    }
92}
93
94impl<Key> Debug for ValidationError<Key>
95where
96    Key: Debug,
97{
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        write!(
100            f,
101            "ValidationError{{ key: {0:?}, type_id: {1}, message: {2} }}",
102            self.key,
103            self.type_id,
104            self.get_message()
105        )
106    }
107}
108
109impl<Key> std::error::Error for ValidationError<Key> where Key: Debug {}
110
111/// A collection of [ValidationError](ValidationError)s as a result of
112/// validating the fields of a form.
113#[derive(Debug, Clone)]
114pub struct ValidationErrors<Key> {
115    pub errors: Vec<ValidationError<Key>>,
116}
117
118impl<Key> PartialEq for ValidationErrors<Key>
119where
120    Key: PartialEq,
121{
122    fn eq(&self, other: &Self) -> bool {
123        self.errors.eq(&other.errors)
124    }
125}
126
127impl<Key> ValidationErrors<Key>
128where
129    Key: PartialEq + Clone,
130{
131    /// Create a new `ValidationErrors`.
132    pub fn new(errors: Vec<ValidationError<Key>>) -> Self {
133        Self { errors }
134    }
135
136    /// Get errors associated with the specified field key, or `None`
137    /// if there are no errors for that field.
138    pub fn get(&self, key: &Key) -> Option<ValidationErrors<Key>> {
139        let errors: Vec<ValidationError<Key>> = self
140            .errors
141            .iter()
142            .filter(|error| &error.key == key)
143            .map(|error| (*error).clone())
144            .collect();
145
146        if !errors.is_empty() {
147            Some(ValidationErrors::new(errors))
148        } else {
149            None
150        }
151    }
152
153    /// Returns true if there are no errors in this collection.
154    pub fn is_empty(&self) -> bool {
155        self.errors.is_empty()
156    }
157
158    /// Extend this collection of errors with the contents of another
159    /// collection.
160    pub fn extend(&mut self, errors: ValidationErrors<Key>) {
161        self.errors.extend(errors.errors)
162    }
163
164    /// The number of errors in this collection.
165    pub fn len(&self) -> usize {
166        self.errors.len()
167    }
168}
169
170impl<Key> Default for ValidationErrors<Key> {
171    fn default() -> Self {
172        Self { errors: Vec::new() }
173    }
174}
175
176impl<Key> Display for ValidationErrors<Key> {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        let errors: Vec<String> = self.errors.iter().map(|e| format!("{}", e)).collect();
179        write!(f, "{}", errors.join(", "))
180    }
181}
182
183impl<Key> std::error::Error for ValidationErrors<Key> where Key: std::fmt::Debug {}
184
185impl<Key> From<ValidationError<Key>> for ValidationErrors<Key>
186where
187    Key: Clone + PartialEq,
188{
189    fn from(err: ValidationError<Key>) -> Self {
190        ValidationErrors::new(vec![err])
191    }
192}