use crate::either::Either;
use crate::nonempty::NonEmptyVec;
use crate::Semigroup;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Validation<T, E> {
Success(T),
Failure(E),
}
impl<T, E> Validation<T, E> {
#[inline]
pub fn success(value: T) -> Self {
Validation::Success(value)
}
#[inline]
pub fn failure(error: E) -> Self {
Validation::Failure(error)
}
#[inline]
pub fn from_result(result: Result<T, E>) -> Self {
match result {
Ok(value) => Validation::Success(value),
Err(error) => Validation::Failure(error),
}
}
#[inline]
pub fn into_result(self) -> Result<T, E> {
match self {
Validation::Success(value) => Ok(value),
Validation::Failure(error) => Err(error),
}
}
#[inline]
pub fn is_success(&self) -> bool {
matches!(self, Validation::Success(_))
}
#[inline]
pub fn is_failure(&self) -> bool {
matches!(self, Validation::Failure(_))
}
#[inline]
pub fn map<U, F>(self, f: F) -> Validation<U, E>
where
F: FnOnce(T) -> U,
{
match self {
Validation::Success(value) => Validation::Success(f(value)),
Validation::Failure(error) => Validation::Failure(error),
}
}
#[inline]
pub fn map_err<E2, F>(self, f: F) -> Validation<T, E2>
where
F: FnOnce(E) -> E2,
{
match self {
Validation::Success(value) => Validation::Success(value),
Validation::Failure(error) => Validation::Failure(f(error)),
}
}
#[inline]
pub fn into_either(self) -> Either<E, T> {
match self {
Validation::Success(value) => Either::Right(value),
Validation::Failure(error) => Either::Left(error),
}
}
pub fn ensure<P>(self, predicate: P, error: E) -> Validation<T, E>
where
P: crate::predicate::Predicate<T>,
{
match self {
Validation::Success(value) if predicate.check(&value) => Validation::Success(value),
Validation::Success(_) => Validation::Failure(error),
Validation::Failure(e) => Validation::Failure(e),
}
}
pub fn ensure_fn<F>(self, predicate: F, error: E) -> Validation<T, E>
where
F: FnOnce(&T) -> bool,
{
match self {
Validation::Success(value) if predicate(&value) => Validation::Success(value),
Validation::Success(_) => Validation::Failure(error),
Validation::Failure(e) => Validation::Failure(e),
}
}
pub fn ensure_with<P, F>(self, predicate: P, error_fn: F) -> Validation<T, E>
where
P: crate::predicate::Predicate<T>,
F: FnOnce(&T) -> E,
{
match self {
Validation::Success(value) => {
if predicate.check(&value) {
Validation::Success(value)
} else {
Validation::Failure(error_fn(&value))
}
}
Validation::Failure(e) => Validation::Failure(e),
}
}
pub fn ensure_fn_with<P, F>(self, predicate: P, error_fn: F) -> Validation<T, E>
where
P: FnOnce(&T) -> bool,
F: FnOnce(&T) -> E,
{
match self {
Validation::Success(value) => {
if predicate(&value) {
Validation::Success(value)
} else {
Validation::Failure(error_fn(&value))
}
}
Validation::Failure(e) => Validation::Failure(e),
}
}
pub fn filter_or<F>(self, predicate: F, error: E) -> Validation<T, E>
where
F: FnOnce(&T) -> bool,
{
self.ensure_fn(predicate, error)
}
pub fn unless<F>(self, predicate: F, error: E) -> Validation<T, E>
where
F: FnOnce(&T) -> bool,
{
match self {
Validation::Success(value) if !predicate(&value) => Validation::Success(value),
Validation::Success(_) => Validation::Failure(error),
Validation::Failure(e) => Validation::Failure(e),
}
}
#[inline]
pub fn bimap<U, E2, F, G>(self, f: F, g: G) -> Validation<U, E2>
where
F: FnOnce(E) -> E2,
G: FnOnce(T) -> U,
{
match self {
Validation::Success(value) => Validation::Success(g(value)),
Validation::Failure(error) => Validation::Failure(f(error)),
}
}
#[inline]
pub fn bimap_success_first<U, E2, F, G>(self, f: F, g: G) -> Validation<U, E2>
where
F: FnOnce(T) -> U,
G: FnOnce(E) -> E2,
{
self.bimap(g, f)
}
#[inline]
pub fn bimap_ref<U, E2, F, G>(&self, f: F, g: G) -> Validation<U, E2>
where
F: FnOnce(&E) -> E2,
G: FnOnce(&T) -> U,
{
match self {
Validation::Success(value) => Validation::Success(g(value)),
Validation::Failure(error) => Validation::Failure(f(error)),
}
}
#[inline]
pub fn fold<R, F, G>(self, on_failure: F, on_success: G) -> R
where
F: FnOnce(E) -> R,
G: FnOnce(T) -> R,
{
match self {
Validation::Success(value) => on_success(value),
Validation::Failure(error) => on_failure(error),
}
}
#[inline]
pub fn fold_success_first<R, F, G>(self, on_success: F, on_failure: G) -> R
where
F: FnOnce(T) -> R,
G: FnOnce(E) -> R,
{
self.fold(on_failure, on_success)
}
#[inline]
pub fn fold_ref<R, F, G>(&self, on_failure: F, on_success: G) -> R
where
F: FnOnce(&E) -> R,
G: FnOnce(&T) -> R,
{
match self {
Validation::Success(value) => on_success(value),
Validation::Failure(error) => on_failure(error),
}
}
#[inline]
pub fn fold_with_seed<R, F, G>(self, seed: R, f: F, g: G) -> R
where
F: FnOnce(R, E) -> R,
G: FnOnce(R, T) -> R,
{
match self {
Validation::Success(value) => g(seed, value),
Validation::Failure(error) => f(seed, error),
}
}
#[inline]
pub fn unwrap_or_else<F>(self, f: F) -> T
where
F: FnOnce(E) -> T,
{
match self {
Validation::Success(value) => value,
Validation::Failure(error) => f(error),
}
}
#[inline]
pub fn unwrap_or(self, default: T) -> T {
match self {
Validation::Success(value) => value,
Validation::Failure(_) => default,
}
}
#[inline]
pub fn unwrap_or_default(self) -> T
where
T: Default,
{
match self {
Validation::Success(value) => value,
Validation::Failure(_) => T::default(),
}
}
#[inline]
pub fn unwrap_err(self) -> E
where
T: std::fmt::Debug,
{
match self {
Validation::Success(value) => panic!(
"called `Validation::unwrap_err()` on a `Success` value: {:?}",
value
),
Validation::Failure(error) => error,
}
}
#[inline]
pub fn expect_err(self, msg: &str) -> E
where
T: std::fmt::Debug,
{
match self {
Validation::Success(value) => panic!("{}: {:?}", msg, value),
Validation::Failure(error) => error,
}
}
}
impl<T> Validation<T, T> {
#[inline]
pub fn merge(self) -> T {
match self {
Validation::Success(value) => value,
Validation::Failure(error) => error,
}
}
}
impl<T, E> Validation<T, NonEmptyVec<E>> {
pub fn fail(error: E) -> Self {
Validation::failure(NonEmptyVec::singleton(error))
}
}
impl<T, E: Semigroup> Validation<T, E> {
pub fn and<U>(self, other: Validation<U, E>) -> Validation<(T, U), E> {
match (self, other) {
(Validation::Success(a), Validation::Success(b)) => Validation::Success((a, b)),
(Validation::Failure(e1), Validation::Failure(e2)) => {
Validation::Failure(e1.combine(e2))
}
(Validation::Failure(e), _) => Validation::Failure(e),
(_, Validation::Failure(e)) => Validation::Failure(e),
}
}
#[inline]
pub fn and_then<U, F>(self, f: F) -> Validation<U, E>
where
F: FnOnce(T) -> Validation<U, E>,
{
match self {
Validation::Success(value) => f(value),
Validation::Failure(error) => Validation::Failure(error),
}
}
pub fn all_vec(validations: Vec<Validation<T, E>>) -> Validation<Vec<T>, E> {
let mut successes = Vec::new();
let mut failures = Vec::new();
for validation in validations {
match validation {
Validation::Success(value) => successes.push(value),
Validation::Failure(error) => failures.push(error),
}
}
if failures.is_empty() {
Validation::Success(successes)
} else {
Validation::Failure(
failures
.into_iter()
.reduce(|acc, e| acc.combine(e))
.unwrap(),
)
}
}
}
impl<T, E> Validation<T, E> {
pub fn all<V, E2>(validations: V) -> Validation<V::Output, E2>
where
E2: Semigroup,
V: ValidateAll<E2>,
{
validations.validate_all()
}
}
pub trait ValidateAll<E: Semigroup> {
type Output;
fn validate_all(self) -> Validation<Self::Output, E>;
}
macro_rules! impl_validate_all {
($T1:ident) => {
impl<E: Semigroup, $T1> ValidateAll<E> for (Validation<$T1, E>,) {
type Output = ($T1,);
fn validate_all(self) -> Validation<Self::Output, E> {
self.0.map(|v| (v,))
}
}
};
($T1:ident, $T2:ident) => {
impl<E: Semigroup, $T1, $T2> ValidateAll<E> for (Validation<$T1, E>, Validation<$T2, E>) {
type Output = ($T1, $T2);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2) = self;
$T1.and($T2)
}
}
};
($T1:ident, $T2:ident, $T3:ident) => {
impl<E: Semigroup, $T1, $T2, $T3> ValidateAll<E>
for (Validation<$T1, E>, Validation<$T2, E>, Validation<$T3, E>)
{
type Output = ($T1, $T2, $T3);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3) = self;
$T1.and($T2)
.map(|(a, b)| (a, b))
.and($T3)
.map(|((a, b), c)| (a, b, c))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4> ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
)
{
type Output = ($T1, $T2, $T3, $T4);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident, $T5:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4, $T5> ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
Validation<$T5, E>,
)
{
type Output = ($T1, $T2, $T3, $T4, $T5);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4, $T5) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
.and($T5)
.map(|((a, b, c, d), e)| (a, b, c, d, e))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident, $T5:ident, $T6:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4, $T5, $T6> ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
Validation<$T5, E>,
Validation<$T6, E>,
)
{
type Output = ($T1, $T2, $T3, $T4, $T5, $T6);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4, $T5, $T6) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
.and($T5)
.map(|((a, b, c, d), e)| (a, b, c, d, e))
.and($T6)
.map(|((a, b, c, d, e), f)| (a, b, c, d, e, f))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident, $T5:ident, $T6:ident, $T7:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4, $T5, $T6, $T7> ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
Validation<$T5, E>,
Validation<$T6, E>,
Validation<$T7, E>,
)
{
type Output = ($T1, $T2, $T3, $T4, $T5, $T6, $T7);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4, $T5, $T6, $T7) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
.and($T5)
.map(|((a, b, c, d), e)| (a, b, c, d, e))
.and($T6)
.map(|((a, b, c, d, e), f)| (a, b, c, d, e, f))
.and($T7)
.map(|((a, b, c, d, e, f), g)| (a, b, c, d, e, f, g))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident, $T5:ident, $T6:ident, $T7:ident, $T8:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8> ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
Validation<$T5, E>,
Validation<$T6, E>,
Validation<$T7, E>,
Validation<$T8, E>,
)
{
type Output = ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
.and($T5)
.map(|((a, b, c, d), e)| (a, b, c, d, e))
.and($T6)
.map(|((a, b, c, d, e), f)| (a, b, c, d, e, f))
.and($T7)
.map(|((a, b, c, d, e, f), g)| (a, b, c, d, e, f, g))
.and($T8)
.map(|((a, b, c, d, e, f, g), h)| (a, b, c, d, e, f, g, h))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident, $T5:ident, $T6:ident, $T7:ident, $T8:ident, $T9:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9> ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
Validation<$T5, E>,
Validation<$T6, E>,
Validation<$T7, E>,
Validation<$T8, E>,
Validation<$T9, E>,
)
{
type Output = ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
.and($T5)
.map(|((a, b, c, d), e)| (a, b, c, d, e))
.and($T6)
.map(|((a, b, c, d, e), f)| (a, b, c, d, e, f))
.and($T7)
.map(|((a, b, c, d, e, f), g)| (a, b, c, d, e, f, g))
.and($T8)
.map(|((a, b, c, d, e, f, g), h)| (a, b, c, d, e, f, g, h))
.and($T9)
.map(|((a, b, c, d, e, f, g, h), i)| (a, b, c, d, e, f, g, h, i))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident, $T5:ident, $T6:ident, $T7:ident, $T8:ident, $T9:ident, $T10:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9, $T10> ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
Validation<$T5, E>,
Validation<$T6, E>,
Validation<$T7, E>,
Validation<$T8, E>,
Validation<$T9, E>,
Validation<$T10, E>,
)
{
type Output = ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9, $T10);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9, $T10) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
.and($T5)
.map(|((a, b, c, d), e)| (a, b, c, d, e))
.and($T6)
.map(|((a, b, c, d, e), f)| (a, b, c, d, e, f))
.and($T7)
.map(|((a, b, c, d, e, f), g)| (a, b, c, d, e, f, g))
.and($T8)
.map(|((a, b, c, d, e, f, g), h)| (a, b, c, d, e, f, g, h))
.and($T9)
.map(|((a, b, c, d, e, f, g, h), i)| (a, b, c, d, e, f, g, h, i))
.and($T10)
.map(|((a, b, c, d, e, f, g, h, i), j)| (a, b, c, d, e, f, g, h, i, j))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident, $T5:ident, $T6:ident, $T7:ident, $T8:ident, $T9:ident, $T10:ident, $T11:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9, $T10, $T11> ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
Validation<$T5, E>,
Validation<$T6, E>,
Validation<$T7, E>,
Validation<$T8, E>,
Validation<$T9, E>,
Validation<$T10, E>,
Validation<$T11, E>,
)
{
type Output = ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9, $T10, $T11);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9, $T10, $T11) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
.and($T5)
.map(|((a, b, c, d), e)| (a, b, c, d, e))
.and($T6)
.map(|((a, b, c, d, e), f)| (a, b, c, d, e, f))
.and($T7)
.map(|((a, b, c, d, e, f), g)| (a, b, c, d, e, f, g))
.and($T8)
.map(|((a, b, c, d, e, f, g), h)| (a, b, c, d, e, f, g, h))
.and($T9)
.map(|((a, b, c, d, e, f, g, h), i)| (a, b, c, d, e, f, g, h, i))
.and($T10)
.map(|((a, b, c, d, e, f, g, h, i), j)| (a, b, c, d, e, f, g, h, i, j))
.and($T11)
.map(|((a, b, c, d, e, f, g, h, i, j), k)| (a, b, c, d, e, f, g, h, i, j, k))
}
}
};
($T1:ident, $T2:ident, $T3:ident, $T4:ident, $T5:ident, $T6:ident, $T7:ident, $T8:ident, $T9:ident, $T10:ident, $T11:ident, $T12:ident) => {
impl<E: Semigroup, $T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9, $T10, $T11, $T12>
ValidateAll<E>
for (
Validation<$T1, E>,
Validation<$T2, E>,
Validation<$T3, E>,
Validation<$T4, E>,
Validation<$T5, E>,
Validation<$T6, E>,
Validation<$T7, E>,
Validation<$T8, E>,
Validation<$T9, E>,
Validation<$T10, E>,
Validation<$T11, E>,
Validation<$T12, E>,
)
{
type Output = (
$T1,
$T2,
$T3,
$T4,
$T5,
$T6,
$T7,
$T8,
$T9,
$T10,
$T11,
$T12,
);
#[allow(non_snake_case)]
fn validate_all(self) -> Validation<Self::Output, E> {
let ($T1, $T2, $T3, $T4, $T5, $T6, $T7, $T8, $T9, $T10, $T11, $T12) = self;
$T1.and($T2)
.and($T3)
.map(|((a, b), c)| (a, b, c))
.and($T4)
.map(|((a, b, c), d)| (a, b, c, d))
.and($T5)
.map(|((a, b, c, d), e)| (a, b, c, d, e))
.and($T6)
.map(|((a, b, c, d, e), f)| (a, b, c, d, e, f))
.and($T7)
.map(|((a, b, c, d, e, f), g)| (a, b, c, d, e, f, g))
.and($T8)
.map(|((a, b, c, d, e, f, g), h)| (a, b, c, d, e, f, g, h))
.and($T9)
.map(|((a, b, c, d, e, f, g, h), i)| (a, b, c, d, e, f, g, h, i))
.and($T10)
.map(|((a, b, c, d, e, f, g, h, i), j)| (a, b, c, d, e, f, g, h, i, j))
.and($T11)
.map(|((a, b, c, d, e, f, g, h, i, j), k)| (a, b, c, d, e, f, g, h, i, j, k))
.and($T12)
.map(|((a, b, c, d, e, f, g, h, i, j, k), l)| {
(a, b, c, d, e, f, g, h, i, j, k, l)
})
}
}
};
}
impl_validate_all!(T1);
impl_validate_all!(T1, T2);
impl_validate_all!(T1, T2, T3);
impl_validate_all!(T1, T2, T3, T4);
impl_validate_all!(T1, T2, T3, T4, T5);
impl_validate_all!(T1, T2, T3, T4, T5, T6);
impl_validate_all!(T1, T2, T3, T4, T5, T6, T7);
impl_validate_all!(T1, T2, T3, T4, T5, T6, T7, T8);
impl_validate_all!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_validate_all!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_validate_all!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_validate_all!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
#[cfg(feature = "try_trait")]
mod try_impl {
use super::*;
use std::convert::Infallible;
use std::ops::{ControlFlow, FromResidual, Try};
impl<T, E> Try for Validation<T, E> {
type Output = T;
type Residual = Validation<Infallible, E>;
fn from_output(output: Self::Output) -> Self {
Validation::Success(output)
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Validation::Success(value) => ControlFlow::Continue(value),
Validation::Failure(error) => ControlFlow::Break(Validation::Failure(error)),
}
}
}
impl<T, E> FromResidual<Validation<Infallible, E>> for Validation<T, E> {
fn from_residual(residual: Validation<Infallible, E>) -> Self {
match residual {
Validation::Failure(error) => Validation::Failure(error),
Validation::Success(_) => unreachable!(),
}
}
}
impl<T, E> FromResidual<Result<Infallible, E>> for Validation<T, E> {
fn from_residual(residual: Result<Infallible, E>) -> Self {
match residual {
Err(error) => Validation::Failure(error),
Ok(_) => unreachable!(),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
let v = Validation::<_, Vec<&str>>::success(42);
assert!(v.is_success());
assert!(!v.is_failure());
}
#[test]
fn test_failure() {
let v = Validation::<i32, _>::failure(vec!["error"]);
assert!(v.is_failure());
assert!(!v.is_success());
}
#[test]
fn test_from_result_ok() {
let v = Validation::from_result(Ok::<_, Vec<&str>>(42));
assert_eq!(v, Validation::Success(42));
}
#[test]
fn test_from_result_err() {
let v = Validation::from_result(Err::<i32, _>(vec!["error"]));
assert_eq!(v, Validation::Failure(vec!["error"]));
}
#[test]
fn test_into_result_success() {
let v = Validation::<_, Vec<&str>>::success(42);
assert_eq!(v.into_result(), Ok(42));
}
#[test]
fn test_into_result_failure() {
let v = Validation::<i32, _>::failure(vec!["error"]);
assert_eq!(v.into_result(), Err(vec!["error"]));
}
#[test]
fn test_map_on_success() {
let v = Validation::<_, Vec<&str>>::success(5);
let result = v.map(|x| x * 2);
assert_eq!(result, Validation::Success(10));
}
#[test]
fn test_map_on_failure() {
let v = Validation::<i32, _>::failure(vec!["error"]);
let result = v.map(|x| x * 2);
assert_eq!(result, Validation::Failure(vec!["error"]));
}
#[test]
fn test_map_err_on_success() {
let v = Validation::<_, Vec<&str>>::success(42);
let result = v.map_err(|errors| errors.len());
assert_eq!(result, Validation::Success(42));
}
#[test]
fn test_map_err_on_failure() {
let v = Validation::<i32, _>::failure(vec!["error1", "error2"]);
let result = v.map_err(|errors| errors.len());
assert_eq!(result, Validation::Failure(2));
}
#[test]
fn test_and_both_success() {
let v1 = Validation::<_, Vec<&str>>::success(1);
let v2 = Validation::<_, Vec<&str>>::success(2);
assert_eq!(v1.and(v2), Validation::Success((1, 2)));
}
#[test]
fn test_and_both_failure() {
let v1 = Validation::<i32, _>::failure(vec!["error1"]);
let v2 = Validation::<i32, _>::failure(vec!["error2"]);
assert_eq!(v1.and(v2), Validation::Failure(vec!["error1", "error2"]));
}
#[test]
fn test_and_first_failure() {
let v1 = Validation::<i32, _>::failure(vec!["error"]);
let v2 = Validation::<_, Vec<&str>>::success(2);
assert_eq!(v1.and(v2), Validation::Failure(vec!["error"]));
}
#[test]
fn test_and_second_failure() {
let v1 = Validation::<_, Vec<&str>>::success(1);
let v2 = Validation::<i32, _>::failure(vec!["error"]);
assert_eq!(v1.and(v2), Validation::Failure(vec!["error"]));
}
#[test]
fn test_and_then_success() {
let v = Validation::<_, Vec<&str>>::success(5);
let result = v.and_then(|x| Validation::success(x * 2));
assert_eq!(result, Validation::Success(10));
}
#[test]
fn test_and_then_failure() {
let v = Validation::<i32, _>::failure(vec!["error"]);
let result = v.and_then(|x| Validation::success(x * 2));
assert_eq!(result, Validation::Failure(vec!["error"]));
}
#[test]
fn test_and_then_chain_failure() {
let v = Validation::<_, Vec<&str>>::success(5);
let result: Validation<i32, Vec<&str>> =
v.and_then(|_| Validation::failure(vec!["new error"]));
assert_eq!(result, Validation::Failure(vec!["new error"]));
}
#[test]
fn test_all_single_success() {
use crate::validation::ValidateAll;
let result = (Validation::<_, Vec<&str>>::success(1),).validate_all();
assert_eq!(result, Validation::Success((1,)));
}
#[test]
fn test_all_two_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
)
.validate_all();
assert_eq!(result, Validation::Success((1, 2)));
}
#[test]
fn test_all_three_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
)
.validate_all();
assert_eq!(result, Validation::Success((1, 2, 3)));
}
#[test]
fn test_all_with_failures() {
use crate::validation::ValidateAll;
let result = (
Validation::<i32, _>::failure(vec!["error1"]),
Validation::<i32, _>::failure(vec!["error2"]),
Validation::<i32, _>::failure(vec!["error3"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec!["error1", "error2", "error3"])
);
}
#[test]
fn test_all_mixed() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<i32, _>::failure(vec!["error1"]),
Validation::<i32, _>::failure(vec!["error2"]),
)
.validate_all();
assert_eq!(result, Validation::Failure(vec!["error1", "error2"]));
}
#[test]
fn test_all_vec_empty() {
let validations: Vec<Validation<i32, Vec<&str>>> = vec![];
let result = Validation::all_vec(validations);
assert_eq!(result, Validation::Success(vec![]));
}
#[test]
fn test_all_vec_all_success() {
let validations = vec![
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
];
let result = Validation::all_vec(validations);
assert_eq!(result, Validation::Success(vec![1, 2, 3]));
}
#[test]
fn test_all_vec_all_failures() {
let validations = vec![
Validation::<i32, _>::failure(vec!["error1"]),
Validation::<i32, _>::failure(vec!["error2"]),
Validation::<i32, _>::failure(vec!["error3"]),
];
let result = Validation::all_vec(validations);
assert_eq!(
result,
Validation::Failure(vec!["error1", "error2", "error3"])
);
}
#[test]
fn test_all_vec_mixed() {
let validations = vec![
Validation::<_, Vec<&str>>::success(1),
Validation::failure(vec!["error1"]),
Validation::<_, Vec<&str>>::success(2),
Validation::failure(vec!["error2"]),
];
let result = Validation::all_vec(validations);
assert_eq!(result, Validation::Failure(vec!["error1", "error2"]));
}
#[test]
fn test_fail_with_nonempty() {
use crate::nonempty::NonEmptyVec;
let v = Validation::<i32, NonEmptyVec<&str>>::fail("error");
assert!(v.is_failure());
match v {
Validation::Failure(errors) => {
assert_eq!(errors.len(), 1);
assert_eq!(errors.head(), &"error");
}
_ => panic!("Expected failure"),
}
}
#[test]
fn test_nonempty_error_accumulation() {
use crate::nonempty::NonEmptyVec;
let v1 = Validation::<i32, NonEmptyVec<&str>>::fail("error1");
let v2 = Validation::<i32, NonEmptyVec<&str>>::fail("error2");
let result = v1.and(v2);
match result {
Validation::Failure(errors) => {
assert_eq!(errors.len(), 2);
assert_eq!(errors.into_vec(), vec!["error1", "error2"]);
}
_ => panic!("Expected failure"),
}
}
#[test]
fn test_form_validation() {
#[derive(Debug, PartialEq)]
enum ValidationError {
InvalidEmail,
PasswordTooShort,
AgeTooYoung,
}
fn validate_email(email: &str) -> Validation<String, Vec<ValidationError>> {
if email.contains('@') {
Validation::success(email.to_string())
} else {
Validation::failure(vec![ValidationError::InvalidEmail])
}
}
fn validate_password(pwd: &str) -> Validation<String, Vec<ValidationError>> {
if pwd.len() >= 8 {
Validation::success(pwd.to_string())
} else {
Validation::failure(vec![ValidationError::PasswordTooShort])
}
}
fn validate_age(age: u8) -> Validation<u8, Vec<ValidationError>> {
if age >= 18 {
Validation::success(age)
} else {
Validation::failure(vec![ValidationError::AgeTooYoung])
}
}
use crate::validation::ValidateAll;
let result = (
validate_email("test@example.com"),
validate_password("secure123"),
validate_age(25),
)
.validate_all();
assert!(result.is_success());
let result = (
validate_email("invalid"),
validate_password("short"),
validate_age(15),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec![
ValidationError::InvalidEmail,
ValidationError::PasswordTooShort,
ValidationError::AgeTooYoung,
])
);
let result = (
validate_email("test@example.com"),
validate_password("short"),
validate_age(15),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec![
ValidationError::PasswordTooShort,
ValidationError::AgeTooYoung,
])
);
}
#[test]
fn test_validate_all_4tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success("two"),
Validation::<_, Vec<&str>>::success(3.0),
Validation::<_, Vec<&str>>::success(true),
)
.validate_all();
assert_eq!(result, Validation::Success((1, "two", 3.0, true)));
}
#[test]
fn test_validate_all_4tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32), Vec<&str>> = (
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
)
.validate_all();
assert_eq!(result, Validation::Failure(vec!["e1", "e2", "e3", "e4"]));
}
#[test]
fn test_validate_all_4tuple_mixed() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32), Vec<&str>> = (
Validation::Success(1),
Validation::Failure(vec!["e2"]),
Validation::Success(3),
Validation::Failure(vec!["e4"]),
)
.validate_all();
assert_eq!(result, Validation::Failure(vec!["e2", "e4"]));
}
#[test]
fn test_validate_all_5tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
Validation::<_, Vec<&str>>::success(4),
Validation::<_, Vec<&str>>::success(5),
)
.validate_all();
assert_eq!(result, Validation::Success((1, 2, 3, 4, 5)));
}
#[test]
fn test_validate_all_5tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32, i32), Vec<&str>> = (
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
Validation::Failure(vec!["e5"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec!["e1", "e2", "e3", "e4", "e5"])
);
}
#[test]
fn test_validate_all_6tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
Validation::<_, Vec<&str>>::success(4),
Validation::<_, Vec<&str>>::success(5),
Validation::<_, Vec<&str>>::success(6),
)
.validate_all();
assert_eq!(result, Validation::Success((1, 2, 3, 4, 5, 6)));
}
#[test]
#[allow(clippy::type_complexity)]
fn test_validate_all_6tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32, i32, i32), Vec<&str>> = (
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
Validation::Failure(vec!["e5"]),
Validation::Failure(vec!["e6"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec!["e1", "e2", "e3", "e4", "e5", "e6"])
);
}
#[test]
fn test_validate_all_7tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
Validation::<_, Vec<&str>>::success(4),
Validation::<_, Vec<&str>>::success(5),
Validation::<_, Vec<&str>>::success(6),
Validation::<_, Vec<&str>>::success(7),
)
.validate_all();
assert_eq!(result, Validation::Success((1, 2, 3, 4, 5, 6, 7)));
}
#[test]
#[allow(clippy::type_complexity)]
fn test_validate_all_7tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32, i32, i32, i32), Vec<&str>> = (
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
Validation::Failure(vec!["e5"]),
Validation::Failure(vec!["e6"]),
Validation::Failure(vec!["e7"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec!["e1", "e2", "e3", "e4", "e5", "e6", "e7"])
);
}
#[test]
fn test_validate_all_8tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
Validation::<_, Vec<&str>>::success(4),
Validation::<_, Vec<&str>>::success(5),
Validation::<_, Vec<&str>>::success(6),
Validation::<_, Vec<&str>>::success(7),
Validation::<_, Vec<&str>>::success(8),
)
.validate_all();
assert_eq!(result, Validation::Success((1, 2, 3, 4, 5, 6, 7, 8)));
}
#[test]
#[allow(clippy::type_complexity)]
fn test_validate_all_8tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32, i32, i32, i32, i32), Vec<&str>> = (
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
Validation::Failure(vec!["e5"]),
Validation::Failure(vec!["e6"]),
Validation::Failure(vec!["e7"]),
Validation::Failure(vec!["e8"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec!["e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8"])
);
}
#[test]
fn test_validate_all_9tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
Validation::<_, Vec<&str>>::success(4),
Validation::<_, Vec<&str>>::success(5),
Validation::<_, Vec<&str>>::success(6),
Validation::<_, Vec<&str>>::success(7),
Validation::<_, Vec<&str>>::success(8),
Validation::<_, Vec<&str>>::success(9),
)
.validate_all();
assert_eq!(result, Validation::Success((1, 2, 3, 4, 5, 6, 7, 8, 9)));
}
#[test]
#[allow(clippy::type_complexity)]
fn test_validate_all_9tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32, i32, i32, i32, i32, i32), Vec<&str>> = (
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
Validation::Failure(vec!["e5"]),
Validation::Failure(vec!["e6"]),
Validation::Failure(vec!["e7"]),
Validation::Failure(vec!["e8"]),
Validation::Failure(vec!["e9"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec!["e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9"])
);
}
#[test]
fn test_validate_all_10tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
Validation::<_, Vec<&str>>::success(4),
Validation::<_, Vec<&str>>::success(5),
Validation::<_, Vec<&str>>::success(6),
Validation::<_, Vec<&str>>::success(7),
Validation::<_, Vec<&str>>::success(8),
Validation::<_, Vec<&str>>::success(9),
Validation::<_, Vec<&str>>::success(10),
)
.validate_all();
assert_eq!(result, Validation::Success((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)));
}
#[test]
#[allow(clippy::type_complexity)]
fn test_validate_all_10tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), Vec<&str>> = (
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
Validation::Failure(vec!["e5"]),
Validation::Failure(vec!["e6"]),
Validation::Failure(vec!["e7"]),
Validation::Failure(vec!["e8"]),
Validation::Failure(vec!["e9"]),
Validation::Failure(vec!["e10"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec![
"e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "e10"
])
);
}
#[test]
fn test_validate_all_11tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
Validation::<_, Vec<&str>>::success(4),
Validation::<_, Vec<&str>>::success(5),
Validation::<_, Vec<&str>>::success(6),
Validation::<_, Vec<&str>>::success(7),
Validation::<_, Vec<&str>>::success(8),
Validation::<_, Vec<&str>>::success(9),
Validation::<_, Vec<&str>>::success(10),
Validation::<_, Vec<&str>>::success(11),
)
.validate_all();
assert_eq!(
result,
Validation::Success((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
);
}
#[test]
#[allow(clippy::type_complexity)]
fn test_validate_all_11tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), Vec<&str>> =
(
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
Validation::Failure(vec!["e5"]),
Validation::Failure(vec!["e6"]),
Validation::Failure(vec!["e7"]),
Validation::Failure(vec!["e8"]),
Validation::Failure(vec!["e9"]),
Validation::Failure(vec!["e10"]),
Validation::Failure(vec!["e11"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec![
"e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "e10", "e11"
])
);
}
#[test]
fn test_validate_all_12tuple_all_success() {
use crate::validation::ValidateAll;
let result = (
Validation::<_, Vec<&str>>::success(1),
Validation::<_, Vec<&str>>::success(2),
Validation::<_, Vec<&str>>::success(3),
Validation::<_, Vec<&str>>::success(4),
Validation::<_, Vec<&str>>::success(5),
Validation::<_, Vec<&str>>::success(6),
Validation::<_, Vec<&str>>::success(7),
Validation::<_, Vec<&str>>::success(8),
Validation::<_, Vec<&str>>::success(9),
Validation::<_, Vec<&str>>::success(10),
Validation::<_, Vec<&str>>::success(11),
Validation::<_, Vec<&str>>::success(12),
)
.validate_all();
assert_eq!(
result,
Validation::Success((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
);
}
#[test]
#[allow(clippy::type_complexity)]
fn test_validate_all_12tuple_all_failure() {
use crate::validation::ValidateAll;
let result: Validation<
(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32),
Vec<&str>,
> = (
Validation::Failure(vec!["e1"]),
Validation::Failure(vec!["e2"]),
Validation::Failure(vec!["e3"]),
Validation::Failure(vec!["e4"]),
Validation::Failure(vec!["e5"]),
Validation::Failure(vec!["e6"]),
Validation::Failure(vec!["e7"]),
Validation::Failure(vec!["e8"]),
Validation::Failure(vec!["e9"]),
Validation::Failure(vec!["e10"]),
Validation::Failure(vec!["e11"]),
Validation::Failure(vec!["e12"]),
)
.validate_all();
assert_eq!(
result,
Validation::Failure(vec![
"e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "e10", "e11", "e12"
])
);
}
#[test]
fn test_bimap_on_success() {
let v = Validation::<_, String>::success(5);
let result = v.bimap(|e| e.len(), |x| x * 2);
assert_eq!(result, Validation::Success(10));
}
#[test]
fn test_bimap_on_failure() {
let v = Validation::<i32, _>::failure("error".to_string());
let result = v.bimap(|e| e.len(), |x| x * 2);
assert_eq!(result, Validation::Failure(5));
}
#[test]
fn test_bimap_type_transformation() {
let v: Validation<i32, String> = Validation::success(5);
let result: Validation<f64, usize> = v.bimap(|e| e.len(), |x| x as f64 * 2.0);
assert_eq!(result, Validation::Success(10.0));
}
#[test]
fn test_bimap_success_first() {
let v = Validation::<_, String>::success(5);
let result = v.bimap_success_first(|x| x * 2, |e| e.len());
assert_eq!(result, Validation::Success(10));
}
#[test]
fn test_bimap_ref() {
let v = Validation::<_, String>::success(5);
let result = v.bimap_ref(|e| e.len(), |x| *x * 2);
assert_eq!(result, Validation::Success(10));
assert!(v.is_success());
}
#[test]
fn test_fold_on_success() {
let v = Validation::<_, String>::success(5);
let result = v.fold(|e| format!("Err: {}", e), |x| format!("Val: {}", x));
assert_eq!(result, "Val: 5");
}
#[test]
fn test_fold_on_failure() {
let v = Validation::<i32, _>::failure("oops".to_string());
let result = v.fold(|e| format!("Err: {}", e), |x| format!("Val: {}", x));
assert_eq!(result, "Err: oops");
}
#[test]
fn test_fold_to_different_type() {
let v = Validation::<_, String>::success(5);
let result: i32 = v.fold(|e| e.len() as i32, |x| x * 2);
assert_eq!(result, 10);
}
#[test]
fn test_fold_success_first() {
let v = Validation::<_, String>::success(5);
let result = v.fold_success_first(|x| x * 2, |e| e.len() as i32);
assert_eq!(result, 10);
}
#[test]
fn test_fold_ref() {
let v = Validation::<_, String>::success(5);
let result = v.fold_ref(|e| e.len() as i32, |x| *x * 2);
assert_eq!(result, 10);
assert!(v.is_success());
}
#[test]
fn test_fold_with_seed() {
let v = Validation::<_, String>::success(5);
let result = v.fold_with_seed(10, |acc, e| acc + e.len() as i32, |acc, x| acc + x);
assert_eq!(result, 15);
let v = Validation::<i32, _>::failure("err".to_string());
let result = v.fold_with_seed(10, |acc, e| acc + e.len() as i32, |acc, x| acc + x);
assert_eq!(result, 13);
}
#[test]
fn test_unwrap_or_else_success() {
let v = Validation::<_, String>::success(5);
assert_eq!(v.unwrap_or_else(|e| e.len() as i32), 5);
}
#[test]
fn test_unwrap_or_else_failure() {
let v = Validation::<i32, _>::failure("error".to_string());
assert_eq!(v.unwrap_or_else(|e| e.len() as i32), 5);
}
#[test]
fn test_unwrap_or() {
let success = Validation::<_, String>::success(5);
assert_eq!(success.unwrap_or(0), 5);
let failure = Validation::<i32, _>::failure("err".to_string());
assert_eq!(failure.unwrap_or(0), 0);
}
#[test]
fn test_unwrap_or_default() {
let failure = Validation::<i32, _>::failure("err".to_string());
assert_eq!(failure.unwrap_or_default(), 0);
}
#[test]
fn test_unwrap_err() {
let failure = Validation::<i32, _>::failure("error");
assert_eq!(failure.unwrap_err(), "error");
}
#[test]
#[should_panic(expected = "called `Validation::unwrap_err()` on a `Success` value")]
fn test_unwrap_err_panics_on_success() {
let success = Validation::<_, String>::success(5);
let _ = success.unwrap_err();
}
#[test]
fn test_expect_err() {
let failure = Validation::<i32, _>::failure("error");
assert_eq!(failure.expect_err("should be error"), "error");
}
#[test]
#[should_panic(expected = "expected error")]
fn test_expect_err_panics_on_success() {
let success = Validation::<_, String>::success(5);
let _ = success.expect_err("expected error");
}
#[test]
fn test_merge_success() {
let v: Validation<i32, i32> = Validation::success(5);
assert_eq!(v.merge(), 5);
}
#[test]
fn test_merge_failure() {
let v: Validation<i32, i32> = Validation::failure(3);
assert_eq!(v.merge(), 3);
}
#[test]
fn test_bifunctor_identity_law() {
let v: Validation<i32, String> = Validation::success(5);
let result = v.clone().bimap(|e| e, |x| x);
assert_eq!(result, v);
let v: Validation<i32, String> = Validation::failure("err".to_string());
let result = v.clone().bimap(|e| e, |x| x);
assert_eq!(result, v);
}
#[test]
fn test_bifunctor_composition_law() {
let f1 = |s: String| s.len();
let f2 = |n: usize| n * 2;
let g1 = |x: i32| x + 1;
let g2 = |x: i32| x * 3;
let v: Validation<i32, String> = Validation::success(5);
let left = v.clone().bimap(f1, g1).bimap(f2, g2);
let right = v.bimap(|e| f2(f1(e)), |x| g2(g1(x)));
assert_eq!(left, right);
}
#[test]
fn test_bimap_with_and_then() {
let v = Validation::<_, Vec<String>>::success(5)
.and_then(|x| {
if x > 0 {
Validation::success(x * 2)
} else {
Validation::failure(vec!["non-positive".to_string()])
}
})
.bimap(|errs| errs.len(), |x| x + 1);
assert_eq!(v, Validation::Success(11));
}
#[test]
fn test_fold_as_final_operation() {
let result = Validation::<_, Vec<String>>::success(5)
.map(|x| x * 2)
.fold(
|errs| format!("{} errors", errs.len()),
|x| format!("result: {}", x),
);
assert_eq!(result, "result: 10");
}
}