codama_errors/
combine_errors.rs

1use crate::CodamaError;
2
3pub trait CombineErrors {
4    fn combine(&mut self, other: Self);
5}
6
7impl CombineErrors for CodamaError {
8    fn combine(&mut self, other: Self) {
9        if let (CodamaError::Compilation(this), CodamaError::Compilation(that)) = (self, other) {
10            this.combine(that)
11        }
12    }
13}
14
15impl CombineErrors for syn::Error {
16    fn combine(&mut self, other: Self) {
17        syn::Error::combine(self, other)
18    }
19}
20
21pub trait IteratorCombineErrors<T, E>: Iterator<Item = Result<T, E>>
22where
23    E: std::error::Error + CombineErrors,
24{
25    fn collect_and_combine_errors(self) -> Result<Vec<T>, E>
26    where
27        Self: Sized,
28    {
29        #[allow(clippy::manual_try_fold)]
30        // We can't use try_fold here because it will short-circuit on the first error.
31        self.fold(Ok(Vec::new()), |acc, result| match (acc, result) {
32            (Ok(mut acc_vec), Ok(parsed)) => {
33                acc_vec.push(parsed);
34                Ok(acc_vec)
35            }
36            (Err(mut acc_err), Err(err)) => {
37                acc_err.combine(err);
38                Err(acc_err)
39            }
40            (Err(acc_err), _) => Err(acc_err),
41            (_, Err(err)) => Err(err),
42        })
43    }
44}
45
46impl<I, T, E> IteratorCombineErrors<T, E> for I
47where
48    I: Iterator<Item = Result<T, E>>,
49    E: std::error::Error + CombineErrors,
50{
51}
52
53/// Combine multiple results into a single result by combining errors.
54/// Note we could use recursion here but the tuple would be nested.
55/// E.g. (a, (b, c)) instead of (a, b, c).
56#[macro_export]
57macro_rules! combine_errors {
58    // Base case: 1 result.
59    ($result:expr) => {
60        $result
61    };
62
63    // 2 results.
64    ($first:expr, $second:expr $(,)?) => {{
65        match ($first, $second) {
66            (Ok(value1), Ok(value2)) => Ok((value1, value2)),
67            (Err(err1), Err(err2)) => {
68                let mut combined = err1;
69                codama_errors::CombineErrors::combine(&mut combined, err2);
70                Err(combined)
71            }
72            (Err(err), _) => Err(err),
73            (_, Err(err)) => Err(err),
74        }
75    }};
76
77    // 3 results.
78    ($first:expr, $second:expr, $third:expr $(,)?) => {{
79        match ($first, combine_errors!($second, $third)) {
80            (Ok(value1), Ok((value2, value3))) => Ok((value1, value2, value3)),
81            (Err(err1), Err(err2)) => {
82                let mut combined = err1;
83                codama_errors::CombineErrors::combine(&mut combined, err2);
84                Err(combined)
85            }
86            (Err(err), _) => Err(err),
87            (_, Err(err)) => Err(err),
88        }
89    }};
90}