use std::{error, fmt};
#[derive(Debug)]
pub enum Error {
UserError(
String,
String,
Option<Box<Error>>,
Option<Box<dyn error::Error + Send + Sync>>,
),
SystemError(
String,
String,
Option<Box<Error>>,
Option<Box<dyn error::Error + Send + Sync>>,
),
}
impl Error {
pub fn description(&self) -> String {
match self {
Error::UserError(description, ..) | Error::SystemError(description, ..) => {
description.clone()
}
}
}
pub fn message(&self) -> String {
let description = match self {
Error::UserError(description, ..) | Error::SystemError(description, ..) => description,
};
let hero_message = match self {
Error::UserError(_, _, _, _) => {
format!("Oh no! {}", description)
}
Error::SystemError(_, _, _, _) => {
format!("Whoops! {} (This isn't your fault)", description)
}
};
match (self.caused_by(), self.advice()) {
(Some(cause), Some(advice)) => {
format!(
"{}\n\nThis was caused by:\n{}\n\nTo try and fix this, you can:\n{}",
hero_message, cause, advice
)
}
(Some(cause), None) => {
format!("{}\n\nThis was caused by:\n{}", hero_message, cause)
}
(None, Some(advice)) => {
format!(
"{}\n\nTo try and fix this, you can:\n{}",
hero_message, advice
)
}
(None, None) => hero_message,
}
}
fn caused_by(&self) -> Option<String> {
match self {
Error::UserError(.., Some(cause), _) | Error::SystemError(.., Some(cause), _) => {
match cause.caused_by() {
Some(child_cause) => {
Some(format!(" - {}\n{}", cause.description(), child_cause))
}
None => Some(format!(" - {}", cause.description())),
}
}
Error::UserError(.., Some(internal)) | Error::SystemError(.., Some(internal)) => {
Some(format!(" - {}", internal))
}
_ => None,
}
}
fn advice(&self) -> Option<String> {
let (advice, cause) = match self {
Error::UserError(_, advice, cause, _) | Error::SystemError(_, advice, cause, _) => {
(advice, cause)
}
};
match cause {
Some(cause) => match cause.advice() {
Some(cause_advice) if !cause_advice.is_empty() => {
Some(format!("{}\n - {}", cause_advice, advice))
}
_ => Some(format!(" - {}", advice)),
},
None => Some(format!(" - {}", advice)),
}
}
pub fn is_user(&self) -> bool {
match self {
Error::UserError(..) => true,
_ => false,
}
}
pub fn is_system(&self) -> bool {
match self {
Error::SystemError(..) => true,
_ => false,
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::UserError(.., Some(ref err)) | Error::SystemError(.., Some(ref err)) => {
err.source()
}
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message())
}
}