use std::error::Error;
use std::fmt;
use crate::infrastructure::InfrastructureError;
#[derive(Debug)]
pub(crate) enum AppFailure {
Infrastructure(InfrastructureError),
Native(anyhow::Error),
Reported,
}
impl AppFailure {
#[must_use]
pub(crate) const fn reported() -> Self {
Self::Reported
}
#[must_use]
pub(crate) const fn is_reported(&self) -> bool {
matches!(self, Self::Reported)
}
#[must_use]
pub(crate) fn exit_code(&self) -> i32 {
match self {
Self::Infrastructure(error) => error.exit_code(),
Self::Native(_) | Self::Reported => 1,
}
}
}
impl From<anyhow::Error> for AppFailure {
fn from(error: anyhow::Error) -> Self {
Self::Native(error)
}
}
impl From<InfrastructureError> for AppFailure {
fn from(error: InfrastructureError) -> Self {
Self::Infrastructure(error)
}
}
impl fmt::Display for AppFailure {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Infrastructure(error) => write!(formatter, "{error}"),
Self::Native(error) => write!(formatter, "{error:#}"),
Self::Reported => formatter.write_str("failure already reported"),
}
}
}
impl Error for AppFailure {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Infrastructure(error) => Some(error),
Self::Native(error) => Some(error.as_ref()),
Self::Reported => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reported_failure_preserves_failure_exit_without_duplicate_rendering() {
let failure = AppFailure::reported();
assert!(failure.is_reported());
assert_eq!(failure.exit_code(), 1);
}
}