use super::{ValidationResult, Validator};
pub struct ConditionalValidator<T: ?Sized, C>
where
C: Fn(&T) -> bool,
{
condition: C,
validator: Box<dyn Validator<T>>,
validate_when_true: bool,
}
impl<T: ?Sized, C> ConditionalValidator<T, C>
where
C: Fn(&T) -> bool,
{
pub fn when(condition: C, validator: Box<dyn Validator<T>>) -> Self {
Self {
condition,
validator,
validate_when_true: true,
}
}
pub fn unless(condition: C, validator: Box<dyn Validator<T>>) -> Self {
Self {
condition,
validator,
validate_when_true: false,
}
}
}
impl<T: ?Sized, C> Validator<T> for ConditionalValidator<T, C>
where
C: Fn(&T) -> bool,
{
fn validate(&self, value: &T) -> ValidationResult<()> {
let condition_result = (self.condition)(value);
if condition_result == self.validate_when_true {
self.validator.validate(value)
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::validators::{EmailValidator, MaxLengthValidator, MinLengthValidator};
#[test]
fn test_when_condition_true_and_valid() {
let validator = ConditionalValidator::when(
|value: &str| value.starts_with("admin_"),
Box::new(MinLengthValidator::new(10)),
);
assert!(validator.validate("admin_john_doe").is_ok());
}
#[test]
fn test_when_condition_true_and_invalid() {
let validator = ConditionalValidator::when(
|value: &str| value.starts_with("admin_"),
Box::new(MinLengthValidator::new(10)),
);
assert!(validator.validate("admin_joe").is_err());
}
#[test]
fn test_when_condition_false() {
let validator = ConditionalValidator::when(
|value: &str| value.starts_with("admin_"),
Box::new(MinLengthValidator::new(10)),
);
assert!(validator.validate("joe").is_ok());
assert!(validator.validate("regular_user").is_ok());
}
#[test]
fn test_unless_condition_false_and_valid() {
let validator = ConditionalValidator::unless(
|value: &str| value.starts_with("system:"),
Box::new(EmailValidator::new()),
);
assert!(validator.validate("user@example.com").is_ok());
}
#[test]
fn test_unless_condition_false_and_invalid() {
let validator = ConditionalValidator::unless(
|value: &str| value.starts_with("system:"),
Box::new(EmailValidator::new()),
);
assert!(validator.validate("invalid").is_err());
}
#[test]
fn test_unless_condition_true() {
let validator = ConditionalValidator::unless(
|value: &str| value.starts_with("system:"),
Box::new(EmailValidator::new()),
);
assert!(validator.validate("system:admin").is_ok());
assert!(validator.validate("system:root").is_ok());
}
#[test]
fn test_complex_condition() {
let validator = ConditionalValidator::when(
|value: &str| value.len() > 5 && value.contains("@"),
Box::new(EmailValidator::new()),
);
assert!(validator.validate("user@example.com").is_ok());
assert!(validator.validate("invalid@").is_err());
assert!(validator.validate("short").is_ok());
assert!(validator.validate("longusername").is_ok());
}
#[test]
fn test_when_with_numeric_validator() {
use crate::validators::RangeValidator;
let validator = ConditionalValidator::when(
|value: &i32| *value >= 0,
Box::new(RangeValidator::new(0, 100)),
);
assert!(validator.validate(&50).is_ok());
assert!(validator.validate(&150).is_err());
assert!(validator.validate(&-50).is_ok());
}
#[test]
fn test_nested_conditional() {
let inner_validator = ConditionalValidator::when(
|value: &str| value.len() > 10,
Box::new(MaxLengthValidator::new(20)),
);
let outer_validator = ConditionalValidator::unless(
|value: &str| value.starts_with("skip:"),
Box::new(inner_validator),
);
assert!(outer_validator.validate("test_username").is_ok()); assert!(
outer_validator
.validate("test_very_long_username_exceeds")
.is_err()
);
assert!(outer_validator.validate("short").is_ok());
assert!(outer_validator.validate("skip:anything").is_ok());
assert!(
outer_validator
.validate("skip:very_long_username_exceeds_limit")
.is_ok()
);
}
#[test]
fn test_always_true_condition() {
let validator =
ConditionalValidator::when(|_: &str| true, Box::new(MinLengthValidator::new(5)));
assert!(validator.validate("hello").is_ok());
assert!(validator.validate("hi").is_err());
}
#[test]
fn test_always_false_condition() {
let validator =
ConditionalValidator::when(|_: &str| false, Box::new(MinLengthValidator::new(5)));
assert!(validator.validate("hello").is_ok());
assert!(validator.validate("hi").is_ok());
}
#[test]
fn test_password_strength_for_admin() {
let validator = ConditionalValidator::when(
|username: &str| username.starts_with("admin_") || username.starts_with("root_"),
Box::new(MinLengthValidator::new(12)),
);
assert!(validator.validate("admin_12345678901").is_ok());
assert!(validator.validate("admin_short").is_err());
assert!(validator.validate("user_short").is_ok());
}
#[test]
fn test_email_validation_for_external_users() {
let validator = ConditionalValidator::unless(
|email: &str| email.ends_with("@internal.company.com"),
Box::new(EmailValidator::new()),
);
assert!(validator.validate("user@internal.company.com").is_ok());
assert!(validator.validate("user@example.com").is_ok());
assert!(validator.validate("invalid@").is_err());
}
}