use std::fmt;
use thiserror::Error;
use uuid::Uuid;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("Failed to publish event: {0}")]
PublishError(String),
#[error("Failed to subscribe: {0}")]
SubscriptionError(String),
#[error("Event type not registered: {type_name}")]
EventNotRegistered {
type_name: String,
},
#[error("Subscription not found: {id}")]
SubscriptionNotFound {
id: Uuid,
},
#[error("Handler error: {0}")]
HandlerError(String),
#[error("Event bus is shutting down")]
ShuttingDown,
#[error("Failed to send through channel")]
ChannelSendError,
#[error("Failed to receive from channel")]
ChannelReceiveError,
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Internal error: {0}")]
Internal(String),
}
impl Error {
pub fn internal(msg: impl Into<String>) -> Self {
Error::Internal(msg.into())
}
pub fn handler(msg: impl Into<String>) -> Self {
Error::HandlerError(msg.into())
}
pub fn is_shutdown(&self) -> bool {
matches!(self, Error::ShuttingDown)
}
pub fn is_channel_error(&self) -> bool {
matches!(self, Error::ChannelSendError | Error::ChannelReceiveError)
}
}
#[derive(Debug)]
pub struct ErrorContext {
pub event_id: Option<Uuid>,
pub event_type: Option<String>,
pub handler_name: Option<String>,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
impl Default for ErrorContext {
fn default() -> Self {
Self::new()
}
}
impl ErrorContext {
pub fn new() -> Self {
Self {
event_id: None,
event_type: None,
handler_name: None,
timestamp: chrono::Utc::now(),
}
}
pub fn with_event_id(mut self, id: Uuid) -> Self {
self.event_id = Some(id);
self
}
pub fn with_event_type(mut self, event_type: impl Into<String>) -> Self {
self.event_type = Some(event_type.into());
self
}
pub fn with_handler(mut self, name: impl Into<String>) -> Self {
self.handler_name = Some(name.into());
self
}
}
impl fmt::Display for ErrorContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error at {}", self.timestamp)?;
if let Some(id) = &self.event_id {
write!(f, " [event_id: {}]", id)?;
}
if let Some(event_type) = &self.event_type {
write!(f, " [type: {}]", event_type)?;
}
if let Some(handler) = &self.handler_name {
write!(f, " [handler: {}]", handler)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = Error::PublishError("test error".to_string());
assert_eq!(err.to_string(), "Failed to publish event: test error");
}
#[test]
fn test_error_is_shutdown() {
assert!(Error::ShuttingDown.is_shutdown());
assert!(!Error::internal("test").is_shutdown());
}
#[test]
fn test_error_context() {
let ctx = ErrorContext::new()
.with_event_id(Uuid::max())
.with_event_type("TestEvent")
.with_handler("test_handler");
let display = ctx.to_string();
assert!(display.contains("TestEvent"));
assert!(display.contains("test_handler"));
}
}