use std::{error, fmt::{self, Display}, io};
use serde::{de, ser};
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum ErrorKind {
NoLeftSpaceError,
BufferOverMaxError,
TypeNotMatchError,
ParseError,
MissingError,
StringFormatError,
IoError,
ExtensionError,
}
#[derive(Debug)]
enum ErrorRepr {
WithDescription(ErrorKind, &'static str),
WithDescriptionAndDetail(ErrorKind, &'static str, String),
ExtensionError(String, String),
IoError(io::Error),
}
pub struct HpError {
repr: ErrorRepr,
}
pub type HpResult<T> = Result<T, HpError>;
impl PartialEq for HpError {
fn eq(&self, other: &HpError) -> bool {
match (&self.repr, &other.repr) {
(&ErrorRepr::WithDescription(kind_a, _), &ErrorRepr::WithDescription(kind_b, _)) => {
kind_a == kind_b
}
(
&ErrorRepr::WithDescriptionAndDetail(kind_a, _, _),
&ErrorRepr::WithDescriptionAndDetail(kind_b, _, _),
) => kind_a == kind_b,
(&ErrorRepr::ExtensionError(ref a, _), &ErrorRepr::ExtensionError(ref b, _)) => {
*a == *b
}
_ => false,
}
}
}
impl From<io::Error> for HpError {
fn from(err: io::Error) -> HpError {
HpError {
repr: ErrorRepr::IoError(err),
}
}
}
impl From<(ErrorKind, &'static str)> for HpError {
fn from((kind, desc): (ErrorKind, &'static str)) -> HpError {
HpError {
repr: ErrorRepr::WithDescription(kind, desc),
}
}
}
impl From<(ErrorKind, &'static str, String)> for HpError {
fn from((kind, desc, detail): (ErrorKind, &'static str, String)) -> HpError {
HpError {
repr: ErrorRepr::WithDescriptionAndDetail(kind, desc, detail),
}
}
}
impl error::Error for HpError {
fn cause(&self) -> Option<&dyn error::Error> {
match self.repr {
ErrorRepr::IoError(ref err) => Some(err as &dyn error::Error),
_ => None,
}
}
}
impl fmt::Display for HpError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.repr {
ErrorRepr::WithDescription(_, desc) => desc.fmt(f),
ErrorRepr::WithDescriptionAndDetail(_, desc, ref detail) => {
desc.fmt(f)?;
f.write_str(": ")?;
detail.fmt(f)
}
ErrorRepr::ExtensionError(ref code, ref detail) => {
code.fmt(f)?;
f.write_str(": ")?;
detail.fmt(f)
}
ErrorRepr::IoError(ref err) => err.fmt(f),
}
}
}
impl fmt::Debug for HpError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Display::fmt(self, f)
}
}
impl HpError {
pub fn kind(&self) -> ErrorKind {
match self.repr {
ErrorRepr::WithDescription(kind, _) => kind,
ErrorRepr::WithDescriptionAndDetail(kind, _, _) => kind,
ErrorRepr::ExtensionError(_, _) => ErrorKind::ExtensionError,
ErrorRepr::IoError(_) => ErrorKind::IoError,
}
}
pub fn category(&self) -> &str {
match self.kind() {
ErrorKind::NoLeftSpaceError => "no left space error",
ErrorKind::BufferOverMaxError => "buffer over max error",
ErrorKind::TypeNotMatchError => "type not match error",
ErrorKind::ParseError => "parse error",
ErrorKind::MissingError => "missing error",
ErrorKind::StringFormatError => "string format error",
ErrorKind::IoError => "I/O error",
ErrorKind::ExtensionError => "extension error",
}
}
pub fn is_io_error(&self) -> bool {
match self.kind() {
ErrorKind::IoError => true,
_ => false,
}
}
pub fn extension_error_code(&self) -> Option<&str> {
match self.repr {
ErrorRepr::ExtensionError(ref code, _) => Some(&code),
_ => None,
}
}
pub fn extension_error_detail(&self) -> Option<&str> {
match self.repr {
ErrorRepr::ExtensionError(_, ref detail) => Some(&detail),
ErrorRepr::WithDescriptionAndDetail(_, _, ref detail) => Some(&detail),
_ => None,
}
}
}
pub fn make_extension_error(code: &str, detail: Option<&str>) -> HpError {
HpError {
repr: ErrorRepr::ExtensionError(
code.to_string(),
match detail {
Some(x) => x.to_string(),
None => "Unknown extension error encountered".to_string(),
},
),
}
}
impl ser::Error for HpError {
fn custom<T: Display>(msg: T) -> Self {
HpError {
repr: ErrorRepr::ExtensionError(
"ser".to_string(),
msg.to_string(),
),
}
}
}
impl de::Error for HpError {
fn custom<T: Display>(msg: T) -> Self {
HpError {
repr: ErrorRepr::ExtensionError(
"de".to_string(),
msg.to_string(),
),
}
}
}