pretty_error_debug/
implementation.rs

1// ////////////////////////////////////////////////////////////////////////////////////////////////
2// All code in this file was extracted from:
3//  * https://github.com/dtolnay/anyhow/blob/0ba6408b5ef508c3dfc95797d21cfbdca9dd64ee/src/fmt.rs
4//  * https://github.com/dtolnay/anyhow/blob/fa9bcc0457a2e51593b874cc2f8bcb5608ad43fe/src/chain.rs
5//
6// Author: David Tolnay <dtolnay@gmail.com> and contributors to the `anyhow` project.
7// ////////////////////////////////////////////////////////////////////////////////////////////////
8
9use core::fmt::{self, Write};
10
11use crate::Error;
12
13/// Write out the [`Error`] message and chain.
14///
15/// Please see the [`crate`] documentation for a more complete example.
16///
17/// ```rust
18/// use std::fmt::{self, Write};
19///
20/// pub enum MyError {
21///     Variant1(/* … */),
22///     Variant2(/* … */),
23///     // …
24/// }
25///
26/// impl fmt::Debug for MyError {
27///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28/// #       /*
29///         pretty_error_debug::pretty_error_debug(self, f)
30/// #       */ Ok(())
31///     }
32/// }
33///
34/// // TODO: implement `std::fmt::Display` and `std::error::Error`.
35/// ```
36///
37/// # Errors
38///
39/// Fails if writing to the `f` argument failed.
40///
41pub fn pretty_error_debug(error: &dyn Error, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42    fmt::Display::fmt(error, f)?;
43    if let Some(cause) = error.source() {
44        f.write_str("\n\nCaused by:")?;
45        let multiple = cause.source().is_some();
46        for (n, error) in Chain(Some(cause)).enumerate() {
47            f.write_char('\n')?;
48            let mut indented = Indented {
49                inner: f,
50                number: if multiple { Some(n + 1) } else { None },
51                started: false,
52            };
53            write!(indented, "{error}")?;
54        }
55    }
56    Ok(())
57}
58
59struct Chain<'a>(Option<&'a dyn Error>);
60
61impl<'a> Iterator for Chain<'a> {
62    type Item = &'a dyn Error;
63
64    #[inline]
65    fn next(&mut self) -> Option<Self::Item> {
66        let error = self.0?;
67        self.0 = error.source();
68        Some(error)
69    }
70}
71
72struct Indented<'a, 'b> {
73    inner: &'a mut fmt::Formatter<'b>,
74    number: Option<usize>,
75    started: bool,
76}
77
78impl fmt::Write for Indented<'_, '_> {
79    fn write_str(&mut self, s: &str) -> fmt::Result {
80        for (i, line) in s.split('\n').enumerate() {
81            if !self.started {
82                self.started = true;
83                match self.number {
84                    Some(number) => write!(self.inner, "{number: >5}: ")?,
85                    None => self.inner.write_str("    ")?,
86                }
87            } else if i > 0 {
88                self.inner.write_char('\n')?;
89                if self.number.is_some() {
90                    self.inner.write_str("       ")?;
91                } else {
92                    self.inner.write_str("    ")?;
93                }
94            }
95
96            self.inner.write_str(line)?;
97        }
98
99        Ok(())
100    }
101}