1use serde::{Deserialize, Serialize};
2use std::{collections::HashMap, fmt};
3
4#[derive(Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
10pub struct ErrorTree {
11 messages: Vec<String>,
13
14 children: HashMap<String, Self>,
16}
17
18impl ErrorTree {
19 #[must_use]
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn collect<I>(iter: I) -> Result<(), Self>
27 where
28 I: IntoIterator<Item = Result<(), Self>>,
29 {
30 let mut errs = Self::new();
31 for res in iter {
32 if let Err(e) = res {
33 errs.merge(e);
34 }
35 }
36
37 errs.result()
38 }
39
40 pub fn add<M: ToString>(&mut self, message: M) {
42 self.messages.push(message.to_string());
43 }
44
45 pub fn add_result<M: ToString>(&mut self, error: Result<(), M>) {
47 if let Err(e) = error {
48 self.messages.push(e.to_string());
49 }
50 }
51
52 pub fn addf(&mut self, args: fmt::Arguments) {
54 self.messages.push(format!("{args}"));
55 }
56
57 pub fn add_for<K: ToString, M: ToString>(&mut self, key: K, message: M) {
59 self.children
60 .entry(key.to_string())
61 .or_default()
62 .add(message);
63 }
64
65 pub fn merge(&mut self, other: Self) {
67 self.messages.extend(other.messages);
68 for (key, child_errors) in other.children {
69 self.children.entry(key).or_default().merge(child_errors);
70 }
71 }
72
73 pub fn merge_for<K: ToString>(&mut self, key: K, other: Self) {
75 self.children
76 .entry(key.to_string())
77 .or_default()
78 .merge(other);
79 }
80
81 #[must_use]
83 pub fn is_empty(&self) -> bool {
84 self.messages.is_empty() && self.children.is_empty()
85 }
86
87 #[must_use]
89 pub fn messages(&self) -> &[String] {
90 &self.messages
91 }
92
93 #[must_use]
95 pub const fn children(&self) -> &HashMap<String, Self> {
96 &self.children
97 }
98
99 #[must_use]
101 pub fn flatten_ref(&self) -> Vec<(String, String)> {
102 let mut result = Vec::new();
103 self.flatten_helper_ref(String::new(), &mut result);
104 result
105 }
106
107 fn flatten_helper_ref(&self, prefix: String, result: &mut Vec<(String, String)>) {
108 for msg in &self.messages {
110 result.push((prefix.clone(), msg.clone()));
111 }
112 for (key, child) in &self.children {
114 let new_prefix = if prefix.is_empty() {
115 key.clone()
116 } else {
117 format!("{prefix}.{key}")
118 };
119 child.flatten_helper_ref(new_prefix, result);
120 }
121 }
122
123 pub fn result(self) -> Result<(), Self> {
125 if self.is_empty() { Ok(()) } else { Err(self) }
126 }
127}
128
129#[macro_export]
130macro_rules! err {
131 ($errs:expr, $($arg:tt)*) => {{
132 $errs.addf(format_args!($($arg)*));
133 }};
134}
135
136impl fmt::Display for ErrorTree {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 for (key, msg) in self.flatten_ref() {
139 if key.is_empty() {
140 writeln!(f, "{msg}")?;
141 } else {
142 writeln!(f, "{key}: {msg}")?;
143 }
144 }
145
146 Ok(())
147 }
148}
149
150impl From<&str> for ErrorTree {
151 fn from(err: &str) -> Self {
152 let mut tree = Self::new();
153 tree.add(err.to_string());
154
155 tree
156 }
157}
158
159impl From<String> for ErrorTree {
160 fn from(s: String) -> Self {
161 let mut tree = Self::new();
162 tree.add(s);
163
164 tree
165 }
166}
167
168#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_empty_errors() {
178 let errs = ErrorTree::new();
179 assert!(errs.is_empty());
180 assert_eq!(errs.result(), Ok(()));
181 }
182
183 #[test]
184 fn test_add_and_merge() {
185 let mut errs = ErrorTree::new();
186 errs.add("top-level error");
187
188 let mut child_errs = ErrorTree::new();
189 child_errs.add("child error 1");
190 child_errs.add("child error 2");
191 errs.add_for("field", "field error");
192 errs.merge_for("nested", child_errs);
193
194 assert_eq!(errs.messages().len(), 1);
196 assert!(errs.children().contains_key("field") || errs.children().contains_key("nested"));
197
198 let flat = errs.flatten_ref();
200 assert_eq!(flat.len(), 4);
201 }
202}