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}