form_validation/
validator.rs

1use crate::{Validation, ValidationErrors, ValidatorFn};
2use std::fmt::Debug;
3
4#[cfg(feature = "async")]
5use crate::AsyncValidatorFn;
6
7#[cfg(feature = "async")]
8use futures::future::join_all;
9
10/// Validates a particular type of value, can contain many validation
11/// functions. Generally used with a single key for all contained
12/// validation functions.
13///
14/// ## Example
15/// ```
16/// use form_validation::{Validation, ValidationError, Validator};
17///
18/// let v: Validator<i32, String> = Validator::new()
19/// .validation(|value: &i32, key: &String| {
20///     if value < &0 {
21///         let value_clone = *value;
22///         Err(ValidationError::new(key.clone(), "NOT_LESS_THAN_0")
23///             .with_message(move |key| {
24///                 format!(
25///                     "The value of {} ({}) cannot be less than 0",
26///                     key, value_clone
27///                 )
28///         }).into()) // convert into ValidationErrors
29///     } else {
30///         Ok(())
31///     }
32/// })
33/// .validation(|value: &i32, key: &String| {
34///     if value > &10 {
35///         let value_clone = *value;
36///         Err(ValidationError::new(key.clone(), "NOT_GREATER_THAN_10")
37///             .with_message(move |key| {
38///                 format!(
39///                     "The value of {} ({}) cannot be greater than 10",
40///                     key, value_clone
41///                 )
42///         }).into())
43///     } else {
44///         Ok(())
45///     }
46/// });
47///
48/// let key = "field1".to_string();
49///
50/// {
51///     let errors = v.validate_value(&11, &key).unwrap_err();
52///     assert_eq!(1, errors.len());
53///     let error = errors.errors.get(0).unwrap();
54///     assert_eq!("NOT_GREATER_THAN_10", error.type_id);
55/// }
56
57/// assert!(v.validate_value(&5, &key).is_ok());
58///
59/// {
60///     let errors = v.validate_value(&-1, &key).unwrap_err();
61///     assert_eq!(1, errors.len());
62///     let error = errors.errors.get(0).unwrap();
63///     assert_eq!("NOT_LESS_THAN_0", error.type_id);
64/// }
65/// ```
66#[derive(Clone, Debug)]
67pub struct Validator<Value, Key> {
68    pub validations: Vec<ValidatorFn<Value, Key>>,
69}
70
71impl<Value, Key> PartialEq for Validator<Value, Key> {
72    fn eq(&self, other: &Self) -> bool {
73        if self.validations.len() == other.validations.len() {
74            let mut all_validations_same = true;
75
76            for (i, this_validation) in self.validations.iter().enumerate() {
77                let other_validation = other.validations.get(i).unwrap();
78
79                all_validations_same &= this_validation == other_validation;
80            }
81
82            all_validations_same
83        } else {
84            false
85        }
86    }
87}
88
89impl<Value, Key> Validator<Value, Key> {
90    /// Create a new `Validator`.
91    pub fn new() -> Self {
92        Self {
93            validations: Vec::new(),
94        }
95    }
96
97    /// A factory method to add a validation function to this validator.
98    pub fn validation<F: Into<ValidatorFn<Value, Key>> + 'static>(
99        mut self,
100        validator_fn: F,
101    ) -> Self {
102        self.validations.push(validator_fn.into());
103        self
104    }
105}
106
107impl<Value, Key> Validation<Value, Key> for Validator<Value, Key>
108where
109    Key: PartialEq + Clone,
110{
111    fn validate_value(&self, value: &Value, key: &Key) -> Result<(), ValidationErrors<Key>> {
112        let mut errors = ValidationErrors::default();
113
114        for validation in &self.validations {
115            if let Err(new_errors) = validation.validate_value(value, key) {
116                errors.extend(new_errors)
117            }
118        }
119
120        if !errors.is_empty() {
121            Err(errors)
122        } else {
123            Ok(())
124        }
125    }
126}
127
128impl<Value, Key> Default for Validator<Value, Key> {
129    fn default() -> Self {
130        Validator::new()
131    }
132}
133
134/// Validates a particular type of value asynchronously, can contain
135/// many validation functions. Generally used with a single key for
136/// all contained validation functions.
137///
138/// See [Validator] for the synchronous version.
139///
140/// ```
141/// use form_validation::{AsyncValidator, ValidationError, AsyncValidatorFn, ValidatorFn};
142/// use futures::executor::block_on;
143///
144/// let v: AsyncValidator<i32, String> = AsyncValidator::new()
145///     .validation(AsyncValidatorFn::new(|value: &i32, key: &String| {
146///         let value = *value;
147///         let key = key.clone();
148///         Box::pin(async move {
149///             if value < 0 {
150///                 Err(ValidationError::new(key.clone(), "NOT_LESS_THAN_0")
151///                     .with_message(move |key| {
152///                         format!("The value of {} ({}) cannot be less than 0", key, value)
153///                     })
154///                     .into()) // convert into ValidationErrors
155///             } else {
156///                 Ok(())
157///             }
158///         })
159///     }))
160///     // also supports compatibility with the synchronous ValidatorFn
161///     .validation(ValidatorFn::new(|value: &i32, key: &String| {
162///         if value > &10 {
163///             let value_clone = *value;
164///             Err(ValidationError::new(key.clone(), "NOT_GREATER_THAN_10")
165///                 .with_message(move |key| {
166///                     format!(
167///                         "The value of {} ({}) cannot be greater than 10",
168///                         key, value_clone
169///                     )
170///                 })
171///                 .into()) // convert into ValidationErrors
172///         } else {
173///             Ok(())
174///         }
175///     }));
176/// let key = "field1".to_string();
177/// {
178///     let errors = block_on(v.validate_value(&11, &key)).unwrap_err();
179///     assert_eq!(1, errors.len());
180///     let error = errors.errors.get(0).unwrap();
181///     assert_eq!("NOT_GREATER_THAN_10", error.type_id);
182/// }
183///
184/// assert!(block_on(v.validate_value(&5, &key)).is_ok());
185///
186/// {
187///     let errors = block_on(v.validate_value(&-1, &key)).unwrap_err();
188///     assert_eq!(1, errors.len());
189///     let error = errors.errors.get(0).unwrap();
190///     assert_eq!("NOT_LESS_THAN_0", error.type_id);
191/// }
192/// ```
193#[cfg(feature = "async")]
194#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
195#[derive(Clone, PartialEq, Debug)]
196pub struct AsyncValidator<Value, Key> {
197    pub validations: Vec<AsyncValidatorFn<Value, Key>>,
198}
199
200#[cfg(feature = "async")]
201impl<Value, Key> AsyncValidator<Value, Key>
202where
203    Key: Clone + PartialEq,
204    Value: Clone + PartialEq,
205{
206    /// Create a new `Validator`.
207    pub fn new() -> Self {
208        Self {
209            validations: Vec::new(),
210        }
211    }
212
213    /// A factory method to add a validation function to this validator.
214    pub fn validation<F: Into<AsyncValidatorFn<Value, Key>> + 'static>(
215        mut self,
216        async_validator_fn: F,
217    ) -> Self {
218        self.validations.push(async_validator_fn.into());
219        self
220    }
221
222    pub async fn validate_value(
223        &self,
224        value: &Value,
225        key: &Key,
226    ) -> Result<(), ValidationErrors<Key>> {
227        let mut errors = ValidationErrors::default();
228
229        let futures = self
230            .validations
231            .iter()
232            .map(|async_validator_fn| async_validator_fn.validate_value(value, key))
233            .collect::<Vec<_>>();
234
235        // Execute all the futures concurrently
236        let results: Vec<Result<(), ValidationErrors<Key>>> = join_all(futures).await;
237
238        for result in results {
239            if let Err(new_errors) = result {
240                errors.extend(new_errors)
241            }
242        }
243
244        if !errors.is_empty() {
245            Err(errors)
246        } else {
247            Ok(())
248        }
249    }
250}
251
252#[cfg(feature = "async")]
253impl<Value, Key> Default for AsyncValidator<Value, Key>
254where
255    Key: Clone + PartialEq,
256    Value: Clone + PartialEq,
257{
258    fn default() -> Self {
259        Self::new()
260    }
261}
262
263#[cfg(feature = "async")]
264impl<Value, Key> From<Validator<Value, Key>> for AsyncValidator<Value, Key>
265where
266    Value: Clone + PartialEq + 'static,
267    Key: Clone + PartialEq + 'static,
268{
269    fn from(validator: Validator<Value, Key>) -> Self {
270        let mut async_validator: AsyncValidator<Value, Key> = AsyncValidator::new();
271
272        for validator_fn in validator.validations {
273            async_validator = async_validator.validation(validator_fn);
274        }
275
276        async_validator
277    }
278}
279
280#[cfg(test)]
281mod test {
282    #[cfg(feature = "async")]
283    mod async_tests {
284        use super::super::{AsyncValidator, Validator};
285        use crate::ValidationError;
286        use futures::executor::block_on;
287
288        /// Unit test for the `From<Validator> for AsyncValidator` implmentation
289        #[test]
290        fn async_validator_from_validator() {
291            let v: Validator<i32, String> = Validator::new()
292                .validation(|value: &i32, key: &String| {
293                    if value < &0 {
294                        let value_clone = *value;
295                        Err(ValidationError::new(key.clone(), "NOT_LESS_THAN_0")
296                            .with_message(move |key| {
297                                format!(
298                                    "The value of {} ({}) cannot be less than 0",
299                                    key, value_clone
300                                )
301                            })
302                            .into())
303                    } else {
304                        Ok(())
305                    }
306                })
307                .validation(|value: &i32, key: &String| {
308                    if value > &10 {
309                        let value_clone = *value;
310                        Err(ValidationError::new(key.clone(), "NOT_GREATER_THAN_10")
311                            .with_message(move |key| {
312                                format!(
313                                    "The value of {} ({}) cannot be greater than 10",
314                                    key, value_clone
315                                )
316                            })
317                            .into())
318                    } else {
319                        Ok(())
320                    }
321                });
322
323            // perform the conversion
324            let av: AsyncValidator<i32, String> = v.into();
325
326            let key = "field1".to_string();
327            {
328                let errors = block_on(av.validate_value(&11, &key)).unwrap_err();
329                assert_eq!(1, errors.len());
330                let error = errors.errors.get(0).unwrap();
331                assert_eq!("NOT_GREATER_THAN_10", error.type_id);
332            }
333            assert!(block_on(av.validate_value(&5, &key)).is_ok());
334            {
335                let errors = block_on(av.validate_value(&-1, &key)).unwrap_err();
336                assert_eq!(1, errors.len());
337                let error = errors.errors.get(0).unwrap();
338                assert_eq!("NOT_LESS_THAN_0", error.type_id);
339            }
340        }
341    }
342}