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