use crate::StackError;
use core::fmt::{Debug, Display, Formatter};
#[cfg(feature = "std")]
use std::io::{Write, stderr};
#[cfg(feature = "std")]
use std::process::{ExitCode, Termination};
pub struct StackReport<E>(Result<(), E>);
impl<E: StackError> From<Result<(), E>> for StackReport<E> {
fn from(result: Result<(), E>) -> Self {
Self(result)
}
}
impl<E: StackError> From<E> for StackReport<E> {
fn from(error: E) -> Self {
Self(Err(error))
}
}
impl<E: StackError> Debug for StackReport<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Display::fmt(self, f)
}
}
impl<E: StackError> Display for StackReport<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match &self.0 {
Ok(()) => Ok(()),
Err(e) => Display::fmt(&StackReportFormatter(e), f),
}
}
}
#[cfg(feature = "std")]
impl<E: StackError> Termination for StackReport<E> {
fn report(self) -> ExitCode {
match self.0 {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
let _ = Write::write_fmt(
&mut stderr(),
format_args!("{}\n", StackReportFormatter(&e)),
);
ExitCode::FAILURE
}
}
}
}
struct StackReportFormatter<'a>(&'a dyn StackError);
impl Display for StackReportFormatter<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let error = self.0;
write!(
f,
"Error: {}: {error}, at {}",
error.type_name(),
error.location()
)?;
if error.source().is_none() {
return Ok(());
}
write!(f, "\nCaused by (recent first):")?;
let mut index = 1;
let mut current_stack: &dyn StackError = error;
while let Some(next) = current_stack.stack_source() {
debug_assert!(
current_stack.source().is_some(),
"StackError::stack_source() returned Some but Error::source() returned None \
for type {}. This indicates an incorrect StackError implementation.",
current_stack.type_name()
);
write!(
f,
"\n {index}| {}: {next}, at {}",
next.type_name(),
next.location()
)?;
index += 1;
current_stack = next;
}
let mut current_error = current_stack.source();
while let Some(e) = current_error {
write!(f, "\n {index}| {e}")?;
index += 1;
current_error = e.source();
}
Ok(())
}
}