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}