1use std::fmt;
2
3#[derive(Debug, Clone)]
4pub enum EventError {
5 Validation {
6 message: String,
7 hint: Option<String>,
8 },
9 Database {
10 message: String,
11 },
12 Observer {
13 message: String,
14 },
15 PropagationStopped {
16 reason: String,
17 },
18}
19
20impl EventError {
21 pub fn validation(message: &str) -> Self {
22 Self::Validation {
23 message: message.to_string(),
24 hint: None,
25 }
26 }
27
28 pub fn validation_with_hint(message: &str, hint: &str) -> Self {
29 Self::Validation {
30 message: message.to_string(),
31 hint: Some(hint.to_string()),
32 }
33 }
34
35 pub fn database(message: &str) -> Self {
36 Self::Database {
37 message: message.to_string(),
38 }
39 }
40
41 pub fn observer(message: &str) -> Self {
42 Self::Observer {
43 message: message.to_string(),
44 }
45 }
46
47 pub fn propagation_stopped(reason: &str) -> Self {
48 Self::PropagationStopped {
49 reason: reason.to_string(),
50 }
51 }
52}
53
54impl fmt::Display for EventError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 EventError::Validation { message, hint } => {
58 write!(f, "Validation error: {}", message)?;
59 if let Some(hint) = hint {
60 write!(f, " (hint: {})", hint)?;
61 }
62 Ok(())
63 }
64 EventError::Database { message } => write!(f, "Database error: {}", message),
65 EventError::Observer { message } => write!(f, "Observer error: {}", message),
66 EventError::PropagationStopped { reason } => {
67 write!(f, "Event propagation stopped: {}", reason)
68 }
69 }
70 }
71}
72
73impl std::error::Error for EventError {}
74
75impl From<std::io::Error> for EventError {
76 fn from(err: std::io::Error) -> Self {
77 Self::database(&err.to_string())
78 }
79}
80
81impl From<sqlx::Error> for EventError {
82 fn from(err: sqlx::Error) -> Self {
83 Self::database(&err.to_string())
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[tokio::test]
92 async fn test_event_error_validation() {
93 let error = EventError::validation("Invalid email format");
94
95 match error {
96 EventError::Validation { message, hint } => {
97 assert_eq!(message, "Invalid email format");
98 assert!(hint.is_none());
99 }
100 _ => panic!("Expected validation error"),
101 }
102 }
103
104 #[tokio::test]
105 async fn test_event_error_validation_with_hint() {
106 let error =
107 EventError::validation_with_hint("Invalid email format", "Use format user@domain.com");
108
109 match error {
110 EventError::Validation { message, hint } => {
111 assert_eq!(message, "Invalid email format");
112 assert_eq!(hint.unwrap(), "Use format user@domain.com");
113 }
114 _ => panic!("Expected validation error"),
115 }
116 }
117
118 #[tokio::test]
119 async fn test_event_error_database() {
120 let error = EventError::database("Connection timeout");
121
122 match error {
123 EventError::Database { message, .. } => {
124 assert_eq!(message, "Connection timeout");
125 }
126 _ => panic!("Expected database error"),
127 }
128 }
129
130 #[tokio::test]
131 async fn test_event_error_observer() {
132 let error = EventError::observer("Observer failed to execute");
133
134 match error {
135 EventError::Observer { message, .. } => {
136 assert_eq!(message, "Observer failed to execute");
137 }
138 _ => panic!("Expected observer error"),
139 }
140 }
141
142 #[tokio::test]
143 async fn test_event_error_propagation_stopped() {
144 let error = EventError::propagation_stopped("User cancelled operation");
145
146 match error {
147 EventError::PropagationStopped { reason, .. } => {
148 assert_eq!(reason, "User cancelled operation");
149 }
150 _ => panic!("Expected propagation stopped error"),
151 }
152 }
153
154 #[tokio::test]
155 async fn test_event_error_display() {
156 let error = EventError::validation("Test error");
157 let display_message = format!("{}", error);
158 assert!(display_message.contains("Test error"));
159 }
160
161 #[tokio::test]
162 async fn test_event_error_debug() {
163 let error = EventError::validation("Test error");
164 let debug_message = format!("{:?}", error);
165 assert!(debug_message.contains("Validation"));
166 assert!(debug_message.contains("Test error"));
167 }
168
169 #[tokio::test]
170 async fn test_event_error_conversion_from_std_error() {
171 let std_error = std::io::Error::new(std::io::ErrorKind::Other, "IO error");
172 let event_error: EventError = std_error.into();
173
174 match event_error {
175 EventError::Database { message, .. } => {
176 assert!(message.contains("IO error"));
177 }
178 _ => panic!("Expected database error from std error conversion"),
179 }
180 }
181}