use crate::error::ValidationError;
use crate::value::Value;
pub trait ValidatorFn: Send + Sync {
fn validate(&self, value: &Value, field: &str) -> Result<(), ValidationError>;
}
pub struct RequiredValidator;
impl ValidatorFn for RequiredValidator {
fn validate(&self, value: &Value, field: &str) -> Result<(), ValidationError> {
if value.is_null() {
Err(ValidationError::new(field, "field is required", "required"))
} else {
Ok(())
}
}
}
pub struct OptionalValidator;
impl ValidatorFn for OptionalValidator {
fn validate(&self, _value: &Value, _field: &str) -> Result<(), ValidationError> {
Ok(())
}
}
pub struct NullableValidator;
impl ValidatorFn for NullableValidator {
fn validate(&self, _value: &Value, _field: &str) -> Result<(), ValidationError> {
Ok(())
}
}
pub struct StrictTypeValidator {
pub expected: &'static str,
}
impl ValidatorFn for StrictTypeValidator {
fn validate(&self, value: &Value, field: &str) -> Result<(), ValidationError> {
if value.type_name() != self.expected {
Err(ValidationError::new(
field,
format!(
"strict mode: expected type '{}', got '{}'",
self.expected,
value.type_name()
),
"strict_type",
))
} else {
Ok(())
}
}
}
type CustomFn = Box<dyn Fn(&Value, &str) -> Result<(), ValidationError> + Send + Sync>;
pub struct CustomValidator {
pub func: CustomFn,
}
impl ValidatorFn for CustomValidator {
fn validate(&self, value: &Value, field: &str) -> Result<(), ValidationError> {
(self.func)(value, field)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_required_fails_on_null() {
let r = RequiredValidator.validate(&Value::Null, "x");
assert!(r.is_err());
}
#[test]
fn test_required_passes_on_value() {
let r = RequiredValidator.validate(&Value::Int(1), "x");
assert!(r.is_ok());
}
#[test]
fn test_optional_always_passes() {
assert!(OptionalValidator.validate(&Value::Null, "x").is_ok());
assert!(OptionalValidator.validate(&Value::Int(99), "x").is_ok());
}
#[test]
fn test_strict_type_fails() {
let v = StrictTypeValidator { expected: "int" };
assert!(v.validate(&Value::String("3".into()), "n").is_err());
}
#[test]
fn test_strict_type_passes() {
let v = StrictTypeValidator { expected: "bool" };
assert!(v.validate(&Value::Bool(true), "flag").is_ok());
}
#[test]
fn test_custom_validator() {
let cv = CustomValidator {
func: Box::new(|val, field| {
if val.as_int() == Some(42) {
Ok(())
} else {
Err(ValidationError::new(field, "must be 42", "must_be_42"))
}
}),
};
assert!(cv.validate(&Value::Int(42), "n").is_ok());
assert!(cv.validate(&Value::Int(1), "n").is_err());
}
}