use core::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TaskFailure<E> {
kind: TaskFailureKind,
error: E,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TaskFailureKind {
ReturnedError,
}
impl<E> TaskFailure<E> {
#[must_use]
pub const fn returned(error: E) -> Self {
Self {
kind: TaskFailureKind::ReturnedError,
error,
}
}
#[must_use]
pub const fn kind(&self) -> TaskFailureKind {
self.kind
}
#[must_use]
pub const fn error(&self) -> &E {
&self.error
}
#[must_use]
pub fn into_error(self) -> E {
self.error
}
pub fn map_error<F, M>(self, map: M) -> TaskFailure<F>
where
M: FnOnce(E) -> F,
{
TaskFailure {
kind: self.kind,
error: map(self.error),
}
}
}
impl<E: fmt::Display> fmt::Display for TaskFailure<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
TaskFailureKind::ReturnedError => write!(f, "task returned an error: {}", self.error),
}
}
}
impl<E: fmt::Debug + fmt::Display + 'static> std::error::Error for TaskFailure<E> {}
#[cfg(test)]
mod tests {
use super::{TaskFailure, TaskFailureKind};
#[test]
fn preserves_original_error_and_classification() {
let failure = TaskFailure::returned(String::from("disk unavailable"));
assert_eq!(failure.kind(), TaskFailureKind::ReturnedError);
assert_eq!(failure.error(), "disk unavailable");
assert_eq!(
failure.to_string(),
"task returned an error: disk unavailable"
);
assert_eq!(failure.into_error(), "disk unavailable");
}
#[test]
fn maps_error_without_changing_classification() {
let failure = TaskFailure::returned(17_u8).map_error(|code| format!("code={code}"));
assert_eq!(failure.kind(), TaskFailureKind::ReturnedError);
assert_eq!(failure.error(), "code=17");
}
}