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