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}