use super::{ValidationError, ValidationResult, Validator};
pub struct AndValidator<T: ?Sized> {
validators: Vec<Box<dyn Validator<T>>>,
}
impl<T: ?Sized> AndValidator<T> {
pub fn new(validators: Vec<Box<dyn Validator<T>>>) -> Self {
Self { validators }
}
pub fn with_validator(mut self, validator: Box<dyn Validator<T>>) -> Self {
self.validators.push(validator);
self
}
}
impl<T: ?Sized> Validator<T> for AndValidator<T> {
fn validate(&self, value: &T) -> ValidationResult<()> {
for validator in &self.validators {
validator.validate(value)?;
}
Ok(())
}
}
pub struct OrValidator<T: ?Sized> {
validators: Vec<Box<dyn Validator<T>>>,
collect_errors: bool,
}
impl<T: ?Sized> OrValidator<T> {
pub fn new(validators: Vec<Box<dyn Validator<T>>>) -> Self {
Self {
validators,
collect_errors: false,
}
}
pub fn with_error_collection(mut self, collect: bool) -> Self {
self.collect_errors = collect;
self
}
}
impl<T: ?Sized> Validator<T> for OrValidator<T> {
fn validate(&self, value: &T) -> ValidationResult<()> {
if self.validators.is_empty() {
return Ok(());
}
let mut errors = Vec::new();
for validator in &self.validators {
match validator.validate(value) {
Ok(()) => return Ok(()), Err(e) if self.collect_errors => errors.push(e.to_string()),
Err(_) => {} }
}
if self.collect_errors && !errors.is_empty() {
Err(ValidationError::AllValidatorsFailed {
errors: errors.join("; "),
})
} else {
Err(ValidationError::CompositeValidationFailed(
"All validators failed".to_string(),
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::validators::{MaxLengthValidator, MinLengthValidator};
#[test]
fn test_and_validator_all_pass() {
let validator = AndValidator::new(vec![
Box::new(MinLengthValidator::new(3)),
Box::new(MaxLengthValidator::new(10)),
]);
assert!(validator.validate("hello").is_ok());
assert!(validator.validate("test").is_ok());
}
#[test]
fn test_and_validator_first_fails() {
let validator = AndValidator::new(vec![
Box::new(MinLengthValidator::new(5)),
Box::new(MaxLengthValidator::new(10)),
]);
assert!(validator.validate("hi").is_err()); }
#[test]
fn test_and_validator_second_fails() {
let validator = AndValidator::new(vec![
Box::new(MinLengthValidator::new(3)),
Box::new(MaxLengthValidator::new(5)),
]);
assert!(validator.validate("toolong").is_err()); }
#[test]
fn test_and_validator_all_fail() {
let validator = AndValidator::new(vec![
Box::new(MinLengthValidator::new(10)),
Box::new(MaxLengthValidator::new(5)),
]);
assert!(validator.validate("test").is_err());
}
#[test]
fn test_and_validator_empty() {
let validator: AndValidator<str> = AndValidator::new(vec![]);
assert!(validator.validate("anything").is_ok());
}
#[test]
fn test_and_validator_add_method() {
let validator = AndValidator::new(vec![Box::new(MinLengthValidator::new(3))])
.with_validator(Box::new(MaxLengthValidator::new(10)));
assert!(validator.validate("hello").is_ok());
assert!(validator.validate("hi").is_err());
assert!(validator.validate("verylongtext").is_err());
}
#[test]
fn test_or_validator_first_passes() {
let validator = OrValidator::new(vec![
Box::new(MinLengthValidator::new(3)),
Box::new(MaxLengthValidator::new(10)),
]);
assert!(validator.validate("hello").is_ok()); }
#[test]
fn test_or_validator_second_passes() {
let validator = OrValidator::new(vec![
Box::new(MinLengthValidator::new(10)), Box::new(MaxLengthValidator::new(10)), ]);
assert!(validator.validate("short").is_ok()); }
#[test]
fn test_or_validator_all_fail() {
let validator = OrValidator::new(vec![
Box::new(MinLengthValidator::new(10)),
Box::new(MinLengthValidator::new(20)),
]);
assert!(validator.validate("short").is_err());
}
#[test]
fn test_or_validator_empty() {
let validator: OrValidator<str> = OrValidator::new(vec![]);
assert!(validator.validate("anything").is_ok());
}
#[test]
fn test_or_validator_with_error_collection() {
let validator = OrValidator::new(vec![
Box::new(MinLengthValidator::new(10)),
Box::new(MinLengthValidator::new(20)),
])
.with_error_collection(true);
match validator.validate("short") {
Err(ValidationError::AllValidatorsFailed { errors }) => {
assert!(errors.contains("minimum: 10"));
assert!(errors.contains("minimum: 20"));
}
_ => panic!("Expected AllValidatorsFailed error"),
}
}
#[test]
fn test_or_validator_without_error_collection() {
let validator = OrValidator::new(vec![
Box::new(MinLengthValidator::new(10)),
Box::new(MinLengthValidator::new(20)),
]);
match validator.validate("short") {
Err(ValidationError::CompositeValidationFailed(_)) => {}
_ => panic!("Expected CompositeValidationFailed error"),
}
}
#[test]
fn test_nested_and_in_or() {
let and_validator = AndValidator::new(vec![
Box::new(MinLengthValidator::new(3)),
Box::new(MaxLengthValidator::new(10)),
]);
let or_validator = OrValidator::new(vec![
Box::new(and_validator),
Box::new(MinLengthValidator::new(20)),
]);
assert!(or_validator.validate("hello").is_ok()); assert!(
or_validator
.validate("verylongusernameexceeds20chars")
.is_ok()
); assert!(or_validator.validate("hi").is_err()); }
#[test]
fn test_nested_or_in_and() {
let or_validator = OrValidator::new(vec![
Box::new(MinLengthValidator::new(3)),
Box::new(MinLengthValidator::new(5)),
]);
let and_validator = AndValidator::new(vec![
Box::new(or_validator),
Box::new(MaxLengthValidator::new(10)),
]);
assert!(and_validator.validate("hello").is_ok()); assert!(and_validator.validate("verylongtext").is_err()); assert!(and_validator.validate("hi").is_err()); }
}