error_accumulator/
error.rs1use std::{error::Error, fmt};
4
5use crate::path::SourcePath;
6
7#[derive(Debug, Default)]
9pub struct AccumulatedError {
10 errors: Vec<(SourcePath, Box<dyn Error + Send + Sync + 'static>)>,
11}
12
13impl AccumulatedError {
14 pub fn get_by_type<E>(&self) -> impl Iterator<Item = (&SourcePath, &E)>
18 where
19 E: Error + Send + Sync + 'static,
20 {
21 self.errors
22 .iter()
23 .filter_map(|(path, stored)| stored.downcast_ref().map(|typed| (path, typed)))
24 }
25
26 pub fn get_by_path(
30 &self,
31 path: &SourcePath,
32 ) -> impl Iterator<Item = &Box<dyn Error + Send + Sync>> {
33 self.errors
34 .iter()
35 .filter_map(move |(error_path, stored)| (error_path == path).then_some(stored))
36 }
37
38 pub fn len(&self) -> usize {
40 self.errors.len()
41 }
42
43 pub fn is_empty(&self) -> bool {
45 self.errors.is_empty()
46 }
47
48 pub(crate) fn append<E>(&mut self, path: SourcePath, error: E)
49 where
50 E: Error + Send + Sync + 'static,
51 {
52 self.errors.push((path, Box::new(error)));
53 }
54
55 pub(crate) fn merge(&mut self, other: AccumulatedError) {
56 self.errors.extend(other.errors);
57 }
58}
59
60impl fmt::Display for AccumulatedError {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 writeln!(f, "Accumulated errors:")?;
63 for (path, error) in &self.errors {
64 writeln!(f, "- {path}: {error}")?;
65 }
66 Ok(())
67 }
68}
69
70impl Error for AccumulatedError {}
71
72#[cfg(test)]
73mod tests {
74 use std::io;
75
76 use super::*;
77 use crate::{path::PathSegment, test_util::n};
78
79 #[test]
80 fn should_include_path_in_display() {
81 let path1 = SourcePath::new().join(PathSegment::Field(n("foo")));
82 let path2 = SourcePath::new().join(PathSegment::Array {
83 name: n("bar"),
84 index: 2,
85 });
86 let mut error = AccumulatedError::default();
87 error.append(
88 path1.clone(),
89 io::Error::new(io::ErrorKind::Interrupted, "error1"),
90 );
91 error.append(
92 path2.clone(),
93 io::Error::new(io::ErrorKind::AlreadyExists, "error2"),
94 );
95
96 let display = error.to_string();
97 dbg!(&display);
98
99 assert!(display.contains(&path1.to_string()));
100 assert!(display.contains(&path2.to_string()));
101 }
102}