use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("method not implemented")]
Unimplemented,
#[error("unexpected packet after rpc was completed")]
Completed,
#[error("unrecognized packet type")]
UnrecognizedPacket,
#[error("invalid empty packet")]
EmptyPacket,
#[error("invalid message: {0}")]
InvalidMessage(#[from] prost::DecodeError),
#[error("method id empty")]
EmptyMethodId,
#[error("service id empty")]
EmptyServiceId,
#[error("no available rpc clients")]
NoAvailableClients,
#[error("writer cannot be nil")]
NilWriter,
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("stream closed")]
StreamClosed,
#[error("rpc aborted")]
Aborted,
#[error("context cancelled")]
Cancelled,
#[error("stream idle timeout exceeded")]
StreamIdle,
#[error("remote error: {0}")]
Remote(String),
#[error("message size {0} exceeds maximum {1}")]
MessageTooLarge(usize, usize),
#[error("unexpected zero length prefix")]
MessageSizeZero,
#[error("expected CallStart packet")]
ExpectedCallStart,
#[error("call start must be sent only once")]
DuplicateCallStart,
#[error("call start must be sent before call data")]
CallDataBeforeStart,
#[error("encode error: {0}")]
Encode(#[from] prost::EncodeError),
#[error("channel closed")]
ChannelClosed,
}
impl Error {
pub fn is_abort(&self) -> bool {
matches!(self, Error::Aborted | Error::Cancelled)
}
pub fn is_closed(&self) -> bool {
matches!(self, Error::StreamClosed | Error::Cancelled)
}
pub fn is_timeout(&self) -> bool {
matches!(self, Error::StreamIdle)
}
pub fn is_unimplemented(&self) -> bool {
matches!(self, Error::Unimplemented)
}
pub fn remote(msg: impl Into<String>) -> Self {
Error::Remote(msg.into())
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub mod codes {
pub const ERR_RPC_ABORT: &str = "ERR_RPC_ABORT";
pub const ERR_STREAM_IDLE: &str = "ERR_STREAM_IDLE";
}
pub fn is_abort_error_message(msg: &str) -> bool {
msg == codes::ERR_RPC_ABORT || msg == "rpc aborted" || msg == "context cancelled"
}
pub fn is_stream_idle_error_message(msg: &str) -> bool {
msg == codes::ERR_STREAM_IDLE || msg == "stream idle timeout exceeded"
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
assert_eq!(Error::Unimplemented.to_string(), "method not implemented");
assert_eq!(Error::Completed.to_string(), "unexpected packet after rpc was completed");
assert_eq!(Error::EmptyMethodId.to_string(), "method id empty");
assert_eq!(Error::Remote("test error".into()).to_string(), "remote error: test error");
}
#[test]
fn test_error_predicates() {
assert!(Error::Aborted.is_abort());
assert!(Error::Cancelled.is_abort());
assert!(!Error::StreamClosed.is_abort());
assert!(Error::StreamClosed.is_closed());
assert!(Error::Cancelled.is_closed());
assert!(!Error::Aborted.is_closed());
assert!(Error::StreamIdle.is_timeout());
assert!(!Error::Cancelled.is_timeout());
assert!(Error::Unimplemented.is_unimplemented());
assert!(!Error::Cancelled.is_unimplemented());
}
}