1#![doc = include_str!("../README.md")]
2
3use std::error::Error as StdError;
4use std::fmt;
5
6use derive_more::{Deref, DerefMut};
7
8pub type ErrorCollection = Errors;
12
13#[derive(Debug, Default, Deref, DerefMut)]
103pub struct Errors(pub Vec<anyhow::Error>);
104
105impl Errors {
106 pub fn new() -> Self {
108 Self::default()
109 }
110
111 pub fn push(&mut self, err: impl Into<anyhow::Error>) {
116 self.0.push(err.into());
117 }
118
119 pub fn append(&mut self, err: impl Into<Self>) {
123 self.0.append(&mut err.into().0);
124 }
125
126 pub fn collect<T, E>(&mut self, result: Result<T, E>) -> Option<T>
128 where
129 E: Into<anyhow::Error>,
130 {
131 match result {
132 Ok(value) => Some(value),
133 Err(err) => {
134 self.append(err.into());
136 None
137 }
138 }
139 }
140
141 pub fn into_vec(self) -> Vec<anyhow::Error> {
143 self.0
144 }
145
146 pub fn as_result(mut self) -> anyhow::Result<()> {
148 match self.len() {
149 0 => Ok(()),
150 1 => Err(self.pop().unwrap()),
151 _ => Err(self.into()),
152 }
153 }
154}
155
156const PADDING: usize = 3;
157
158impl fmt::Display for Errors {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 format_collection(f, self, 0)
161 }
162}
163
164fn format_collection(f: &mut fmt::Formatter<'_>, errors: &Errors, indent: usize) -> fmt::Result {
166 if errors.is_empty() {
167 writeln!(f, "none")
168 } else if errors.len() == 1 {
169 format_error(f, &errors[0], indent)
170 } else {
171 writeln!(f, "{} errors:", errors.len())?;
172 for (idx, error) in errors.iter().enumerate() {
173 write!(f, "{}{}. ", spaces(indent + PADDING), idx + 1)?;
174 match error.downcast_ref::<Errors>() {
175 None => format_error(f, error, indent + PADDING)?,
176 Some(errors) => format_collection(f, errors, indent + PADDING)?,
177 }
178 }
179 Ok(())
180 }
181}
182
183fn format_error(f: &mut fmt::Formatter<'_>, error: &anyhow::Error, indent: usize) -> fmt::Result {
185 let padding = spaces(indent + PADDING);
186 let error_string = if f.alternate() {
187 format!("{:#}", error)
188 } else {
189 format!("{}", error)
190 };
191 for (idx, line) in error_string.split('\n').enumerate() {
192 let padding = if idx == 0 { "" } else { padding };
193 writeln!(f, "{padding}{line}")?;
194 }
195 Ok(())
196}
197
198fn spaces(indent: usize) -> &'static str {
200 &" "[..indent.min(32)]
201}
202
203impl StdError for Errors {}
204
205impl From<&str> for Errors {
206 fn from(value: &str) -> Self {
207 Self(vec![anyhow::anyhow!("{value}")])
208 }
209}
210
211impl From<String> for Errors {
212 fn from(value: String) -> Self {
213 Self(vec![anyhow::anyhow!(value)])
214 }
215}
216
217impl<T> From<Option<T>> for Errors
218where
219 T: Into<anyhow::Error>,
220{
221 fn from(result: Option<T>) -> Self {
222 match result {
223 Some(err) => err.into().into(),
224 None => Self::default(),
225 }
226 }
227}
228
229impl<T, E> From<Result<T, E>> for Errors
230where
231 E: Into<anyhow::Error>,
232{
233 fn from(result: Result<T, E>) -> Self {
234 match result {
235 Ok(_) => Self::default(),
236 Err(err) => err.into().into(),
237 }
238 }
239}
240
241impl From<Vec<anyhow::Error>> for Errors {
242 fn from(errors: Vec<anyhow::Error>) -> Self {
243 Self(errors)
244 }
245}
246
247impl From<anyhow::Error> for Errors {
248 fn from(error: anyhow::Error) -> Self {
249 match error.downcast::<Self>() {
250 Ok(errors) => errors,
251 Err(error) => Self(vec![error]),
252 }
253 }
254}
255
256impl<T> From<Errors> for anyhow::Result<T>
257where
258 T: Default,
259{
260 fn from(mut value: Errors) -> Self {
261 match value.len() {
262 0 => Ok(T::default()),
263 1 => Err(value.pop().unwrap()),
264 _ => Err(value.into()),
265 }
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use std::io;
272
273 use anyhow::anyhow;
274
275 use super::*;
276
277 #[test]
278 fn push() {
279 let mut nested = Errors::new();
280 nested.push(anyhow!("Generic error 1"));
281 nested.push(anyhow!("Generic error 2"));
282 nested.push(anyhow!("Generic error 3"));
283
284 let mut errors = Errors::new();
285 errors.push(nested);
286 errors.push(anyhow!("Generic error 4"));
287 errors.push(io::Error::from_raw_os_error(22));
288
289 assert_eq!(errors.len(), 3);
290 }
291
292 #[test]
293 fn append() {
294 let mut nested = Errors::new();
295 nested.append(vec![anyhow!("Generic error 1"), anyhow!("Generic error 2")]);
296
297 let mut errors = Errors::new();
298 errors.append(nested);
299 errors.append(anyhow!("Generic error 3"));
300
301 assert_eq!(errors.len(), 3);
302 }
303
304 #[test]
305 fn collect() {
306 let mut errors = Errors::new();
307
308 let result: Result<(), anyhow::Error> = Ok(());
309 assert_eq!(errors.collect(result), Some(()));
310
311 let result: Result<(), anyhow::Error> = Err(anyhow!("Generic error 1"));
312 assert_eq!(errors.collect(result), None);
313
314 assert_eq!(errors.len(), 1);
315 }
316
317 #[test]
318 fn collect_nested() {
319 let mut nested = Errors::new();
320 nested.push(anyhow!("Generic error 1"));
321 nested.push(anyhow!("Generic error 2"));
322 nested.push(anyhow!("Generic error 3"));
323
324 let mut errors = Errors::new();
325
326 let result: Result<(), Errors> = Err(nested);
327 assert_eq!(errors.collect(result), None);
328
329 assert_eq!(errors.len(), 3);
330 }
331
332 #[test]
333 fn fmt() {
334 let mut child = Errors::new();
335 child.push(anyhow!("Generic error 2"));
336 child.push(anyhow!("Generic error 3\nnew line"));
337 child.push(Errors(vec![anyhow!("Generic error 4")]));
338
339 let mut parent = Errors::new();
340 parent.push(child);
341 parent.push(io::Error::from_raw_os_error(1));
342
343 let mut errors = Errors::new();
344 errors.push(anyhow!("Generic error 1\nnew line"));
345 errors.push(parent);
346
347 assert_eq!(
348 format!("{errors:#}"),
349 "2 errors:
350 1. Generic error 1
351 new line
352 2. 2 errors:
353 1. 3 errors:
354 1. Generic error 2
355 2. Generic error 3
356 new line
357 3. Generic error 4
358 2. Operation not permitted (os error 1)\n"
359 .replace("\n ", "\n")
360 );
361 }
362}