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 add<M: ToString>(&mut self, message: M) {
27 self.messages.push(message.to_string());
28 }
29
30 pub fn addf(&mut self, args: fmt::Arguments) {
32 self.messages.push(format!("{args}"));
33 }
34
35 pub fn merge(&mut self, other: Self) {
37 self.messages.extend(other.messages);
38 for (key, child_errors) in other.children {
39 self.children.entry(key).or_default().merge(child_errors);
40 }
41 }
42
43 pub fn merge_for<K: ToString>(&mut self, key: K, other: Self) {
45 self.children
46 .entry(key.to_string())
47 .or_default()
48 .merge(other);
49 }
50
51 #[must_use]
53 pub fn is_empty(&self) -> bool {
54 self.messages.is_empty() && self.children.is_empty()
55 }
56
57 #[must_use]
59 pub fn messages(&self) -> &[String] {
60 &self.messages
61 }
62
63 #[must_use]
65 pub const fn children(&self) -> &HashMap<String, Self> {
66 &self.children
67 }
68
69 #[must_use]
71 pub fn flatten_ref(&self) -> Vec<(String, String)> {
72 let mut result = Vec::new();
73 self.flatten_helper_ref(String::new(), &mut result);
74 result
75 }
76
77 fn flatten_helper_ref(&self, prefix: String, result: &mut Vec<(String, String)>) {
78 for msg in &self.messages {
80 result.push((prefix.clone(), msg.clone()));
81 }
82 for (key, child) in &self.children {
84 let new_prefix = if prefix.is_empty() {
85 key.clone()
86 } else {
87 format!("{prefix}.{key}")
88 };
89 child.flatten_helper_ref(new_prefix, result);
90 }
91 }
92
93 pub fn result(self) -> Result<(), Self> {
95 if self.is_empty() { Ok(()) } else { Err(self) }
96 }
97}
98
99#[macro_export]
100macro_rules! err {
101 ($errs:expr, $($arg:tt)*) => {{
102 $errs.addf(format_args!($($arg)*));
103 }};
104}
105
106impl fmt::Display for ErrorTree {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 for (key, msg) in self.flatten_ref() {
109 if key.is_empty() {
110 writeln!(f, "{msg}")?;
111 } else {
112 writeln!(f, "{key}: {msg}")?;
113 }
114 }
115
116 Ok(())
117 }
118}
119
120impl From<&str> for ErrorTree {
121 fn from(err: &str) -> Self {
122 let mut tree = Self::new();
123 tree.add(err.to_string());
124
125 tree
126 }
127}
128
129impl From<String> for ErrorTree {
130 fn from(s: String) -> Self {
131 let mut tree = Self::new();
132 tree.add(s);
133
134 tree
135 }
136}
137
138#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_empty_errors() {
148 let errs = ErrorTree::new();
149 assert!(errs.is_empty());
150 assert_eq!(errs.result(), Ok(()));
151 }
152
153 #[test]
154 fn test_add_and_merge() {
155 let mut errs = ErrorTree::new();
156 errs.add("top-level error");
157
158 let mut child_errs = ErrorTree::new();
159 child_errs.add("child error 1");
160 child_errs.add("child error 2");
161 errs.merge_for("field", ErrorTree::from("field error"));
162 errs.merge_for("nested", child_errs);
163
164 assert_eq!(errs.messages().len(), 1);
166 assert!(errs.children().contains_key("field"));
167 assert!(errs.children().contains_key("nested"));
168
169 let flat = errs.flatten_ref();
171 assert_eq!(flat.len(), 4);
172 }
173}