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 { display(this: Ref<Self>, f: &mut fmt::Formatter) -> fmt::Result7 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 debug(this: Ref<Self>, f: &mut fmt::Formatter) -> fmt::Result20 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 69 struct Indented<'a, D> { 70 inner: &'a mut D, 71 number: Option<usize>, 72 started: bool, 73 } 74 75 impl<T> Write for Indented<'_, T> 76 where 77 T: Write, 78 { write_str(&mut self, s: &str) -> fmt::Result79 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)] 104 mod tests { 105 use super::*; 106 use alloc::string::String; 107 108 #[test] one_digit()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] two_digits()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] no_digits()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 } 159