| use crate::chain::Chain; |
| use crate::error::ErrorImpl; |
| use crate::ptr::Ref; |
| use core::fmt::{self, Debug, Write}; |
| |
| impl ErrorImpl { |
| pub(crate) unsafe fn display(this: Ref<Self>, f: &mut fmt::Formatter) -> fmt::Result { |
| write!(f, "{}", unsafe { Self::error(this) })?; |
| |
| if f.alternate() { |
| let chain = unsafe { Self::chain(this) }; |
| for cause in chain.skip(1) { |
| write!(f, ": {}", cause)?; |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| pub(crate) unsafe fn debug(this: Ref<Self>, f: &mut fmt::Formatter) -> fmt::Result { |
| let error = unsafe { Self::error(this) }; |
| |
| if f.alternate() { |
| return Debug::fmt(error, f); |
| } |
| |
| write!(f, "{}", error)?; |
| |
| if let Some(cause) = error.source() { |
| write!(f, "\n\nCaused by:")?; |
| let multiple = cause.source().is_some(); |
| for (n, error) in Chain::new(cause).enumerate() { |
| writeln!(f)?; |
| let mut indented = Indented { |
| inner: f, |
| number: if multiple { Some(n) } else { None }, |
| started: false, |
| }; |
| write!(indented, "{}", error)?; |
| } |
| } |
| |
| #[cfg(any(std_backtrace, feature = "backtrace"))] |
| { |
| use crate::backtrace::BacktraceStatus; |
| use alloc::string::ToString; |
| |
| let backtrace = unsafe { Self::backtrace(this) }; |
| if let BacktraceStatus::Captured = backtrace.status() { |
| let mut backtrace = backtrace.to_string(); |
| write!(f, "\n\n")?; |
| if backtrace.starts_with("stack backtrace:") { |
| // Capitalize to match "Caused by:" |
| backtrace.replace_range(0..1, "S"); |
| } else { |
| // "stack backtrace:" prefix was removed in |
| // https://github.com/rust-lang/backtrace-rs/pull/286 |
| writeln!(f, "Stack backtrace:")?; |
| } |
| backtrace.truncate(backtrace.trim_end().len()); |
| write!(f, "{}", backtrace)?; |
| } |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| struct Indented<'a, D> { |
| inner: &'a mut D, |
| number: Option<usize>, |
| started: bool, |
| } |
| |
| impl<T> Write for Indented<'_, T> |
| where |
| T: Write, |
| { |
| fn write_str(&mut self, s: &str) -> fmt::Result { |
| for (i, line) in s.split('\n').enumerate() { |
| if !self.started { |
| self.started = true; |
| match self.number { |
| Some(number) => write!(self.inner, "{: >5}: ", number)?, |
| None => self.inner.write_str(" ")?, |
| } |
| } else if i > 0 { |
| self.inner.write_char('\n')?; |
| if self.number.is_some() { |
| self.inner.write_str(" ")?; |
| } else { |
| self.inner.write_str(" ")?; |
| } |
| } |
| |
| self.inner.write_str(line)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use alloc::string::String; |
| |
| #[test] |
| fn one_digit() { |
| let input = "verify\nthis"; |
| let expected = " 2: verify\n this"; |
| let mut output = String::new(); |
| |
| Indented { |
| inner: &mut output, |
| number: Some(2), |
| started: false, |
| } |
| .write_str(input) |
| .unwrap(); |
| |
| assert_eq!(expected, output); |
| } |
| |
| #[test] |
| fn two_digits() { |
| let input = "verify\nthis"; |
| let expected = " 12: verify\n this"; |
| let mut output = String::new(); |
| |
| Indented { |
| inner: &mut output, |
| number: Some(12), |
| started: false, |
| } |
| .write_str(input) |
| .unwrap(); |
| |
| assert_eq!(expected, output); |
| } |
| |
| #[test] |
| fn no_digits() { |
| let input = "verify\nthis"; |
| let expected = " verify\n this"; |
| let mut output = String::new(); |
| |
| Indented { |
| inner: &mut output, |
| number: None, |
| started: false, |
| } |
| .write_str(input) |
| .unwrap(); |
| |
| assert_eq!(expected, output); |
| } |
| } |