1use itertools::Itertools;
9
10#[derive(Debug)]
15pub enum ErrorTree<L, E> {
16 Leaf(E),
18 Edge(L, Box<ErrorTree<L, E>>),
20 Vec(Vec<ErrorTree<L, E>>),
22}
23
24impl<L, E> ErrorTree<L, E> {
25 pub fn leaf(error: E) -> Self {
36 Self::Leaf(error)
37 }
38}
39
40#[derive(Debug)]
42pub struct FlatError<L, E> {
43 pub path: Vec<L>,
45 pub error: E,
47}
48
49impl<L, E> ErrorTree<L, E>
50where
51 L: Clone,
52{
53 pub fn flatten_tree(self) -> Vec<FlatError<L, E>> {
96 match self {
97 ErrorTree::Leaf(error) => vec![FlatError {
98 path: Vec::new(),
99 error,
100 }],
101 ErrorTree::Edge(label, tree) => {
102 let mut flat_errors = tree.flatten_tree();
103 for flat in &mut flat_errors {
104 flat.path.push(label.clone());
105 }
106 flat_errors
107 }
108 ErrorTree::Vec(errors) => errors
109 .into_iter()
110 .flat_map(|tree| tree.flatten_tree())
111 .collect_vec(),
112 }
113 }
114}
115
116pub trait IntoErrorTree<L, E> {
118 fn with_label(self, label: L) -> ErrorTree<L, E>;
128}
129
130impl<L, E> IntoErrorTree<L, E> for E
131where
132 E: Into<ErrorTree<L, E>>,
133{
134 fn with_label(self, label: L) -> ErrorTree<L, E> {
135 ErrorTree::Edge(label, Box::new(self.into()))
136 }
137}
138
139impl<L, E> IntoErrorTree<L, E> for ErrorTree<L, E> {
140 fn with_label(self, label: L) -> ErrorTree<L, E> {
141 ErrorTree::Edge(label, Box::new(self))
142 }
143}
144
145impl<L, E> From<Vec<ErrorTree<L, E>>> for ErrorTree<L, E> {
146 fn from(subtrees: Vec<ErrorTree<L, E>>) -> Self {
147 ErrorTree::Vec(subtrees)
148 }
149}
150
151impl<L, E> From<Vec<E>> for ErrorTree<L, E>
152where
153 E: IntoErrorTree<L, E>,
154 ErrorTree<L, E>: From<E>,
155{
156 fn from(errors: Vec<E>) -> Self {
157 ErrorTree::Vec(errors.into_iter().map(|x| x.into()).collect_vec())
158 }
159}
160
161pub trait IntoResult<T, E> {
163 fn into_result(self) -> Result<T, E>;
211}
212
213impl<T, IE, E> IntoResult<T, E> for (T, Vec<IE>)
214where
215 Vec<IE>: Into<E>,
216{
217 fn into_result(self) -> Result<T, E> {
218 let (oks, errs) = self;
219 if errs.is_empty() {
220 Ok(oks)
221 } else {
222 Err(errs.into())
223 }
224 }
225}
226
227impl<IE, E> IntoResult<(), E> for Vec<IE>
228where
229 Vec<IE>: Into<E>,
230{
231 fn into_result(self) -> Result<(), E> {
232 if self.is_empty() {
233 Ok(())
234 } else {
235 Err(self.into())
236 }
237 }
238}
239
240pub trait LabelResult<T, L, E> {
242 fn label_error(self, label: L) -> Result<T, ErrorTree<L, E>>;
254}
255
256impl<T, L, E> LabelResult<T, L, E> for Result<T, E>
257where
258 ErrorTree<L, E>: From<E>,
259{
260 fn label_error(self, label: L) -> Result<T, ErrorTree<L, E>> {
261 self.map_err(|e| {
262 let tree: ErrorTree<L, E> = e.into();
263 tree.with_label(label)
264 })
265 }
266}
267
268impl<T, L, E> LabelResult<T, L, E> for Result<T, ErrorTree<L, E>> {
269 fn label_error(self, label: L) -> Result<T, ErrorTree<L, E>> {
270 self.map_err(|tree| tree.with_label(label))
271 }
272}
273
274pub trait FlattenResultErrors<T, L, E> {
275 fn flatten_results(self) -> Result<T, Vec<FlatError<L, E>>>;
276}
277
278impl<T, L, E> FlattenResultErrors<T, L, E> for Result<T, ErrorTree<L, E>>
279where
280 L: Clone,
281{
282 fn flatten_results(self) -> Result<T, Vec<FlatError<L, E>>> {
283 self.map_err(|tree| tree.flatten_tree())
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use itertools::Itertools;
290
291 use super::*;
292
293 fn faulty(error: &str) -> Result<(), Error> {
294 Err(Error(error.into()))
295 }
296
297 #[test]
298 fn can_build_tree_from_vec_of_results() {
299 let result_1 = faulty("error1").map_err(|e| e.with_label("label1"));
300 let result_2 = faulty("error2").map_err(|e| e.with_label("label2"));
301
302 let (_, errors): (Vec<_>, Vec<_>) = vec![result_1, result_2].into_iter().partition_result();
303
304 let tree: ErrorTree<&'static str, Error> = errors.into();
305 let tree = tree.with_label("parent_label");
306
307 let flat_errors = tree.flatten_tree();
308
309 assert!(
310 matches!(
311 &flat_errors[..],
312 [
313 FlatError {
314 path: path1,
315 error: Error(error1),
316 },
317 FlatError {
318 path: path2,
319 error: Error(error2),
320 },
321 ]
322 if path1 == &vec!["label1", "parent_label"]
323 && path2 == &vec!["label2", "parent_label"]
324 && error1 == "error1"
325 && error2 == "error2"
326 ),
327 "unexpected: {:#?}",
328 flat_errors
329 );
330 }
331
332 #[test]
333 fn can_call_into_result_from_vec_of_results() {
334 let result_1 = faulty("error1").map_err(|e| e.with_label("label1"));
335 let result_2 = faulty("error2").map_err(|e| e.with_label("label2"));
336
337 let result: Result<Vec<()>, ErrorTree<_, _>> = vec![result_1, result_2]
338 .into_iter()
339 .partition_result()
340 .into_result();
341
342 let flat_result = result.map_err(|e| e.flatten_tree());
343
344 let flat_errors = flat_result.unwrap_err();
345
346 assert!(
347 matches!(
348 &flat_errors[..],
349 [
350 FlatError {
351 path: path1,
352 error: Error(error1),
353 },
354 FlatError {
355 path: path2,
356 error: Error(error2),
357 },
358 ]
359 if path1 == &vec!["label1"]
360 && path2 == &vec!["label2"]
361 && error1 == "error1"
362 && error2 == "error2"
363 ),
364 "unexpected: {:#?}",
365 flat_errors
366 );
367 }
368
369 #[test]
370 fn can_call_into_result_from_vec_of_errors() {
371 let error1 = Error("error1".into()).with_label("label1");
372 let error2 = Error("error2".into()).with_label("label2");
373
374 let result: Result<_, ErrorTree<_, _>> = vec![error1, error2].into_result();
375
376 let flat_result = result.map_err(|e| e.flatten_tree());
377
378 let flat_errors = flat_result.unwrap_err();
379
380 assert!(
381 matches!(
382 &flat_errors[..],
383 [
384 FlatError {
385 path: path1,
386 error: Error(error1),
387 },
388 FlatError {
389 path: path2,
390 error: Error(error2),
391 },
392 ]
393 if path1 == &vec!["label1"]
394 && path2 == &vec!["label2"]
395 && error1 == "error1"
396 && error2 == "error2"
397 ),
398 "unexpected: {:#?}",
399 flat_errors
400 );
401 }
402
403 #[derive(Debug)]
407 struct Error(String);
408
409 impl<L> From<Error> for ErrorTree<L, Error> {
410 fn from(e: Error) -> Self {
411 Self::leaf(e)
412 }
413 }
414
415 fn faulty_function() -> Result<(), Error> {
417 Err(Error("error".into()))
418 }
419
420 fn parent_function() -> Result<Vec<()>, ErrorTree<&'static str, Error>> {
422 let result1 = faulty_function().label_error("first faulty");
423 let result2 = faulty_function().label_error("second faulty");
424
425 let result: Result<_, ErrorTree<_, _>> = vec![result1, result2]
426 .into_iter()
427 .partition_result::<Vec<_>, Vec<_>, _, _>()
428 .into_result();
429 result.label_error("parent function")
430 }
431
432 #[test]
434 fn main_function() {
435 let result = parent_function();
436
437 let flat_results = result.flatten_results();
438 let flat_errors: Vec<FlatError<&str, Error>> = flat_results.unwrap_err();
439
440 assert!(
441 matches!(
442 &flat_errors[..],
443 [
444 FlatError {
445 path: path1,
446 error: Error(_),
447 },
448 FlatError {
449 path: path2,
450 error: Error(_),
451 },
452 ]
453 if path1 == &vec!["first faulty", "parent function"]
454 && path2 == &vec!["second faulty", "parent function"]
455 ),
456 "unexpected: {:#?}",
457 flat_errors
458 );
459 }
460}