use core::result;
use std::{
error,
fmt::{self, Debug, Display},
io,
};
pub struct Error {
err: Box<ErrorImpl>,
}
struct ErrorImpl {
code: ErrorCode,
}
pub type Result<T> = result::Result<T, Error>;
impl Error {
pub fn classify(&self) -> Category {
match self.err.code {
ErrorCode::Io(_) => Category::Io,
ErrorCode::InvalidData(_) | ErrorCode::UnexpectedPacket(_) => Category::Data,
ErrorCode::ListenerClosed
| ErrorCode::HandlerClosedWhileCommandRequest
| ErrorCode::HandlerClosedWhileEventRequest(_)
| ErrorCode::HandlerClosedWhileStreaming(_) => Category::Closed,
ErrorCode::CommandFailed(_) => Category::CmdFailure,
ErrorCode::UnknownCmd => Category::UnknownCmd,
ErrorCode::UnknownEvent(_) => Category::UnknownEvent,
}
}
pub fn is_io(&self) -> bool {
self.classify() == Category::Io
}
pub fn is_data(&self) -> bool {
self.classify() == Category::Data
}
pub fn is_closed(&self) -> bool {
self.classify() == Category::Closed
}
pub fn is_command_failed(&self) -> bool {
self.classify() == Category::CmdFailure
}
pub fn is_unknown_cmd(&self) -> bool {
self.classify() == Category::UnknownCmd
}
pub fn is_unknown_event(&self) -> bool {
self.classify() == Category::UnknownEvent
}
pub(crate) fn io(e: io::Error) -> Self {
Self {
err: Box::new(ErrorImpl { code: ErrorCode::Io(e) }),
}
}
pub(crate) fn data(code: ErrorCode) -> Self {
Self {
err: Box::new(ErrorImpl { code }),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Category {
Io,
Data,
Closed,
CmdFailure,
UnknownCmd,
UnknownEvent,
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::io(e)
}
}
impl From<Error> for io::Error {
fn from(e: Error) -> Self {
match e.classify() {
Category::Io => {
if let ErrorCode::Io(e) = e.err.code {
e
} else {
unreachable!()
}
},
Category::Data => io::Error::new(io::ErrorKind::InvalidData, e),
Category::Closed => io::Error::new(io::ErrorKind::BrokenPipe, e),
Category::CmdFailure => io::Error::other(e),
Category::UnknownCmd | Category::UnknownEvent => io::Error::new(io::ErrorKind::Unsupported, e),
}
}
}
impl From<serde_vici::Error> for Error {
fn from(e: serde_vici::Error) -> Self {
Error::data(ErrorCode::InvalidData(e))
}
}
pub(crate) enum ErrorCode {
Io(io::Error),
InvalidData(serde_vici::Error),
ListenerClosed,
HandlerClosedWhileCommandRequest,
HandlerClosedWhileEventRequest(String),
HandlerClosedWhileStreaming(String),
UnexpectedPacket(String),
CommandFailed(Option<String>),
UnknownCmd,
UnknownEvent(String),
}
impl Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ErrorCode::Io(ref err) => Display::fmt(err, f),
ErrorCode::InvalidData(ref err) => Display::fmt(err, f),
ErrorCode::ListenerClosed => f.write_str("listener has been closed"),
ErrorCode::HandlerClosedWhileCommandRequest => f.write_str("handler has been closed while processing command request"),
ErrorCode::HandlerClosedWhileEventRequest(ref event) => f.write_fmt(format_args!("handler has been closed while processing event: {event}")),
ErrorCode::HandlerClosedWhileStreaming(ref packet_type) => f.write_fmt(format_args!("handler has been closed while processing {packet_type}")),
ErrorCode::UnexpectedPacket(ref packet_type) => f.write_fmt(format_args!("unexpected packet type {packet_type}")),
ErrorCode::CommandFailed(ref reason) => match reason {
Some(errmsg) => f.write_fmt(format_args!("command failed: {errmsg}")),
None => f.write_str("command failed"),
},
ErrorCode::UnknownCmd => f.write_str("unknown command"),
ErrorCode::UnknownEvent(ref event) => f.write_fmt(format_args!("unknown event {event}")),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self.err.code {
ErrorCode::Io(ref err) => Some(err),
_ => None,
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.err.code, f)
}
}
impl Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Error({:?})", self.err.code.to_string())
}
}