Skip to main content

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        f.write_fmt(format_args!("{0}", unsafe { Self::error(this) }))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                f.write_fmt(format_args!(": {0}", cause))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        f.write_fmt(format_args!("{0}", error))write!(f, "{}", error)?;
28        unsafe { Self::fmt_location(this, f) }?;
29
30        if let Some(cause) = error.source() {
31            f.write_fmt(format_args!("\n\nCaused by:"))write!(f, "\n\nCaused by:")?;
32            let multiple = cause.source().is_some();
33            let mut context = unsafe { Self::context(this) };
34            for (n, error) in Chain::new(cause).enumerate() {
35                f.write_fmt(format_args!("\n"))writeln!(f)?;
36                let mut indented = Indented {
37                    inner: f,
38                    number: if multiple { Some(n) } else { None },
39                    started: false,
40                };
41                indented.write_fmt(format_args!("{0}", error))write!(indented, "{}", error)?;
42                if let Some(layer) = context {
43                    // Do not make Indented add whitespace to an empty message.
44                    unsafe { Self::fmt_location(layer, indented.inner) }?;
45                    context = unsafe { Self::context(layer) };
46                }
47            }
48        }
49
50        #[cfg(feature = "std")]
51        {
52            use alloc::string::ToString;
53            use std::backtrace::BacktraceStatus;
54
55            let backtrace = unsafe { Self::backtrace(this) };
56            if let BacktraceStatus::Captured = backtrace.status() {
57                let mut backtrace = backtrace.to_string();
58                f.write_fmt(format_args!("\n\n"))write!(f, "\n\n")?;
59                if backtrace.starts_with("stack backtrace:") {
60                    // Capitalize to match "Caused by:"
61                    backtrace.replace_range(0..1, "S");
62                } else {
63                    // "stack backtrace:" prefix was removed in
64                    // https://github.com/rust-lang/backtrace-rs/pull/286
65                    f.write_fmt(format_args!("Stack backtrace:\n"))writeln!(f, "Stack backtrace:")?;
66                }
67                backtrace.truncate(backtrace.trim_end().len());
68                f.write_fmt(format_args!("{0}", backtrace))write!(f, "{}", backtrace)?;
69            }
70        }
71
72        Ok(())
73    }
74
75    unsafe fn fmt_location(this: Ref<Self>, f: &mut fmt::Formatter) -> fmt::Result {
76        let location = unsafe { Self::location(this) };
77        f.write_fmt(format_args!(" [{0}:{1}]", location.file(), location.line()))write!(f, " [{}:{}]", location.file(), location.line())
78    }
79}
80
81struct Indented<'a, D> {
82    inner: &'a mut D,
83    number: Option<usize>,
84    started: bool,
85}
86
87impl<T> Write for Indented<'_, T>
88where
89    T: Write,
90{
91    fn write_str(&mut self, s: &str) -> fmt::Result {
92        for (i, line) in s.split('\n').enumerate() {
93            if !self.started {
94                self.started = true;
95                match self.number {
96                    Some(number) => self.inner.write_fmt(format_args!("{0: >5}: ", number))write!(self.inner, "{: >5}: ", number)?,
97                    None => self.inner.write_str("    ")?,
98                }
99            } else if i > 0 {
100                self.inner.write_char('\n')?;
101                if self.number.is_some() {
102                    self.inner.write_str("       ")?;
103                } else {
104                    self.inner.write_str("    ")?;
105                }
106            }
107
108            self.inner.write_str(line)?;
109        }
110
111        Ok(())
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use alloc::string::String;
119
120    #[test]
121    fn one_digit() {
122        let input = "verify\nthis";
123        let expected = "    2: verify\n       this";
124        let mut output = String::new();
125
126        Indented {
127            inner: &mut output,
128            number: Some(2),
129            started: false,
130        }
131        .write_str(input)
132        .unwrap();
133
134        assert_eq!(expected, output);
135    }
136
137    #[test]
138    fn two_digits() {
139        let input = "verify\nthis";
140        let expected = "   12: verify\n       this";
141        let mut output = String::new();
142
143        Indented {
144            inner: &mut output,
145            number: Some(12),
146            started: false,
147        }
148        .write_str(input)
149        .unwrap();
150
151        assert_eq!(expected, output);
152    }
153
154    #[test]
155    fn no_digits() {
156        let input = "verify\nthis";
157        let expected = "    verify\n    this";
158        let mut output = String::new();
159
160        Indented {
161            inner: &mut output,
162            number: None,
163            started: false,
164        }
165        .write_str(input)
166        .unwrap();
167
168        assert_eq!(expected, output);
169    }
170}