Skip to main content

bo/
validation.rs

1use std::collections::LinkedList;
2use semigroup::Semigroup;
3
4#[derive(Clone, Debug)]
5pub enum Validation<E, A> {
6    Success(A),
7    Failure(E),
8}
9
10pub fn success<E: Clone, A: Clone>(item: A) -> Validation<E, A> {
11    Validation::Success(item)
12}
13
14pub fn failure<E: Clone, A: Clone>(e: E) -> Validation<E, A> {
15    Validation::Failure(e)
16}
17
18impl<E: Clone + Semigroup, A: Clone> Validation<E, A> {
19    // This is the Applicative ap function but somehow I do not need it - this indicates
20    // serious misunderstanding in the code below and should be fixed at some point.
21    //pub fn ap<R, F>(&self, vf: Validation<E, F>) -> Validation<E, R>
22    //where
23    //    F: FnOnce(A) -> R,
24    //{
25    //    match self {
26    //        &Validation::Failure(ref e) => {
27    //            match vf {
28    //                Validation::Failure(e2) => Validation::Failure(e2.mappend(e.clone())),
29    //                Validation::Success(_) => Validation::Failure(e.clone()),
30    //            }
31    //        }
32    //        &Validation::Success(ref a) => {
33    //            match vf {
34    //                Validation::Failure(ref e2) => Validation::Failure(e2.clone()),
35    //                Validation::Success(f) => Validation::Success(f(a.clone())),
36    //            }
37    //        }
38    //    }
39    //}
40
41    pub fn map<B, F>(&self, f: F) -> Validation<E, B>
42    where
43        F: FnOnce(A) -> B,
44    {
45        match self {
46            &Validation::Success(ref a) => Validation::Success(f(a.clone())),
47            &Validation::Failure(ref e) => Validation::Failure::<E, B>(e.clone()),
48        }
49    }
50
51    pub fn get_or_else(self, fallback: A) -> A {
52        match self {
53            Validation::Success(a) => a,
54            Validation::Failure(_) => fallback,
55        }
56    }
57
58    pub fn unwrap(self) -> A {
59        match self {
60            Validation::Success(a) => a,
61            Validation::Failure(_) => panic!("Validation is a failure"),
62        }
63    }
64    pub fn is_success(&self) -> bool {
65        match self {
66            &Validation::Success(_) => true,
67            &Validation::Failure(_) => false,
68        }
69    }
70    pub fn is_failure(&self) -> bool {
71        !self.is_success()
72    }
73
74    pub fn get_err(self) -> E {
75        match self {
76            Validation::Failure(e) => e,
77            Validation::Success(_) => panic!("Validation is a success"),
78        }
79    }
80}
81
82fn collect_err1<A, E>(a: Validation<E, A>, e: E) -> E
83where
84    A: Clone,
85    E: Clone + Semigroup,
86{
87    match a {
88        Validation::Failure(x) => x.mappend(e),
89        Validation::Success(_) => e,
90    }
91}
92fn collect_err2<A, B, E>(a: Validation<E, A>, b: Validation<E, B>, e: E) -> E
93where
94    A: Clone,
95    B: Clone,
96    E: Clone + Semigroup,
97{
98    match b {
99        Validation::Failure(x) => x.mappend(collect_err1(a, e)),
100        Validation::Success(_) => collect_err1(a, e),
101    }
102}
103fn collect_err3<A, B, C, E>(
104    a: Validation<E, A>,
105    b: Validation<E, B>,
106    c: Validation<E, C>,
107    e: E,
108) -> E
109where
110    A: Clone,
111    B: Clone,
112    C: Clone,
113    E: Clone + Semigroup,
114{
115    match c {
116        Validation::Failure(x) => x.mappend(collect_err2(a, b, e)),
117        Validation::Success(_) => collect_err2(a, b, e),
118    }
119}
120
121fn collect_err4<A, B, C, D, E>(
122    a: Validation<E, A>,
123    b: Validation<E, B>,
124    c: Validation<E, C>,
125    d: Validation<E, D>,
126    e: E,
127) -> E
128where
129    A: Clone,
130    B: Clone,
131    C: Clone,
132    D: Clone,
133    E: Clone + Semigroup,
134{
135    match d {
136        Validation::Failure(x) => x.mappend(collect_err3(a, b, c, e)),
137        Validation::Success(_) => collect_err3(a, b, c, e),
138    }
139}
140
141// Runs a function f in the success of the Validation a or passing the failure
142// of a through.
143pub fn apply2<A, B, R, F, E>(a: Validation<E, A>, b: Validation<E, B>, f: F) -> Validation<E, R>
144where
145    A: Clone,
146    B: Clone,
147    R: Clone,
148    E: Clone + Semigroup,
149    F: FnOnce(A, B) -> R,
150{
151
152    match b {
153        Validation::Failure(e) => Validation::Failure(collect_err1(a, e)),
154        Validation::Success(b2) => {
155            let p = |a2: A| f(a2, b2);
156            a.map(p)
157        }
158    }
159}
160
161// Runs a function f in the success of the Validations a,b,and c or passing any
162// failures through, accumulating errors along the way. This means that evaluation
163// is not short-circuited, but that all failures are collected using the supplied
164// semigroup error type.
165pub fn apply3<A, B, C, R, F, E>(
166    a: Validation<E, A>,
167    b: Validation<E, B>,
168    c: Validation<E, C>,
169    f: F,
170) -> Validation<E, R>
171where
172    A: Clone,
173    B: Clone,
174    C: Clone,
175    R: Clone,
176    E: Clone + Semigroup,
177    F: FnOnce(A, B, C) -> R,
178{
179
180    match c {
181        Validation::Failure(e) => Validation::Failure(collect_err2(a, b, e)),
182        Validation::Success(c2) => {
183            let p = |a2: A, b2: B| f(a2, b2, c2);
184            apply2(a, b, p)
185        }
186    }
187}
188// Runs a function f in the success of the Validations a,b,c, and d or passing any
189// failures through, accumulating errors along the way. This means that evaluation
190// is not short-circuited, but that all failures are collected using the supplied
191// semigroup error type.
192pub fn apply4<A, B, C, D, R, F, E>(
193    a: Validation<E, A>,
194    b: Validation<E, B>,
195    c: Validation<E, C>,
196    d: Validation<E, D>,
197    f: F,
198) -> Validation<E, R>
199where
200    A: Clone,
201    B: Clone,
202    C: Clone,
203    D: Clone,
204    R: Clone,
205    E: Clone + Semigroup,
206    F: FnOnce(A, B, C, D) -> R,
207{
208
209    match d {
210        Validation::Failure(e) => Validation::Failure(collect_err3(a, b, c, e)),
211        Validation::Success(d2) => {
212            let p = |a2: A, b2: B, c2: C| f(a2, b2, c2, d2);
213            apply3(a, b, c, p)
214        }
215    }
216}
217// Runs a function f in the success of the Validations a,b,c,g and e or passing any
218// failures through, accumulating errors along the way. This means that evaluation
219// is not short-circuited, but that all failures are collected using the supplied
220// semigroup error type.
221pub fn apply5<A, B, C, D, G, R, F, E>(
222    a: Validation<E, A>,
223    b: Validation<E, B>,
224    c: Validation<E, C>,
225    d: Validation<E, D>,
226    g: Validation<E, G>,
227    f: F,
228) -> Validation<E, R>
229where
230    A: Clone,
231    B: Clone,
232    C: Clone,
233    D: Clone,
234    G: Clone,
235    R: Clone,
236    E: Clone + Semigroup,
237    F: FnOnce(A, B, C, D, G) -> R,
238{
239
240    match g {
241        Validation::Failure(e) => Validation::Failure(collect_err4(a, b, c, d, e)),
242        Validation::Success(g2) => {
243            let p = |a2: A, b2: B, c2: C, d2: D| f(a2, b2, c2, d2, g2);
244            apply4(a, b, c, d, p)
245        }
246    }
247}
248
249impl<T: Clone> Semigroup for LinkedList<T> {
250    fn mappend(&self, b: LinkedList<T>) -> LinkedList<T> {
251        let mut cloned_list = self.clone();
252        for e in b.iter() {
253            cloned_list.push_back(e.clone());
254        }
255        cloned_list
256    }
257}
258
259pub type ValidationNel<E: Clone, A: Clone> = Validation<LinkedList<E>, A>;
260
261pub fn failure_nel<E: Clone, A: Clone>(e: E) -> ValidationNel<E, A> {
262    let mut li: LinkedList<E> = LinkedList::new();
263    li.push_back(e);
264    Validation::Failure(li)
265}
266
267pub fn success_nel<E: Clone, A: Clone>(a: A) -> ValidationNel<E, A> {
268    Validation::Success(a)
269}
270
271#[cfg(test)]
272mod tests {
273    use validation::*;
274    use super::success;
275    use super::failure;
276
277    // Let's use i32 as error counter error type
278    impl Semigroup for i32 {
279        fn mappend(&self, b: i32) -> i32 {
280            self + b
281        }
282    }
283
284
285    // A function that takes two parameters
286    fn add2(a: i32, b: i32) -> i32 {
287        a + b
288    }
289    // A function that takes three parameters
290    fn add3(a: i32, b: i32, c: i32) -> i32 {
291        a + b + c
292    }
293
294    // A function that works in the context of validation and returns an error if
295    // if devision by 0 would occur.
296    fn div(s: i32, t: i32) -> ValidationNel<String, i32> {
297        match t {
298            0 => failure_nel::<String, i32>("Cannot devide by 0".to_owned()),
299            _ => success_nel::<String, i32>(s / t),
300        }
301    }
302
303    #[test]
304    fn it_works() {
305        let a = success::<i32, i32>(1);
306        let b = success::<i32, i32>(2);
307        let r = apply2(a, b, add2);
308        assert!(r.unwrap() == 3);
309
310        let a = success::<i32, i32>(1);
311        let b = success::<i32, i32>(2);
312        let c = success::<i32, i32>(3);
313        let r = apply3(a, b, c, add3);
314        assert!(r.unwrap() == 6);
315
316        let a = success::<i32, i32>(1);
317        let b = success::<i32, i32>(2);
318        let e = failure::<i32, i32>(3);
319        let r = apply3(a, b, e, add3);
320        assert!(r.is_failure());
321
322        let r = div(10, 0);
323        assert!(r.is_failure());
324
325        let r = div(10, 2);
326        assert!(r.unwrap() == 5);
327
328        let r = apply3(div(10, 0), div(10, 1), div(0, 0), add3);
329        assert!(r.get_err().len() == 2);
330
331        let r = apply3(
332            failure::<i32, i32>(1),
333            failure::<i32, i32>(1),
334            failure::<i32, i32>(1),
335            add3,
336        );
337        assert!(r.get_err() == 3);
338    }
339
340}