use core::marker::PhantomData;
use crate::{ValidationError, Validator};
pub struct And<A, B>(PhantomData<fn() -> (A, B)>);
impl<T, A, B, E> Validator<T> for And<A, B>
where
T: ?Sized,
A: Validator<T, Error = E>,
B: Validator<T, Error = E>,
{
type Error = E;
fn validate(value: &T) -> Result<(), Self::Error> {
A::validate(value)?;
B::validate(value)
}
}
pub struct Or<A, B>(PhantomData<fn() -> (A, B)>);
impl<T, A, B, E> Validator<T> for Or<A, B>
where
T: ?Sized,
A: Validator<T, Error = E>,
B: Validator<T, Error = E>,
{
type Error = E;
fn validate(value: &T) -> Result<(), Self::Error> {
match A::validate(value) {
Ok(()) => Ok(()),
Err(_) => B::validate(value),
}
}
}
pub struct Not<A>(PhantomData<fn() -> A>);
impl<T, A> Validator<T> for Not<A>
where
T: ?Sized,
A: Validator<T>,
{
type Error = ValidationError;
fn validate(value: &T) -> Result<(), Self::Error> {
match A::validate(value) {
Ok(()) => Err(ValidationError::new(
"not",
"value matched a rule it was required to violate",
)),
Err(_) => Ok(()),
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
use crate::rules::{Ascii, MaxLen, NonEmpty};
#[test]
fn and_requires_both() {
type R = And<NonEmpty, MaxLen<4>>;
assert!(R::validate("abc").is_ok());
assert_eq!(R::validate("").unwrap_err().code(), "non_empty");
assert_eq!(R::validate("toolong").unwrap_err().code(), "max_len");
}
#[test]
fn or_accepts_either() {
type R = Or<NonEmpty, MaxLen<0>>;
assert!(R::validate("x").is_ok()); assert!(R::validate("").is_ok()); }
#[test]
fn or_returns_second_error_when_both_fail() {
use crate::rules::MinLen;
type R = Or<MinLen<5>, MinLen<10>>;
assert_eq!(R::validate("abc").unwrap_err().code(), "min_len");
}
#[test]
fn not_inverts() {
type R = Not<Ascii>;
assert!(R::validate("café").is_ok());
assert_eq!(R::validate("ascii").unwrap_err().code(), "not");
}
}