156 lines
4.0 KiB
Rust
156 lines
4.0 KiB
Rust
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, "{}", Self::error(this))?;
|
|
|
|
if f.alternate() {
|
|
for cause in Self::chain(this).skip(1) {
|
|
write!(f, ": {}", cause)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) unsafe fn debug(this: Ref<Self>, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let error = 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(backtrace, feature = "backtrace"))]
|
|
{
|
|
use crate::backtrace::BacktraceStatus;
|
|
|
|
let backtrace = 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::*;
|
|
|
|
#[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);
|
|
}
|
|
}
|