use crate::{Validation, ValidationErrors, ValidatorFn};
use std::fmt::Debug;
#[cfg(feature = "async")]
use crate::AsyncValidatorFn;
#[cfg(feature = "async")]
use futures::future::join_all;
#[derive(Clone, Debug)]
pub struct Validator<Value, Key> {
pub validations: Vec<ValidatorFn<Value, Key>>,
}
impl<Value, Key> PartialEq for Validator<Value, Key> {
fn eq(&self, other: &Self) -> bool {
if self.validations.len() == other.validations.len() {
let mut all_validations_same = true;
for (i, this_validation) in self.validations.iter().enumerate() {
let other_validation = other.validations.get(i).unwrap();
all_validations_same &= this_validation == other_validation;
}
all_validations_same
} else {
false
}
}
}
impl<Value, Key> Validator<Value, Key> {
pub fn new() -> Self {
Self {
validations: Vec::new(),
}
}
pub fn validation<F: Into<ValidatorFn<Value, Key>> + 'static>(
mut self,
validator_fn: F,
) -> Self {
self.validations.push(validator_fn.into());
self
}
}
impl<Value, Key> Validation<Value, Key> for Validator<Value, Key>
where
Key: PartialEq + Clone,
{
fn validate_value(&self, value: &Value, key: &Key) -> Result<(), ValidationErrors<Key>> {
let mut errors = ValidationErrors::default();
for validation in &self.validations {
if let Err(new_errors) = validation.validate_value(value, key) {
errors.extend(new_errors)
}
}
if !errors.is_empty() {
Err(errors)
} else {
Ok(())
}
}
}
impl<Value, Key> Default for Validator<Value, Key> {
fn default() -> Self {
Validator::new()
}
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[derive(Clone, PartialEq, Debug)]
pub struct AsyncValidator<Value, Key> {
pub validations: Vec<AsyncValidatorFn<Value, Key>>,
}
#[cfg(feature = "async")]
impl<Value, Key> AsyncValidator<Value, Key>
where
Key: Clone + PartialEq,
Value: Clone + PartialEq,
{
pub fn new() -> Self {
Self {
validations: Vec::new(),
}
}
pub fn validation<F: Into<AsyncValidatorFn<Value, Key>> + 'static>(
mut self,
async_validator_fn: F,
) -> Self {
self.validations.push(async_validator_fn.into());
self
}
pub async fn validate_value(
&self,
value: &Value,
key: &Key,
) -> Result<(), ValidationErrors<Key>> {
let mut errors = ValidationErrors::default();
let futures = self
.validations
.iter()
.map(|async_validator_fn| async_validator_fn.validate_value(value, key))
.collect::<Vec<_>>();
let results: Vec<Result<(), ValidationErrors<Key>>> = join_all(futures).await;
for result in results {
if let Err(new_errors) = result {
errors.extend(new_errors)
}
}
if !errors.is_empty() {
Err(errors)
} else {
Ok(())
}
}
}
#[cfg(feature = "async")]
impl<Value, Key> Default for AsyncValidator<Value, Key>
where
Key: Clone + PartialEq,
Value: Clone + PartialEq,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "async")]
impl<Value, Key> From<Validator<Value, Key>> for AsyncValidator<Value, Key>
where
Value: Clone + PartialEq + 'static,
Key: Clone + PartialEq + 'static,
{
fn from(validator: Validator<Value, Key>) -> Self {
let mut async_validator: AsyncValidator<Value, Key> = AsyncValidator::new();
for validator_fn in validator.validations {
async_validator = async_validator.validation(validator_fn);
}
async_validator
}
}
#[cfg(test)]
mod test {
#[cfg(feature = "async")]
mod async_tests {
use super::super::{AsyncValidator, Validator};
use crate::ValidationError;
use futures::executor::block_on;
#[test]
fn async_validator_from_validator() {
let v: Validator<i32, String> = Validator::new()
.validation(|value: &i32, key: &String| {
if value < &0 {
let value_clone = *value;
Err(ValidationError::new(key.clone(), "NOT_LESS_THAN_0")
.with_message(move |key| {
format!(
"The value of {} ({}) cannot be less than 0",
key, value_clone
)
})
.into())
} else {
Ok(())
}
})
.validation(|value: &i32, key: &String| {
if value > &10 {
let value_clone = *value;
Err(ValidationError::new(key.clone(), "NOT_GREATER_THAN_10")
.with_message(move |key| {
format!(
"The value of {} ({}) cannot be greater than 10",
key, value_clone
)
})
.into())
} else {
Ok(())
}
});
let av: AsyncValidator<i32, String> = v.into();
let key = "field1".to_string();
{
let errors = block_on(av.validate_value(&11, &key)).unwrap_err();
assert_eq!(1, errors.len());
let error = errors.errors.get(0).unwrap();
assert_eq!("NOT_GREATER_THAN_10", error.type_id);
}
assert!(block_on(av.validate_value(&5, &key)).is_ok());
{
let errors = block_on(av.validate_value(&-1, &key)).unwrap_err();
assert_eq!(1, errors.len());
let error = errors.errors.get(0).unwrap();
assert_eq!("NOT_LESS_THAN_0", error.type_id);
}
}
}
}