use std::collections::LinkedList;
use semigroup::Semigroup;
#[derive(Clone, Debug)]
pub enum Validation<E, A> {
Success(A),
Failure(E),
}
pub fn success<E: Clone, A: Clone>(item: A) -> Validation<E, A> {
Validation::Success(item)
}
pub fn failure<E: Clone, A: Clone>(e: E) -> Validation<E, A> {
Validation::Failure(e)
}
impl<E: Clone + Semigroup, A: Clone> Validation<E, A> {
pub fn map<B, F>(&self, f: F) -> Validation<E, B>
where
F: FnOnce(A) -> B,
{
match self {
&Validation::Success(ref a) => Validation::Success(f(a.clone())),
&Validation::Failure(ref e) => Validation::Failure::<E, B>(e.clone()),
}
}
pub fn get_or_else(self, fallback: A) -> A {
match self {
Validation::Success(a) => a,
Validation::Failure(_) => fallback,
}
}
pub fn unwrap(self) -> A {
match self {
Validation::Success(a) => a,
Validation::Failure(_) => panic!("Validation is a failure"),
}
}
pub fn is_success(&self) -> bool {
match self {
&Validation::Success(_) => true,
&Validation::Failure(_) => false,
}
}
pub fn is_failure(&self) -> bool {
!self.is_success()
}
pub fn get_err(self) -> E {
match self {
Validation::Failure(e) => e,
Validation::Success(_) => panic!("Validation is a success"),
}
}
}
fn collect_err1<A, E>(a: Validation<E, A>, e: E) -> E
where
A: Clone,
E: Clone + Semigroup,
{
match a {
Validation::Failure(x) => x.mappend(e),
Validation::Success(_) => e,
}
}
fn collect_err2<A, B, E>(a: Validation<E, A>, b: Validation<E, B>, e: E) -> E
where
A: Clone,
B: Clone,
E: Clone + Semigroup,
{
match b {
Validation::Failure(x) => x.mappend(collect_err1(a, e)),
Validation::Success(_) => collect_err1(a, e),
}
}
fn collect_err3<A, B, C, E>(
a: Validation<E, A>,
b: Validation<E, B>,
c: Validation<E, C>,
e: E,
) -> E
where
A: Clone,
B: Clone,
C: Clone,
E: Clone + Semigroup,
{
match c {
Validation::Failure(x) => x.mappend(collect_err2(a, b, e)),
Validation::Success(_) => collect_err2(a, b, e),
}
}
fn collect_err4<A, B, C, D, E>(
a: Validation<E, A>,
b: Validation<E, B>,
c: Validation<E, C>,
d: Validation<E, D>,
e: E,
) -> E
where
A: Clone,
B: Clone,
C: Clone,
D: Clone,
E: Clone + Semigroup,
{
match d {
Validation::Failure(x) => x.mappend(collect_err3(a, b, c, e)),
Validation::Success(_) => collect_err3(a, b, c, e),
}
}
pub fn apply2<A, B, R, F, E>(a: Validation<E, A>, b: Validation<E, B>, f: F) -> Validation<E, R>
where
A: Clone,
B: Clone,
R: Clone,
E: Clone + Semigroup,
F: FnOnce(A, B) -> R,
{
match b {
Validation::Failure(e) => Validation::Failure(collect_err1(a, e)),
Validation::Success(b2) => {
let p = |a2: A| f(a2, b2);
a.map(p)
}
}
}
pub fn apply3<A, B, C, R, F, E>(
a: Validation<E, A>,
b: Validation<E, B>,
c: Validation<E, C>,
f: F,
) -> Validation<E, R>
where
A: Clone,
B: Clone,
C: Clone,
R: Clone,
E: Clone + Semigroup,
F: FnOnce(A, B, C) -> R,
{
match c {
Validation::Failure(e) => Validation::Failure(collect_err2(a, b, e)),
Validation::Success(c2) => {
let p = |a2: A, b2: B| f(a2, b2, c2);
apply2(a, b, p)
}
}
}
pub fn apply4<A, B, C, D, R, F, E>(
a: Validation<E, A>,
b: Validation<E, B>,
c: Validation<E, C>,
d: Validation<E, D>,
f: F,
) -> Validation<E, R>
where
A: Clone,
B: Clone,
C: Clone,
D: Clone,
R: Clone,
E: Clone + Semigroup,
F: FnOnce(A, B, C, D) -> R,
{
match d {
Validation::Failure(e) => Validation::Failure(collect_err3(a, b, c, e)),
Validation::Success(d2) => {
let p = |a2: A, b2: B, c2: C| f(a2, b2, c2, d2);
apply3(a, b, c, p)
}
}
}
pub fn apply5<A, B, C, D, G, R, F, E>(
a: Validation<E, A>,
b: Validation<E, B>,
c: Validation<E, C>,
d: Validation<E, D>,
g: Validation<E, G>,
f: F,
) -> Validation<E, R>
where
A: Clone,
B: Clone,
C: Clone,
D: Clone,
G: Clone,
R: Clone,
E: Clone + Semigroup,
F: FnOnce(A, B, C, D, G) -> R,
{
match g {
Validation::Failure(e) => Validation::Failure(collect_err4(a, b, c, d, e)),
Validation::Success(g2) => {
let p = |a2: A, b2: B, c2: C, d2: D| f(a2, b2, c2, d2, g2);
apply4(a, b, c, d, p)
}
}
}
impl<T: Clone> Semigroup for LinkedList<T> {
fn mappend(&self, b: LinkedList<T>) -> LinkedList<T> {
let mut cloned_list = self.clone();
for e in b.iter() {
cloned_list.push_back(e.clone());
}
cloned_list
}
}
pub type ValidationNel<E: Clone, A: Clone> = Validation<LinkedList<E>, A>;
pub fn failure_nel<E: Clone, A: Clone>(e: E) -> ValidationNel<E, A> {
let mut li: LinkedList<E> = LinkedList::new();
li.push_back(e);
Validation::Failure(li)
}
pub fn success_nel<E: Clone, A: Clone>(a: A) -> ValidationNel<E, A> {
Validation::Success(a)
}
#[cfg(test)]
mod tests {
use validation::*;
use super::success;
use super::failure;
impl Semigroup for i32 {
fn mappend(&self, b: i32) -> i32 {
self + b
}
}
fn add2(a: i32, b: i32) -> i32 {
a + b
}
fn add3(a: i32, b: i32, c: i32) -> i32 {
a + b + c
}
fn div(s: i32, t: i32) -> ValidationNel<String, i32> {
match t {
0 => failure_nel::<String, i32>("Cannot devide by 0".to_owned()),
_ => success_nel::<String, i32>(s / t),
}
}
#[test]
fn it_works() {
let a = success::<i32, i32>(1);
let b = success::<i32, i32>(2);
let r = apply2(a, b, add2);
assert!(r.unwrap() == 3);
let a = success::<i32, i32>(1);
let b = success::<i32, i32>(2);
let c = success::<i32, i32>(3);
let r = apply3(a, b, c, add3);
assert!(r.unwrap() == 6);
let a = success::<i32, i32>(1);
let b = success::<i32, i32>(2);
let e = failure::<i32, i32>(3);
let r = apply3(a, b, e, add3);
assert!(r.is_failure());
let r = div(10, 0);
assert!(r.is_failure());
let r = div(10, 2);
assert!(r.unwrap() == 5);
let r = apply3(div(10, 0), div(10, 1), div(0, 0), add3);
assert!(r.get_err().len() == 2);
let r = apply3(
failure::<i32, i32>(1),
failure::<i32, i32>(1),
failure::<i32, i32>(1),
add3,
);
assert!(r.get_err() == 3);
}
}