use std::fmt::{Debug, Display};
#[derive(Debug)]
pub struct Error<Kind>
where
Kind: Clone + Debug + Display + PartialEq,
{
pub(crate) kind: Kind,
pub(crate) source: Option<crate::Error>,
}
impl<Kind> Error<Kind>
where
Kind: Clone + Debug + Display + PartialEq,
{
pub(crate) fn new(kind: Kind) -> Self {
Self { kind, source: None }
}
pub(crate) fn with_source<S>(kind: Kind, source: S) -> Self
where
S: Into<crate::Error>,
{
Self {
kind,
source: Some(source.into()),
}
}
pub fn kind(&self) -> Kind {
self.kind.clone()
}
}
impl<Kind> Display for Error<Kind>
where
Kind: Clone + Debug + Display + PartialEq,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(err) = &self.source {
write!(f, "{}: {}", self.kind, err)
} else {
write!(f, "{}", self.kind)
}
}
}
impl<Kind> std::error::Error for Error<Kind>
where
Kind: Clone + Debug + Display + PartialEq,
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_ref()
.map(|boxed| boxed.as_ref() as &(dyn std::error::Error + 'static))
}
}
impl<Kind> From<Kind> for Error<Kind>
where
Kind: Clone + Debug + Display + PartialEq,
{
fn from(kind: Kind) -> Self {
Self { kind, source: None }
}
}
#[cfg(test)]
mod test {
#![allow(dead_code)]
use super::*;
use std::fmt::Formatter;
#[derive(Clone, Debug, PartialEq)]
enum FooErrorKind {
Bar,
Baz,
}
impl Display for FooErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bar => write!(f, "bar error"),
Self::Baz => write!(f, "baz error"),
}
}
}
type FooError = Error<FooErrorKind>;
#[test]
fn new() {
let error = FooError::new(FooErrorKind::Bar);
assert_eq!(error.kind, FooErrorKind::Bar);
assert!(error.source.is_none());
}
#[test]
fn with_source() {
let source = std::io::Error::new(std::io::ErrorKind::Other, "foo");
let error = FooError::with_source(FooErrorKind::Bar, source);
assert_eq!(error.kind, FooErrorKind::Bar);
assert_eq!(error.source.unwrap().to_string(), "foo");
}
#[test]
fn kind() {
let error: FooError = FooErrorKind::Bar.into();
let kind = error.kind();
let _ = error.kind();
assert_eq!(kind, FooErrorKind::Bar);
}
#[test]
fn display_without_source() {
let error: FooError = FooErrorKind::Bar.into();
assert_eq!(format!("{}", error), "bar error");
}
#[test]
fn from() {
let error: FooError = FooErrorKind::Bar.into();
assert_eq!(error.kind, FooErrorKind::Bar);
assert!(error.source.is_none());
}
}