anyhow/
fmt.rs

1use crate::chain::Chain;
2use crate::error::ErrorImpl;
3use crate::ptr::Ref;
4use core::fmt::{self, Debug, Write};
5
6impl ErrorImpl {
7    pub(crate) unsafe fn display(this: Ref<Self>, f: &mut fmt::Formatter) -> fmt::Result {
8        write!(f, "{}", unsafe { Self::error(this) })?;
9
10        if f.alternate() {
11            let chain = unsafe { Self::chain(this) };
12            for cause in chain.skip(1) {
13                write!(f, ": {}", cause)?;
14            }
15        }
16
17        Ok(())
18    }
19
20    pub(crate) unsafe fn debug(this: Ref<Self>, f: &mut fmt::Formatter) -> fmt::Result {
21        let error = unsafe { Self::error(this) };
22
23        if f.alternate() {
24            return Debug::fmt(error, f);
25        }
26
27        write!(f, "{}", error)?;
28
29        if let Some(cause) = error.source() {
30            write!(f, "\n\nCaused by:")?;
31            let multiple = cause.source().is_some();
32            for (n, error) in Chain::new(cause).enumerate() {
33                writeln!(f)?;
34                let mut indented = Indented {
35                    inner: f,
36                    number: if multiple { Some(n) } else { None },
37                    started: false,
38                };
39                write!(indented, "{}", error)?;
40            }
41        }
42
43        #[cfg(any(std_backtrace, feature = "backtrace"))]
44        {
45            use crate::backtrace::BacktraceStatus;
46            use alloc::string::ToString;
47
48            let backtrace = unsafe { Self::backtrace(this) };
49            if let BacktraceStatus::Captured = backtrace.status() {
50                let mut backtrace = backtrace.to_string();
51                write!(f, "\n\n")?;
52                if backtrace.starts_with("stack backtrace:") {
53                    // Capitalize to match "Caused by:"
54                    backtrace.replace_range(0..1, "S");
55                } else {
56                    // "stack backtrace:" prefix was removed in
57                    // https://github.com/rust-lang/backtrace-rs/pull/286
58                    writeln!(f, "Stack backtrace:")?;
59                }
60                backtrace.truncate(backtrace.trim_end().len());
61                write!(f, "{}", backtrace)?;
62            }
63        }
64
65        Ok(())
66    }
67}
68
69struct Indented<'a, D> {
70    inner: &'a mut D,
71    number: Option<usize>,
72    started: bool,
73}
74
75impl<T> Write for Indented<'_, T>
76where
77    T: Write,
78{
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, "{: >5}: ", number)?,
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}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use alloc::string::String;
107
108    #[test]
109    fn one_digit() {
110        let input = "verify\nthis";
111        let expected = "    2: verify\n       this";
112        let mut output = String::new();
113
114        Indented {
115            inner: &mut output,
116            number: Some(2),
117            started: false,
118        }
119        .write_str(input)
120        .unwrap();
121
122        assert_eq!(expected, output);
123    }
124
125    #[test]
126    fn two_digits() {
127        let input = "verify\nthis";
128        let expected = "   12: verify\n       this";
129        let mut output = String::new();
130
131        Indented {
132            inner: &mut output,
133            number: Some(12),
134            started: false,
135        }
136        .write_str(input)
137        .unwrap();
138
139        assert_eq!(expected, output);
140    }
141
142    #[test]
143    fn no_digits() {
144        let input = "verify\nthis";
145        let expected = "    verify\n    this";
146        let mut output = String::new();
147
148        Indented {
149            inner: &mut output,
150            number: None,
151            started: false,
152        }
153        .write_str(input)
154        .unwrap();
155
156        assert_eq!(expected, output);
157    }
158}