use std::{
any::Any,
error::Error,
fmt,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BatchTaskError<E> {
Failed(E),
Panicked {
message: Option<String>,
},
}
impl<E> BatchTaskError<E> {
#[inline]
pub fn from_panic_payload(payload: &(dyn Any + Send)) -> Self {
match panic_payload_message(payload) {
Some(message) => Self::panicked(message),
None => Self::panicked_without_message(),
}
}
#[inline]
pub fn panicked(message: impl Into<String>) -> Self {
Self::Panicked {
message: Some(message.into()),
}
}
#[inline]
pub const fn panicked_without_message() -> Self {
Self::Panicked { message: None }
}
#[inline]
pub const fn is_failed(&self) -> bool {
matches!(self, Self::Failed(_))
}
#[inline]
pub const fn is_panicked(&self) -> bool {
matches!(self, Self::Panicked { .. })
}
#[inline]
pub fn panic_message(&self) -> Option<&str> {
match self {
Self::Failed(_) | Self::Panicked { message: None } => None,
Self::Panicked {
message: Some(message),
} => Some(message.as_str()),
}
}
}
impl<E> fmt::Display for BatchTaskError<E>
where
E: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Failed(error) => write!(f, "task failed: {error}"),
Self::Panicked { message: None } => f.write_str("task panicked"),
Self::Panicked {
message: Some(message),
} => write!(f, "task panicked: {message}"),
}
}
}
impl<E> Error for BatchTaskError<E>
where
E: Error + 'static,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Failed(error) => Some(error),
Self::Panicked { .. } => None,
}
}
}
pub(crate) fn panic_payload_to_error<E>(payload: &(dyn Any + Send)) -> BatchTaskError<E> {
BatchTaskError::from_panic_payload(payload)
}
fn panic_payload_message(payload: &(dyn Any + Send)) -> Option<String> {
if let Some(message) = payload.downcast_ref::<&'static str>() {
Some((*message).to_owned())
} else {
payload.downcast_ref::<String>().cloned()
}
}