use std::fmt;
use std::io;
pub struct Error {
message: String,
source: Option<anyhow::Error>,
context: Vec<(&'static str, String)>,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)?;
if !self.context.is_empty() {
write!(f, ", context: {{ ")?;
write!(
f,
"{}",
self.context
.iter()
.map(|(k, v)| format!("{k}: {v}"))
.collect::<Vec<_>>()
.join(", ")
)?;
write!(f, " }}")?;
}
if let Some(source) = &self.source {
write!(f, ", source: {source}")?;
}
Ok(())
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
let mut de = f.debug_struct("Error");
de.field("message", &self.message);
de.field("context", &self.context);
de.field("source", &self.source);
return de.finish();
}
write!(f, "{}", self.message)?;
writeln!(f)?;
if !self.context.is_empty() {
writeln!(f)?;
writeln!(f, "Context:")?;
for (k, v) in self.context.iter() {
writeln!(f, " {k}: {v}")?;
}
}
if let Some(source) = &self.source {
writeln!(f)?;
writeln!(f, "Source:")?;
writeln!(f, " {source:#}")?;
}
Ok(())
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|v| v.as_ref())
}
}
impl Error {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
source: None,
context: vec![],
}
}
pub fn with_context(mut self, key: &'static str, value: impl ToString) -> Self {
self.context.push((key, value.to_string()));
self
}
pub fn set_source(mut self, src: impl Into<anyhow::Error>) -> Self {
debug_assert!(self.source.is_none(), "the source error has been set");
self.source = Some(src.into());
self
}
pub fn from_io_error(err: io::Error) -> Error {
Error::new("failed to perform io").set_source(err)
}
pub fn from_fmt_error(err: fmt::Error) -> Error {
Error::new("failed to perform format").set_source(err)
}
}