pub mod common_validators;
pub mod number_validators;
pub mod string_validators;
pub use common_validators::{
CustomValidator, NullableValidator, OptionalValidator, RequiredValidator, StrictTypeValidator,
ValidatorFn,
};
pub use number_validators::{
MaxFloatValidator, MaxIntValidator, MinFloatValidator, MinIntValidator, NegativeValidator,
NonZeroValidator, PositiveValidator, RangeValidator,
};
pub use string_validators::{
AlphanumericValidator, EmailValidator, MaxLengthValidator, MinLengthValidator,
NonEmptyValidator, RegexValidator, UrlValidator, is_valid_email,
};
use crate::error::ValidationErrors;
use crate::value::Value;
#[derive(Default)]
pub struct ValidatorChain {
validators: Vec<Box<dyn ValidatorFn>>,
}
impl ValidatorChain {
pub fn new() -> Self {
Self::default()
}
pub fn push_validator(mut self, v: impl ValidatorFn + 'static) -> Self {
self.validators.push(Box::new(v));
self
}
pub fn validate(&self, value: &Value, field: &str) -> Result<(), ValidationErrors> {
let mut errs = ValidationErrors::new();
for v in &self.validators {
if let Err(e) = v.validate(value, field) {
errs.push(e);
}
}
if errs.is_empty() { Ok(()) } else { Err(errs) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chain_collects_all_errors() {
let chain = ValidatorChain::new()
.push_validator(MinLengthValidator(10))
.push_validator(EmailValidator);
let result = chain.validate(&Value::String("hi".into()), "email");
assert!(result.is_err());
let errs = result.unwrap_err();
assert_eq!(errs.errors.len(), 2);
}
#[test]
fn test_chain_passes_when_all_valid() {
let chain = ValidatorChain::new()
.push_validator(MinLengthValidator(3))
.push_validator(MaxLengthValidator(20));
assert!(
chain
.validate(&Value::String("hello".into()), "name")
.is_ok()
);
}
#[test]
fn test_chain_empty_always_passes() {
let chain = ValidatorChain::new();
assert!(chain.validate(&Value::Null, "x").is_ok());
}
#[test]
fn test_required_validator_in_chain() {
let chain = ValidatorChain::new().push_validator(RequiredValidator);
assert!(chain.validate(&Value::Null, "x").is_err());
assert!(chain.validate(&Value::Int(1), "x").is_ok());
}
}