typed_eval/
errors.rs

1use crate::Error;
2use std::ops::Deref;
3
4pub type Result<T> = std::result::Result<T, Errors>;
5
6#[derive(Debug, PartialEq)]
7pub struct Errors(Vec<Error>);
8
9impl Deref for Errors {
10    type Target = [Error];
11
12    fn deref(&self) -> &Self::Target {
13        &self.0
14    }
15}
16
17impl From<Error> for Errors {
18    fn from(value: Error) -> Self {
19        Self(vec![value])
20    }
21}
22
23impl From<Vec<Error>> for Errors {
24    fn from(value: Vec<Error>) -> Self {
25        Self(value)
26    }
27}
28
29impl Errors {
30    pub fn empty() -> Errors {
31        Self(vec![])
32    }
33
34    pub fn combine(
35        a: impl Into<Option<Errors>>,
36        b: impl Into<Option<Errors>>,
37    ) -> Option<Errors> {
38        match (a.into(), b.into()) {
39            (None, None) => None,
40            (Some(e), None) | (None, Some(e)) => Some(e),
41            (Some(mut e1), Some(e2)) => {
42                e1.0.extend(e2.0);
43                Some(e1)
44            }
45        }
46    }
47
48    pub fn append(&mut self, errors: impl Into<Errors>) {
49        self.0.append(&mut errors.into().0);
50    }
51}
52
53/// This trait is implemented for tuples and vectors, so it is possible to check multiple results,
54/// and collect all errors before bailing out.
55/// This will return errors from both compute_a and compute_b, if any:
56/// `let (a, b) = (compute_a(), compute_b()).all_ok()?`
57pub trait CombineResults {
58    type Output;
59    fn all_ok(self) -> Result<Self::Output>;
60}
61
62impl<T, E> CombineResults for Vec<std::result::Result<T, E>>
63where
64    E: Into<Errors>,
65{
66    type Output = Vec<T>;
67
68    fn all_ok(self) -> Result<Self::Output> {
69        let mut values = Vec::with_capacity(self.len());
70        let mut errors: Option<Errors> = None;
71
72        for res in self {
73            match res {
74                Ok(v) => values.push(v),
75                Err(e) => {
76                    errors = Errors::combine(errors, e.into());
77                }
78            }
79        }
80
81        if let Some(e) = errors {
82            Err(e)
83        } else {
84            Ok(values)
85        }
86    }
87}
88
89macro_rules! impl_combine_results {
90    ( $( $name:ident, $err:ident ),+ ) => {
91        impl< $( $name, $err ),+ > CombineResults for ( $( std::result::Result<$name, $err> ),+ )
92        where $( $err: Into<Errors>, )+
93        {
94            type Output = ( $( $name ),+ );
95
96            #[allow(non_snake_case)]
97            fn all_ok(self) -> Result<Self::Output> {
98                let ( $( $name ),+ ) = self;
99
100                let mut errors: Option<Errors> = None;
101
102                $(
103                    let $name = match $name {
104                        Ok(v) => Some(v),
105                        Err(e) => {
106                            errors = Errors::combine(errors, e.into());
107                            None
108                        }
109                    };
110                )+
111
112                if let Some(e) = errors {
113                    Err(e)
114                } else {
115                    Ok(( $( $name.unwrap() ),+ ))
116                }
117            }
118        }
119    };
120}
121impl_combine_results!(A, EA, B, EB);
122impl_combine_results!(A, EA, B, EB, C, EC);
123impl_combine_results!(A, EA, B, EB, C, EC, D, ED);
124impl_combine_results!(A, EA, B, EB, C, EC, D, ED, E, EE);
125impl_combine_results!(A, EA, B, EB, C, EC, D, ED, E, EE, F, EF);