use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Transport layer error: {message}")]
Transport {
message: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Service '{name}' not found")]
ServiceNotFound { name: String },
#[error("Method '{method}' not found on service '{service}'")]
MethodNotFound { service: String, method: String },
#[error("Serialization error: {message}")]
Serialization {
message: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Connection error: {message}")]
Connection {
message: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Operation timed out after {duration_ms}ms: {operation}")]
Timeout { operation: String, duration_ms: u64 },
#[error("Runtime error: {message}")]
Runtime {
message: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("Configuration error: {message}")]
Configuration {
message: String,
field: Option<String>,
},
#[error("IO error: {message}")]
Io {
message: String,
#[source]
source: std::io::Error,
},
#[error("Service lifecycle error: {message}")]
ServiceLifecycle {
message: String,
service: String,
state: String,
},
#[error("Protocol error: {message}")]
Protocol {
message: String,
expected: Option<String>,
received: Option<String>,
},
#[error("Invalid topic pattern: {pattern}")]
InvalidTopicPattern { pattern: String },
#[error("Subscription error: {message}")]
SubscriptionError {
message: String,
topic: Option<String>,
},
#[error("Invalid request: {message}")]
InvalidRequest {
message: String,
context: Option<String>,
},
}
impl Error {
pub fn transport<E>(message: impl Into<String>, source: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::Transport {
message: message.into(),
source: Some(Box::new(source)),
}
}
pub fn transport_msg(message: impl Into<String>) -> Self {
Self::Transport {
message: message.into(),
source: None,
}
}
pub fn service_not_found(name: impl Into<String>) -> Self {
Self::ServiceNotFound { name: name.into() }
}
pub fn method_not_found(service: impl Into<String>, method: impl Into<String>) -> Self {
Self::MethodNotFound {
service: service.into(),
method: method.into(),
}
}
pub fn serialization<E>(message: impl Into<String>, source: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::Serialization {
message: message.into(),
source: Some(Box::new(source)),
}
}
pub fn serialization_msg(message: impl Into<String>) -> Self {
Self::Serialization {
message: message.into(),
source: None,
}
}
pub fn connection<E>(message: impl Into<String>, source: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::Connection {
message: message.into(),
source: Some(Box::new(source)),
}
}
pub fn connection_msg(message: impl Into<String>) -> Self {
Self::Connection {
message: message.into(),
source: None,
}
}
pub fn timeout(operation: impl Into<String>, duration_ms: u64) -> Self {
Self::Timeout {
operation: operation.into(),
duration_ms,
}
}
pub fn runtime<E>(message: impl Into<String>, source: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::Runtime {
message: message.into(),
source: Some(Box::new(source)),
}
}
pub fn runtime_msg(message: impl Into<String>) -> Self {
Self::Runtime {
message: message.into(),
source: None,
}
}
pub fn configuration(message: impl Into<String>, field: Option<String>) -> Self {
Self::Configuration {
message: message.into(),
field,
}
}
pub fn service_lifecycle(
message: impl Into<String>,
service: impl Into<String>,
state: impl Into<String>,
) -> Self {
Self::ServiceLifecycle {
message: message.into(),
service: service.into(),
state: state.into(),
}
}
pub fn protocol(
message: impl Into<String>,
expected: Option<String>,
received: Option<String>,
) -> Self {
Self::Protocol {
message: message.into(),
expected,
received,
}
}
pub fn invalid_topic_pattern(pattern: impl Into<String>) -> Self {
Self::InvalidTopicPattern {
pattern: pattern.into(),
}
}
pub fn subscription_error(message: impl Into<String>, topic: Option<String>) -> Self {
Self::SubscriptionError {
message: message.into(),
topic,
}
}
pub fn invalid_request(message: impl Into<String>, context: Option<String>) -> Self {
Self::InvalidRequest {
message: message.into(),
context,
}
}
pub fn is_retryable(&self) -> bool {
match self {
Error::Transport { .. } => true,
Error::Connection { .. } => true,
Error::Timeout { .. } => true,
Error::Runtime { .. } => true,
Error::Io { .. } => true,
Error::ServiceNotFound { .. } => false,
Error::MethodNotFound { .. } => false,
Error::Serialization { .. } => false,
Error::Configuration { .. } => false,
Error::ServiceLifecycle { .. } => false,
Error::Protocol { .. } => false,
Error::InvalidTopicPattern { .. } => false,
Error::SubscriptionError { .. } => false,
Error::InvalidRequest { .. } => false,
}
}
pub fn category(&self) -> &'static str {
match self {
Error::Transport { .. } => "transport",
Error::ServiceNotFound { .. } => "service_discovery",
Error::MethodNotFound { .. } => "method_resolution",
Error::Serialization { .. } => "serialization",
Error::Connection { .. } => "connection",
Error::Timeout { .. } => "timeout",
Error::Runtime { .. } => "runtime",
Error::Configuration { .. } => "configuration",
Error::Io { .. } => "io",
Error::ServiceLifecycle { .. } => "service_lifecycle",
Error::Protocol { .. } => "protocol",
Error::InvalidTopicPattern { .. } => "topic_validation",
Error::SubscriptionError { .. } => "subscription",
Error::InvalidRequest { .. } => "request_validation",
}
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io {
message: err.to_string(),
source: err,
}
}
}
impl From<bincode::Error> for Error {
fn from(err: bincode::Error) -> Self {
Error::serialization("Bincode serialization failed", err)
}
}
impl From<tokio::task::JoinError> for Error {
fn from(err: tokio::task::JoinError) -> Self {
Error::runtime("Task join failed", err)
}
}
impl From<String> for Error {
fn from(msg: String) -> Self {
Error::runtime_msg(msg)
}
}
impl From<&str> for Error {
fn from(msg: &str) -> Self {
Error::runtime_msg(msg.to_string())
}
}
impl Error {
pub fn from_std<E>(err: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Error::runtime(err.to_string(), err)
}
}
pub type Result<T> = std::result::Result<T, Error>;