use std::any::Any;
use std::fmt::{Debug, Display, Formatter};
#[derive(Debug, Clone, Copy)]
pub enum Outcome<T, E, P> {
Success(T),
Failure(Failure<E, P>),
}
impl<T, E, P> Outcome<T, E, P> {
pub fn is_success(&self) -> bool {
matches!(self, Self::Success(_))
}
}
impl<T, E, P> Display for Outcome<T, E, P>
where
E: Display,
P: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Outcome::Success(_) => "Passed!".to_string(),
Outcome::Failure(failure) => format!("{}", failure),
}
)
}
}
impl<T, E> From<Result<Result<T, E>, Box<dyn Any + Send>>> for Outcome<T, E, String> {
fn from(value: Result<Result<T, E>, Box<dyn Any + Send>>) -> Self {
match value {
Ok(Ok(success)) => Outcome::Success(success),
Ok(Err(err)) => Outcome::Failure(Failure::Err(err)),
Err(pnic) => Outcome::Failure(Failure::Panic(panic_msg(pnic))),
}
}
}
impl<T, E> From<Result<T, Box<dyn Any + Send>>> for Outcome<T, E, String> {
fn from(value: Result<T, Box<dyn Any + Send>>) -> Self {
match value {
Ok(success) => Outcome::Success(success),
Err(pnic) => Outcome::Failure(Failure::Panic(panic_msg(pnic))),
}
}
}
fn panic_msg(panic: Box<dyn Any + Send>) -> String {
(&*panic)
.downcast_ref::<String>()
.map_or_else(
|| (&*panic).downcast_ref::<&str>().map(|s| s.to_string()),
|s| Some(s.to_owned()),
)
.unwrap_or_else(|| "Fickle was unable to determine the panic message".into())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Failure<E, P> {
Err(E),
Panic(P),
}
impl<E, P> Display for Failure<E, P>
where
E: Display,
P: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Failure::Err(err) => format!("Failed with error: {}", err),
Failure::Panic(panic) => format!("Failed with panic: {:?}", panic),
}
)
}
}