errorvec/
errorvec.rs

1use std::fmt;
2use std::ops::{Deref, DerefMut};
3
4/// A newtype wrapper around `Vec<E>` aimed at supporting multi-error scenarios.
5///
6/// # `Display`
7///
8/// [ErrorVec] implements [std::fmt::Display] by prepending each error's display with a count:
9///
10/// ```
11/// use errorvec::ErrorVec;
12///
13/// let ev: ErrorVec<&str> = ["whoops", "something borked", "ouch!"].into_iter().collect();
14/// let expected_display = r#"
15/// [error 1 of 3] whoops
16///
17/// [error 2 of 3] something borked
18///
19/// [error 3 of 3] ouch!
20/// "#.trim_start();
21///
22/// assert_eq!(expected_display, &ev.to_string());
23/// ```
24///
25/// # `Vec` deref
26///
27/// [ErrorVec] implements [Deref] and [DerefMut] for `Target = Vec<E>`, exposing all [Vec] methods
28/// directly:
29///
30/// ```
31/// use errorvec::ErrorVec;
32///
33/// let mut ev = ErrorVec::default();
34/// ev.push(42);
35/// ev.push(17);
36/// assert_eq!(&[42, 17], ev.as_slice());
37/// assert_eq!(Some(17), ev.pop());
38/// assert_eq!(1, ev.len());
39/// assert_eq!(Some(42), ev.pop());
40/// assert!(ev.is_empty());
41/// ```
42///
43/// # `ResultIterator` usage
44///
45/// A common usage is via
46/// [ResultIterator::into_errorvec_result](crate::ResultIterator::into_errorvec_result) for gathering
47/// all errors in an [Iterator] over [Result] values.
48///
49/// # Empty `ErrorVec`
50///
51/// An [ErrorVec] containing no values (ie `ErrorVec::is_empty() == true`) typically does not
52/// represent an error, and the [ErrorVec::into_result] and [ErrorVec::into_result_with] are often
53/// useful in this case:
54///
55/// ```
56/// use errorvec::ErrorVec;
57///
58/// let ev: ErrorVec<()> = ErrorVec::default();
59/// assert!(ev.into_result().is_ok());
60/// ```
61///
62/// # Example - Gathering errors with `take_error` and `into_result_with`
63///
64/// For scenarios where [ResultIterator](crate::ResultIterator) isn't
65/// useful, the [ErrorVec::take_error] and [ErrorVec::into_result_with]
66/// methods may be useful.
67///
68/// ```
69/// use std::path::Path;
70/// use errorvec::ErrorVec;
71///
72/// /// Return the string contents of all the paths listed in the `manifest_file`, reporting all
73/// /// errors encountered.
74/// fn read_manifest_files(manifest_file: &Path) -> Result<Vec<String>, ErrorVec<std::io::Error>> {
75///     use std::fs::read_to_string;
76///
77///     let mut contents = vec![];
78///     let mut errs = ErrorVec::default();
79///
80///     if let Some(manifest) = errs.take_error(read_to_string(manifest_file)) {
81///         for line in manifest.lines() {
82///             let path = &Path::new(line.trim_end());
83///             if let Some(content) = errs.take_error(read_to_string(path)) {
84///                 contents.push(content)
85///             }
86///         }
87///     }
88///
89///     errs.into_result_with(contents)
90/// }
91/// ```
92#[derive(Debug, derive_more::From, derive_more::Into)]
93pub struct ErrorVec<E>(Vec<E>);
94
95impl<E> ErrorVec<E> {
96    /// If `self.is_empty()`, signifying no errors, `Ok(())`, else, `Err(self)`.
97    pub fn into_result(self) -> Result<(), Self> {
98        self.into_result_with(())
99    }
100
101    /// If `self.is_empty()`, signifying no errors, `Ok(value)`, else, `Err(self)`.
102    pub fn into_result_with<T>(self, value: T) -> Result<T, Self> {
103        if self.is_empty() {
104            Ok(value)
105        } else {
106            Err(self)
107        }
108    }
109
110    /// Collect the error from a result, if present, otherwise return the `Ok` value.
111    pub fn take_error<T>(&mut self, r: Result<T, E>) -> Option<T> {
112        match r {
113            Ok(x) => Some(x),
114            Err(e) => {
115                self.push(e);
116                None
117            }
118        }
119    }
120}
121
122impl<E> std::error::Error for ErrorVec<E> where E: fmt::Display + fmt::Debug {}
123
124impl<E> Default for ErrorVec<E> {
125    fn default() -> Self {
126        ErrorVec(vec![])
127    }
128}
129
130impl<E> Deref for ErrorVec<E> {
131    type Target = Vec<E>;
132
133    fn deref(&self) -> &Self::Target {
134        &self.0
135    }
136}
137
138impl<E> DerefMut for ErrorVec<E> {
139    fn deref_mut(&mut self) -> &mut Self::Target {
140        &mut self.0
141    }
142}
143
144impl<E> FromIterator<E> for ErrorVec<E> {
145    fn from_iter<I>(iter: I) -> Self
146    where
147        I: IntoIterator<Item = E>,
148    {
149        ErrorVec(iter.into_iter().collect())
150    }
151}
152
153impl<E> IntoIterator for ErrorVec<E> {
154    type Item = E;
155    type IntoIter = <Vec<E> as IntoIterator>::IntoIter;
156
157    fn into_iter(self) -> Self::IntoIter {
158        self.0.into_iter()
159    }
160}
161
162impl<E> fmt::Display for ErrorVec<E>
163where
164    E: fmt::Display,
165{
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        let total = self.0.len();
168        for (i, e) in self.0.iter().enumerate() {
169            let edisp = e.to_string();
170            writeln!(f, "[error {} of {}] {}", i + 1, total, edisp.trim_end())?;
171            if i + 1 < total {
172                writeln!(f)?;
173            }
174        }
175        Ok(())
176    }
177}