use crate::{Error, ExitError};
use explicit_error::{Domain, Error as ExplicitError};
use std::{error::Error as StdError, fmt::Display};
#[derive(Debug)]
pub struct DomainError {
pub output: ExitError,
pub source: Option<Box<dyn StdError + Send + Sync>>,
}
impl Display for DomainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.output)
}
}
impl StdError for DomainError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source.as_deref().map(|o| o as _)
}
}
impl From<DomainError> for ExplicitError<DomainError> {
fn from(value: DomainError) -> Self {
Error::Domain(Box::new(value))
}
}
impl Domain for DomainError {
fn into_source(self) -> Option<Box<dyn std::error::Error + Send + Sync>> {
self.source
}
fn context(&self) -> Option<&str> {
self.output.context.as_deref()
}
fn with_context(mut self, context: impl Display) -> Self {
self.output = self.output.with_context(context);
self
}
}
pub trait ToDomainError
where
Self: StdError + 'static + Into<Error> + Send + Sync,
for<'a> &'a Self: Into<ExitError>,
{
fn to_domain_error(self) -> DomainError {
DomainError {
output: (&self).into(),
source: Some(Box::new(self)),
}
}
fn display(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let bin_error: ExitError = self.into();
write!(f, "{}", bin_error)
}
}
pub trait ResultDomainWithContext<T, D>
where
D: ToDomainError,
for<'a> &'a D: Into<ExitError>,
{
fn with_context(self, context: impl std::fmt::Display) -> std::result::Result<T, DomainError>;
}
impl<T, D> ResultDomainWithContext<T, D> for std::result::Result<T, D>
where
D: ToDomainError,
for<'a> &'a D: Into<ExitError>,
{
fn with_context(self, context: impl std::fmt::Display) -> std::result::Result<T, DomainError> {
match self {
Ok(ok) => Ok(ok),
Err(e) => Err(e.to_domain_error().with_context(context)),
}
}
}