tailcall_valid/
valid.rs

1use super::append::Append;
2use super::Cause;
3
4/// A validation type that can represent either a successful value of type `A`
5/// or a collection of validation errors of type `E` with trace context `T`.
6///
7/// `Valid` is useful for accumulating multiple validation errors rather than
8/// stopping at the first error encountered.
9#[derive(Debug, PartialEq)]
10pub struct Valid<A, E, T>(Result<A, Vec<Cause<E, T>>>);
11
12/// Trait for types that can perform validation operations.
13///
14/// This trait provides a rich set of combinators for working with validations,
15/// allowing you to chain, combine and transform validation results.
16pub trait Validator<A, E, T>: Sized {
17    /// Maps a function over the successful value, transforming it to a new type.
18    ///
19    /// # Examples
20    /// ```
21    /// use tailcall_valid::{Valid, Validator};
22    /// let valid = Valid::<i32, (), ()>::succeed(1);
23    /// let result = valid.map(|x| x.to_string());
24    /// assert_eq!(result, Valid::succeed("1".to_string()));
25    /// ```
26    fn map<A1>(self, f: impl FnOnce(A) -> A1) -> Valid<A1, E, T> {
27        Valid(self.to_result().map(f))
28    }
29
30    /// Executes a side effect function if the validation is successful.
31    /// The original value is preserved.
32    ///
33    /// # Examples
34    /// ```
35    /// use tailcall_valid::{Valid, Validator};
36    /// let mut sum = 0;
37    /// let valid = Valid::<i32, (), ()>::succeed(5);
38    /// valid.foreach(|x| sum += x);
39    /// assert_eq!(sum, 5);
40    /// ```
41    fn foreach(self, mut f: impl FnMut(A)) -> Valid<A, E, T>
42    where
43        A: Clone,
44    {
45        match self.to_result() {
46            Ok(a) => {
47                f(a.clone());
48                Valid::succeed(a)
49            }
50            Err(e) => Valid(Err(e)),
51        }
52    }
53
54    /// Returns true if the validation is successful.
55    fn is_succeed(&self) -> bool;
56
57    /// Returns true if the validation contains errors.
58    fn is_fail(&self) -> bool;
59
60    /// Combines two validations, keeping the result of the second one if both succeed.
61    /// If either validation fails, all errors are collected.
62    ///
63    /// # Examples
64    /// ```
65    /// use tailcall_valid::{Valid, Validator};
66    /// let v1 = Valid::<i32, &str, ()>::succeed(1);
67    /// let v2 = Valid::<&str, &str, ()>::succeed("ok");
68    /// assert_eq!(v1.and(v2), Valid::succeed("ok"));
69    /// ```
70    fn and<A1>(self, other: Valid<A1, E, T>) -> Valid<A1, E, T> {
71        self.zip(other).map(|(_, a1)| a1)
72    }
73
74    /// Combines two validations into a tuple of their results.
75    /// If either validation fails, all errors are collected.
76    ///
77    /// # Examples
78    /// ```
79    /// use tailcall_valid::{Valid, Validator};
80    /// let v1 = Valid::<i32, &str, ()>::succeed(1);
81    /// let v2 = Valid::<&str, &str, ()>::succeed("ok");
82    /// assert_eq!(v1.zip(v2), Valid::succeed((1, "ok")));
83    /// ```
84    fn zip<A1>(self, other: Valid<A1, E, T>) -> Valid<(A, A1), E, T> {
85        match self.to_result() {
86            Ok(a) => match other.0 {
87                Ok(a1) => Valid(Ok((a, a1))),
88                Err(e1) => Valid(Err(e1)),
89            },
90            Err(mut e1) => match other.0 {
91                Ok(_) => Valid(Err(e1)),
92                Err(e2) => {
93                    e1.extend(e2);
94                    Valid(Err(e1))
95                }
96            },
97        }
98    }
99
100    /// Starts a fusion chain of validations. This allows combining multiple
101    /// validation results using the `Append` trait.
102    ///
103    /// # Examples
104    /// ```
105    /// use tailcall_valid::{Valid, Validator};
106    /// let v1: Valid<Vec<i32>, (), ()> = Valid::succeed(vec![1, 2]);
107    /// let v2: Valid<Vec<i32>, (), ()> = Valid::succeed(vec![3, 4]);
108    /// let result = v1.fuse(v2);
109    /// assert_eq!(result.to_result().unwrap(), (vec![1, 2], vec![3, 4]));
110    /// ```
111    fn fuse<A1>(self, other: Valid<A1, E, T>) -> Fusion<(A, A1), E, T> {
112        Fusion(self.zip(other))
113    }
114
115    /// Adds trace context to any errors in the validation.
116    /// Successful validations are unaffected.
117    ///
118    /// # Examples
119    /// ```
120    /// use tailcall_valid::{Valid, Validator};
121    /// let result = Valid::<(), &str, &str>::fail("error")
122    ///     .trace("field_name")
123    ///     .trace("form");
124    /// ```
125    fn trace(self, trace: impl Into<T> + Clone) -> Valid<A, E, T> {
126        let valid = self.to_result();
127        if let Err(error) = valid {
128            return Valid(Err(error
129                .into_iter()
130                .map(|cause| cause.trace(trace.clone().into()))
131                .collect()));
132        }
133
134        Valid(valid)
135    }
136
137    /// Handles both success and failure cases of a validation.
138    ///
139    /// - If successful, applies the `ok` function to the value
140    /// - If failed, calls the `err` function and combines any new errors
141    ///
142    /// # Examples
143    /// ```
144    /// use tailcall_valid::{Valid, Validator};
145    /// let valid = Valid::<i32, &str, ()>::succeed(1);
146    /// let result = valid.fold(
147    ///     |n| Valid::succeed(n + 1),
148    ///     || Valid::succeed(0)
149    /// );
150    /// assert_eq!(result, Valid::succeed(2));
151    /// ```
152    fn fold<A1>(
153        self,
154        ok: impl FnOnce(A) -> Valid<A1, E, T>,
155        err: impl FnOnce() -> Valid<A1, E, T>,
156    ) -> Valid<A1, E, T> {
157        match self.to_result() {
158            Ok(a) => ok(a),
159            Err(e) => Valid::<A1, E, T>(Err(e)).and(err()),
160        }
161    }
162
163    /// Converts the validation into a Result.
164    fn to_result(self) -> Result<A, Vec<Cause<E, T>>>;
165
166    /// Chains a validation operation by applying a function to a successful value.
167    /// If the original validation failed, the errors are propagated.
168    ///
169    /// # Examples
170    /// ```
171    /// use tailcall_valid::{Valid, Validator};
172    /// let valid = Valid::<i32, &str, ()>::succeed(1);
173    /// let result = valid.and_then(|n| {
174    ///     if n > 0 {
175    ///         Valid::succeed(n * 2)
176    ///     } else {
177    ///         Valid::fail("must be positive")
178    ///     }
179    /// });
180    /// assert_eq!(result, Valid::succeed(2));
181    /// ```
182    fn and_then<B>(self, f: impl FnOnce(A) -> Valid<B, E, T>) -> Valid<B, E, T> {
183        match self.to_result() {
184            Ok(a) => f(a),
185            Err(e) => Valid(Err(e)),
186        }
187    }
188
189    /// Converts a successful validation to `()`.
190    /// Failed validations retain their errors.
191    ///
192    /// # Examples
193    /// ```
194    /// use tailcall_valid::{Valid, Validator};
195    /// let valid = Valid::<i32, &str, ()>::succeed(1);
196    /// assert_eq!(valid.unit(), Valid::succeed(()));
197    /// ```
198    fn unit(self) -> Valid<(), E, T> {
199        self.map(|_| ())
200    }
201
202    /// Wraps a successful value in Some(_).
203    ///
204    /// # Examples
205    /// ```
206    /// use tailcall_valid::{Valid, Validator};
207    /// let valid = Valid::<i32, &str, ()>::succeed(1);
208    /// assert_eq!(valid.some(), Valid::succeed(Some(1)));
209    /// ```
210    fn some(self) -> Valid<Option<A>, E, T> {
211        self.map(Some)
212    }
213
214    /// Maps a successful validation to a constant value.
215    ///
216    /// # Examples
217    /// ```
218    /// use tailcall_valid::{Valid, Validator};
219    /// let valid = Valid::<i32, &str, ()>::succeed(1);
220    /// assert_eq!(valid.map_to("ok"), Valid::succeed("ok"));
221    /// ```
222    fn map_to<B>(self, b: B) -> Valid<B, E, T> {
223        self.map(|_| b)
224    }
225
226    /// Conditionally validates based on a predicate.
227    /// If the predicate returns false, succeeds with ().
228    ///
229    /// # Examples
230    /// ```
231    /// use tailcall_valid::{Valid, Validator};
232    /// let valid = Valid::<(), &str, ()>::fail("error");
233    /// let result = valid.when(|| false);
234    /// assert_eq!(result, Valid::succeed(()));
235    /// ```
236    fn when(self, f: impl FnOnce() -> bool) -> Valid<(), E, T> {
237        if f() {
238            self.unit()
239        } else {
240            Valid::succeed(())
241        }
242    }
243}
244
245impl<A, E, T> Valid<A, E, T> {
246    /// Creates a new failed validation with a single error.
247    ///
248    /// # Examples
249    /// ```
250    /// use tailcall_valid::{Valid, Validator};
251    /// let result: Valid<(), i32, ()> = Valid::fail(1);
252    /// assert!(result.is_fail());
253    /// ```
254    pub fn fail(e: E) -> Valid<A, E, T> {
255        Valid(Err(vec![Cause {
256            error: e,
257            trace: Default::default(),
258        }]))
259    }
260
261    /// Creates a new failed validation with an error and trace context.
262    ///
263    /// # Examples
264    /// ```
265    /// use tailcall_valid::{Valid, Validator};
266    /// let result = Valid::<(), &str, &str>::fail_at("error", "context");
267    /// assert!(result.is_fail());
268    /// ```
269    pub fn fail_at(error: E, trace: T) -> Valid<A, E, T>
270    where
271        E: std::fmt::Debug,
272    {
273        let cause = Cause::new(error).trace(trace);
274        Valid(Err(vec![cause]))
275    }
276
277    /// Creates a new successful validation containing the given value.
278    ///
279    /// # Examples
280    /// ```
281    /// use tailcall_valid::{Valid, Validator};
282    /// let result = Valid::<i32, (), ()>::succeed(42);
283    /// assert!(result.is_succeed());
284    /// ```
285    pub fn succeed(a: A) -> Valid<A, E, T> {
286        Valid(Ok(a))
287    }
288
289    /// Validates each item in an iterator using the provided validation function,
290    /// collecting all errors that occur.
291    ///
292    /// # Examples
293    /// ```
294    /// use tailcall_valid::{Valid, Validator};
295    /// let numbers = vec![1, 2, 3];
296    /// let result = Valid::from_iter(numbers, |n| {
297    ///     if n % 2 == 0 {
298    ///         Valid::<i32, String, ()>::succeed(n * 2)
299    ///     } else {
300    ///         Valid::<i32, String, ()>::fail(format!("{} is odd", n))
301    ///     }
302    /// });
303    /// ```
304    pub fn from_iter<B>(
305        iter: impl IntoIterator<Item = A>,
306        mut f: impl FnMut(A) -> Valid<B, E, T>,
307    ) -> Valid<Vec<B>, E, T> {
308        let mut values: Vec<B> = Vec::new();
309        let mut errors: Vec<Cause<E, T>> = Vec::new();
310        for a in iter.into_iter() {
311            match f(a).to_result() {
312                Ok(b) => values.push(b),
313                Err(err) => errors.extend(err),
314            }
315        }
316
317        if errors.is_empty() {
318            Valid::succeed(values)
319        } else {
320            Valid::from(errors)
321        }
322    }
323
324    /// Creates a new `Valid` from an `Option` value.
325    /// If the option is `None`, creates a failed validation with the provided error.
326    /// If the option is `Some`, creates a successful validation with the contained value.
327    ///
328    /// # Examples
329    /// ```
330    /// use tailcall_valid::{Valid, Validator};
331    /// let some_value = Some(42);
332    /// let result: Valid<i32, &str, ()> = Valid::from_option(some_value, "error");
333    /// assert_eq!(result, Valid::succeed(42));
334    ///
335    /// let none_value: Option<i32> = None;
336    /// let result: Valid<i32, &str, ()> = Valid::from_option(none_value, "error");
337    /// assert!(result.is_fail());
338    /// ```
339    pub fn from_option(option: Option<A>, e: E) -> Valid<A, E, T> {
340        match option {
341            Some(a) => Valid::succeed(a),
342            None => Valid::fail(e),
343        }
344    }
345
346    /// Creates a successful validation containing `None`.
347    ///
348    /// This is useful when you want to explicitly represent the absence of a value
349    /// as a successful validation rather than an error condition.
350    ///
351    /// # Examples
352    /// ```
353    /// use tailcall_valid::Valid;
354    /// let result: Valid<Option<i32>, &str, ()> = Valid::none();
355    /// assert_eq!(result, Valid::succeed(None));
356    /// ```
357    pub fn none() -> Valid<Option<A>, E, T> {
358        Valid::succeed(None)
359    }
360}
361
362impl<A, E, T> From<Cause<E, T>> for Valid<A, E, T> {
363    /// Creates a failed validation from a single `Cause`.
364    ///
365    /// # Examples
366    /// ```
367    /// use tailcall_valid::{Valid, Validator, Cause};
368    /// let cause = Cause::new("error");
369    /// let result: Valid<(), &str, ()> = Valid::from(cause);
370    /// assert!(result.is_fail());
371    /// ```
372    fn from(value: Cause<E, T>) -> Self {
373        Valid(Err(vec![value]))
374    }
375}
376
377impl<A, E, T> From<Vec<Cause<E, T>>> for Valid<A, E, T> {
378    /// Creates a failed validation from a vector of `Cause`s.
379    ///
380    /// # Examples
381    /// ```
382    /// use tailcall_valid::{Valid, Validator, Cause};
383    /// let causes = vec![Cause::new("error1"), Cause::new("error2")];
384    /// let result: Valid<(), &str, ()> = Valid::from(causes);
385    /// assert!(result.is_fail());
386    /// ```
387    fn from(value: Vec<Cause<E, T>>) -> Self {
388        Valid(Err(value))
389    }
390}
391
392impl<A, E, T> Validator<A, E, T> for Valid<A, E, T> {
393    fn to_result(self) -> Result<A, Vec<Cause<E, T>>> {
394        self.0
395    }
396
397    fn is_succeed(&self) -> bool {
398        self.0.is_ok()
399    }
400
401    fn is_fail(&self) -> bool {
402        self.0.is_err()
403    }
404}
405
406/// A type that allows chaining multiple validations together while combining their results.
407///
408/// `Fusion` is particularly useful when you want to accumulate values from multiple
409/// successful validations into a single composite value.
410pub struct Fusion<A, E, T>(Valid<A, E, T>);
411impl<A, E, T> Fusion<A, E, T> {
412    /// Combines this fusion with another validation, using the `Append` trait to
413    /// combine their successful values.
414    ///
415    /// # Examples
416    /// ```
417    /// use tailcall_valid::{Valid, Validator};
418    /// let v1: Valid<Vec<i32>, (), ()> = Valid::succeed(vec![1, 2]);
419    /// let v2: Valid<Vec<i32>, (), ()> = Valid::succeed(vec![3, 4]);
420    /// let fusion = v1.fuse(v2);
421    /// let result = fusion.to_result().unwrap();
422    /// assert_eq!(result, (vec![1, 2], vec![3, 4]));
423    /// ```
424    pub fn fuse<A1>(self, other: Valid<A1, E, T>) -> Fusion<A::Out, E, T>
425    where
426        A: Append<A1>,
427    {
428        Fusion(self.0.zip(other).map(|(a, a1)| a.append(a1)))
429    }
430}
431
432impl<A, E, T> Validator<A, E, T> for Fusion<A, E, T> {
433    fn to_result(self) -> Result<A, Vec<Cause<E, T>>> {
434        self.0.to_result()
435    }
436    fn is_succeed(&self) -> bool {
437        self.0.is_succeed()
438    }
439    fn is_fail(&self) -> bool {
440        self.0.is_fail()
441    }
442}
443
444impl<A, E, T> From<Result<A, Cause<E, T>>> for Valid<A, E, T> {
445    /// Creates a `Valid` from a `Result` containing a single `Cause` as its error type.
446    ///
447    /// # Examples
448    /// ```
449    /// use tailcall_valid::{Valid, Validator, Cause};
450    /// let ok_result: Result<i32, Cause<&str, ()>> = Ok(42);
451    /// let valid = Valid::from(ok_result);
452    /// assert_eq!(valid, Valid::succeed(42));
453    ///
454    /// let err_result: Result<i32, Cause<&str, ()>> = Err(Cause::new("error"));
455    /// let valid = Valid::from(err_result);
456    /// assert!(valid.is_fail());
457    /// ```
458    fn from(value: Result<A, Cause<E, T>>) -> Self {
459        match value {
460            Ok(a) => Valid::succeed(a),
461            Err(e) => Valid(Err(vec![e])),
462        }
463    }
464}
465
466impl<A, E, T> From<Result<A, Vec<Cause<E, T>>>> for Valid<A, E, T> {
467    /// Creates a `Valid` from a `Result` containing multiple `Cause`s as its error type.
468    ///
469    /// # Examples
470    /// ```
471    /// use tailcall_valid::{Valid, Validator, Cause};
472    /// let ok_result: Result<i32, Vec<Cause<&str, ()>>> = Ok(42);
473    /// let valid = Valid::from(ok_result);
474    /// assert_eq!(valid, Valid::succeed(42));
475    ///
476    /// let err_result: Result<i32, Vec<Cause<&str, ()>>> = Err(vec![
477    ///     Cause::new("error1"),
478    ///     Cause::new("error2")
479    /// ]);
480    /// let valid = Valid::from(err_result);
481    /// assert!(valid.is_fail());
482    /// ```
483    fn from(value: Result<A, Vec<Cause<E, T>>>) -> Self {
484        match value {
485            Ok(a) => Valid::succeed(a),
486            Err(e) => Valid(Err(e)),
487        }
488    }
489}
490
491impl<A, E, T> From<Fusion<A, E, T>> for Valid<A, E, T> {
492    /// Converts a `Fusion` back into a `Valid`.
493    ///
494    /// This is typically used at the end of a chain of `fuse` operations
495    /// to convert the final result back into a `Valid`.
496    ///
497    /// # Examples
498    /// ```
499    /// use tailcall_valid::{Valid, Validator};
500    /// let v1: Valid<Vec<i32>, (), ()> = Valid::succeed(vec![1]);
501    /// let v2: Valid<Vec<i32>, (), ()> = Valid::succeed(vec![2]);
502    /// let fusion = v1.fuse(v2);
503    /// let result: Valid<(Vec<i32>, Vec<i32>), (), ()> = Valid::from(fusion);
504    /// assert!(result.is_succeed());
505    /// ```
506    fn from(value: Fusion<A, E, T>) -> Self {
507        Valid(value.to_result())
508    }
509}
510
511impl<A, E, T> Clone for Valid<A, E, T>
512where
513    A: Clone,
514    E: Clone,
515    T: Clone,
516{
517    fn clone(&self) -> Self {
518        Self(self.0.clone())
519    }
520}
521
522#[cfg(test)]
523mod tests {
524    use super::{Cause, Valid, Validator};
525
526    #[test]
527    fn test_ok() {
528        let result = Valid::<i32, (), ()>::succeed(1);
529        assert_eq!(result, Valid::succeed(1));
530    }
531
532    #[test]
533    fn test_fail() {
534        let result = Valid::<(), i32, ()>::fail(1);
535        assert_eq!(result, Valid::fail(1));
536    }
537
538    #[test]
539    fn test_validate_or_both_ok() {
540        let result1 = Valid::<bool, i32, ()>::succeed(true);
541        let result2 = Valid::<u8, i32, ()>::succeed(3);
542
543        assert_eq!(result1.and(result2), Valid::succeed(3u8));
544    }
545
546    #[test]
547    fn test_validate_or_first_fail() {
548        let result1 = Valid::<bool, i32, ()>::fail(-1);
549        let result2 = Valid::<u8, i32, ()>::succeed(3);
550
551        assert_eq!(result1.and(result2), Valid::fail(-1));
552    }
553
554    #[test]
555    fn test_validate_or_second_fail() {
556        let result1 = Valid::<bool, i32, ()>::succeed(true);
557        let result2 = Valid::<u8, i32, ()>::fail(-2);
558
559        assert_eq!(result1.and(result2), Valid::fail(-2));
560    }
561
562    #[test]
563    fn test_validate_all() {
564        let input: Vec<i32> = [1, 2, 3].to_vec();
565        let result: Valid<Vec<i32>, i32, ()> = Valid::from_iter(input, |a| Valid::fail(a * 2));
566        assert_eq!(
567            result,
568            Valid::from(vec![Cause::new(2), Cause::new(4), Cause::new(6)])
569        );
570    }
571
572    #[test]
573    fn test_validate_all_ques() {
574        let input: Vec<i32> = [1, 2, 3].to_vec();
575        let result: Valid<Vec<i32>, i32, ()> = Valid::from_iter(input, |a| Valid::fail(a * 2));
576        assert_eq!(
577            result,
578            Valid::from(vec![Cause::new(2), Cause::new(4), Cause::new(6)])
579        );
580    }
581
582    #[test]
583    fn test_ok_ok_cause() {
584        let option: Option<i32> = None;
585        let result: Valid<i32, i32, ()> = Valid::from_option(option, 1);
586        assert_eq!(result, Valid::from(vec![Cause::new(1)]));
587    }
588
589    #[test]
590    fn test_trace() {
591        let result = Valid::<(), i32, String>::fail(1)
592            .trace("A")
593            .trace("B")
594            .trace("C");
595
596        let expected = Valid::from(vec![Cause {
597            error: 1,
598            trace: vec!["C".to_string(), "B".to_string(), "A".to_string()].into(),
599        }]);
600        assert_eq!(result, expected);
601    }
602
603    #[test]
604    fn test_validate_fold_err() {
605        let valid = Valid::<(), i32, ()>::fail(1);
606        let result = valid.fold(
607            |_| Valid::<(), i32, ()>::fail(2),
608            || Valid::<(), i32, ()>::fail(3),
609        );
610        assert_eq!(result, Valid::from(vec![Cause::new(1), Cause::new(3)]));
611    }
612
613    #[test]
614    fn test_validate_fold_ok() {
615        let valid = Valid::<i32, i32, i32>::succeed(1);
616        let result = valid.fold(Valid::<i32, i32, i32>::fail, || {
617            Valid::<i32, i32, i32>::fail(2)
618        });
619        assert_eq!(result, Valid::fail(1));
620    }
621
622    #[test]
623    fn test_to_result() {
624        let result = Valid::<(), i32, i32>::fail(1).to_result().unwrap_err();
625        assert_eq!(result, vec![Cause::new(1)]);
626    }
627
628    #[test]
629    fn test_validate_both_ok() {
630        let result1 = Valid::<bool, i32, i32>::succeed(true);
631        let result2 = Valid::<u8, i32, i32>::succeed(3);
632
633        assert_eq!(result1.zip(result2), Valid::succeed((true, 3u8)));
634    }
635    #[test]
636    fn test_validate_both_first_fail() {
637        let result1 = Valid::<bool, i32, i32>::fail(-1);
638        let result2 = Valid::<u8, i32, i32>::succeed(3);
639
640        assert_eq!(result1.zip(result2), Valid::fail(-1));
641    }
642    #[test]
643    fn test_validate_both_second_fail() {
644        let result1 = Valid::<bool, i32, i32>::succeed(true);
645        let result2 = Valid::<u8, i32, i32>::fail(-2);
646
647        assert_eq!(result1.zip(result2), Valid::fail(-2));
648    }
649
650    #[test]
651    fn test_validate_both_both_fail() {
652        let result1 = Valid::<bool, i32, i32>::fail(-1);
653        let result2 = Valid::<u8, i32, i32>::fail(-2);
654
655        assert_eq!(
656            result1.zip(result2),
657            Valid::from(vec![Cause::new(-1), Cause::new(-2)])
658        );
659    }
660
661    #[test]
662    fn test_and_then_success() {
663        let result = Valid::<i32, i32, i32>::succeed(1).and_then(|a| Valid::succeed(a + 1));
664        assert_eq!(result, Valid::succeed(2));
665    }
666
667    #[test]
668    fn test_and_then_fail() {
669        let result =
670            Valid::<i32, i32, i32>::succeed(1).and_then(|a| Valid::<i32, i32, i32>::fail(a + 1));
671        assert_eq!(result, Valid::fail(2));
672    }
673
674    #[test]
675    fn test_foreach_succeed() {
676        let mut a = 0;
677        let result = Valid::<i32, i32, i32>::succeed(1).foreach(|v| a = v);
678        assert_eq!(result, Valid::succeed(1));
679        assert_eq!(a, 1);
680    }
681
682    #[test]
683    fn test_foreach_fail() {
684        let mut a = 0;
685        let result = Valid::<i32, i32, i32>::fail(1).foreach(|v| a = v);
686        assert_eq!(result, Valid::fail(1));
687        assert_eq!(a, 0);
688    }
689
690    #[test]
691    fn test_trace_owned_referenced() {
692        let trace_value = "inner".to_string();
693
694        let valid: Valid<((), ()), &str, String> = Valid::fail("fail")
695            .trace(&trace_value)
696            .zip(Valid::fail("fail 2").trace(trace_value))
697            .trace("outer");
698
699        let causes = valid.to_result().unwrap_err();
700
701        assert_eq!(causes.len(), 2);
702        assert_eq!(causes[0].to_string(), "[outer, inner] fail");
703        assert_eq!(causes[1].to_string(), "[outer, inner] fail 2");
704    }
705    #[test]
706    fn test_from_result_vec_causes_ok() {
707        let ok_result: Result<i32, Vec<Cause<&str, ()>>> = Ok(42);
708        let valid = Valid::from(ok_result);
709        assert_eq!(valid, Valid::succeed(42));
710    }
711
712    #[test]
713    fn test_from_result_vec_causes_err() {
714        let err_result: Result<i32, Vec<Cause<&str, ()>>> =
715            Err(vec![Cause::new("error1"), Cause::new("error2")]);
716        let valid = Valid::from(err_result);
717        let expected = Valid::from(vec![Cause::new("error1"), Cause::new("error2")]);
718        assert_eq!(valid, expected);
719        assert!(valid.is_fail());
720    }
721
722    #[test]
723    fn test_from_result_vec_causes_empty_err() {
724        let err_result: Result<i32, Vec<Cause<&str, ()>>> = Err(vec![]);
725        let valid = Valid::from(err_result);
726        let expected = Valid::from(vec![]);
727        assert_eq!(valid, expected);
728        assert!(valid.is_fail());
729    }
730}