qubit_event_bus/core/event_bus_error.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Error type returned by event bus operations.
11
12use std::error::Error;
13use std::fmt::{
14 self,
15 Display,
16 Formatter,
17};
18use std::time::Duration;
19
20/// Result type used by event bus operations.
21pub type EventBusResult<T> = Result<T, EventBusError>;
22
23/// Error returned by event bus configuration, publishing, or subscription work.
24#[derive(Debug, Clone, Eq, PartialEq)]
25pub enum EventBusError {
26 /// Operation requires a started event bus.
27 NotStarted,
28 /// The event bus could not be started.
29 StartFailed {
30 /// Human-readable startup failure reason.
31 message: String,
32 },
33 /// An argument value is invalid.
34 InvalidArgument {
35 /// Argument name.
36 field: &'static str,
37 /// Human-readable validation message.
38 message: String,
39 },
40 /// A required builder field is missing.
41 MissingField {
42 /// Missing field name.
43 field: &'static str,
44 },
45 /// Subscriber handler failed.
46 HandlerFailed {
47 /// Human-readable failure message.
48 message: String,
49 },
50 /// Subscriber handler or interceptor panicked.
51 HandlerPanicked,
52 /// A configured interceptor failed while processing an event.
53 InterceptorFailed {
54 /// Interceptor phase.
55 phase: &'static str,
56 /// Human-readable failure message.
57 message: String,
58 },
59 /// A configured error handler failed while processing another error.
60 ErrorHandlerFailed {
61 /// Error handling phase.
62 phase: &'static str,
63 /// Human-readable failure message.
64 message: String,
65 },
66 /// Dead-letter routing failed after a terminal subscriber failure.
67 DeadLetterFailed {
68 /// Human-readable routing failure message.
69 message: String,
70 },
71 /// Managed executor rejected event processing work.
72 ExecutionRejected {
73 /// Human-readable rejection reason.
74 message: String,
75 },
76 /// Graceful shutdown did not complete before the configured timeout.
77 ShutdownTimedOut {
78 /// Timeout used for the shutdown wait.
79 timeout: Duration,
80 },
81 /// Shared state lock was poisoned.
82 LockPoisoned {
83 /// Shared resource name.
84 resource: &'static str,
85 },
86 /// A type-erased event or handler had an unexpected payload type.
87 TypeMismatch {
88 /// Expected Rust type name.
89 expected: &'static str,
90 /// Actual Rust type name.
91 actual: &'static str,
92 },
93 /// Operation is not supported by this backend.
94 UnsupportedOperation {
95 /// Operation name or feature category.
96 operation: &'static str,
97 },
98}
99
100impl EventBusError {
101 /// Creates [`EventBusError::NotStarted`].
102 ///
103 /// # Returns
104 /// Error indicating that the bus must be started first.
105 pub const fn not_started() -> Self {
106 Self::NotStarted
107 }
108
109 /// Creates [`EventBusError::StartFailed`].
110 ///
111 /// # Parameters
112 /// - `message`: Startup failure details.
113 ///
114 /// # Returns
115 /// Startup error with context.
116 pub fn start_failed(message: impl Into<String>) -> Self {
117 Self::StartFailed {
118 message: message.into(),
119 }
120 }
121
122 /// Creates [`EventBusError::InvalidArgument`].
123 ///
124 /// # Parameters
125 /// - `field`: Argument name.
126 /// - `message`: Validation message.
127 ///
128 /// # Returns
129 /// Validation error with field context.
130 pub fn invalid_argument(field: &'static str, message: impl Into<String>) -> Self {
131 Self::InvalidArgument {
132 field,
133 message: message.into(),
134 }
135 }
136
137 /// Creates [`EventBusError::MissingField`].
138 ///
139 /// # Parameters
140 /// - `field`: Missing builder field.
141 ///
142 /// # Returns
143 /// Builder validation error.
144 pub const fn missing_field(field: &'static str) -> Self {
145 Self::MissingField { field }
146 }
147
148 /// Creates [`EventBusError::HandlerFailed`].
149 ///
150 /// # Parameters
151 /// - `message`: Handler failure description.
152 ///
153 /// # Returns
154 /// Handler failure error.
155 pub fn handler_failed(message: impl Into<String>) -> Self {
156 Self::HandlerFailed {
157 message: message.into(),
158 }
159 }
160
161 /// Creates [`EventBusError::HandlerPanicked`].
162 ///
163 /// # Returns
164 /// Handler panic error used by subscriber failure handling.
165 pub const fn handler_panicked() -> Self {
166 Self::HandlerPanicked
167 }
168
169 /// Creates [`EventBusError::InterceptorFailed`].
170 ///
171 /// # Parameters
172 /// - `phase`: Interceptor phase.
173 /// - `message`: Interceptor failure details.
174 ///
175 /// # Returns
176 /// Interceptor failure with context.
177 pub fn interceptor_failed(phase: &'static str, message: impl Into<String>) -> Self {
178 Self::InterceptorFailed {
179 phase,
180 message: message.into(),
181 }
182 }
183
184 /// Creates [`EventBusError::ErrorHandlerFailed`].
185 ///
186 /// # Parameters
187 /// - `phase`: Error handling phase.
188 /// - `message`: Handler failure details.
189 ///
190 /// # Returns
191 /// Error handler failure with context.
192 pub fn error_handler_failed(phase: &'static str, message: impl Into<String>) -> Self {
193 Self::ErrorHandlerFailed {
194 phase,
195 message: message.into(),
196 }
197 }
198
199 /// Creates [`EventBusError::DeadLetterFailed`].
200 ///
201 /// # Parameters
202 /// - `message`: Dead-letter routing failure details.
203 ///
204 /// # Returns
205 /// Dead-letter failure with context.
206 pub fn dead_letter_failed(message: impl Into<String>) -> Self {
207 Self::DeadLetterFailed {
208 message: message.into(),
209 }
210 }
211
212 /// Creates [`EventBusError::ExecutionRejected`].
213 ///
214 /// # Parameters
215 /// - `message`: Executor rejection details.
216 ///
217 /// # Returns
218 /// Rejection error with executor context.
219 pub fn execution_rejected(message: impl Into<String>) -> Self {
220 Self::ExecutionRejected {
221 message: message.into(),
222 }
223 }
224
225 /// Creates [`EventBusError::ShutdownTimedOut`].
226 ///
227 /// # Parameters
228 /// - `timeout`: Timeout that elapsed before shutdown completed.
229 ///
230 /// # Returns
231 /// Shutdown timeout error with the configured duration.
232 pub const fn shutdown_timed_out(timeout: Duration) -> Self {
233 Self::ShutdownTimedOut { timeout }
234 }
235
236 /// Creates [`EventBusError::LockPoisoned`].
237 ///
238 /// # Parameters
239 /// - `resource`: Name of the poisoned shared state.
240 ///
241 /// # Returns
242 /// Lock-poisoning error.
243 pub const fn lock_poisoned(resource: &'static str) -> Self {
244 Self::LockPoisoned { resource }
245 }
246
247 /// Creates [`EventBusError::TypeMismatch`].
248 ///
249 /// # Parameters
250 /// - `expected`: Expected type name.
251 /// - `actual`: Actual type name.
252 ///
253 /// # Returns
254 /// Type-erasure mismatch error.
255 pub const fn type_mismatch(expected: &'static str, actual: &'static str) -> Self {
256 Self::TypeMismatch { expected, actual }
257 }
258
259 /// Creates [`EventBusError::UnsupportedOperation`].
260 ///
261 /// # Parameters
262 /// - `operation`: Operation name or feature category.
263 ///
264 /// # Returns
265 /// Error indicating that the current backend does not support the operation.
266 pub const fn unsupported_operation(operation: &'static str) -> Self {
267 Self::UnsupportedOperation { operation }
268 }
269
270 /// Returns a stable symbolic error kind.
271 ///
272 /// # Returns
273 /// Static string useful for diagnostics and dead-letter metadata.
274 pub const fn kind(&self) -> &'static str {
275 match self {
276 Self::NotStarted => "not_started",
277 Self::StartFailed { .. } => "start_failed",
278 Self::InvalidArgument { .. } => "invalid_argument",
279 Self::MissingField { .. } => "missing_field",
280 Self::HandlerFailed { .. } => "handler_failed",
281 Self::HandlerPanicked => "handler_panicked",
282 Self::InterceptorFailed { .. } => "interceptor_failed",
283 Self::ErrorHandlerFailed { .. } => "error_handler_failed",
284 Self::DeadLetterFailed { .. } => "dead_letter_failed",
285 Self::ExecutionRejected { .. } => "execution_rejected",
286 Self::ShutdownTimedOut { .. } => "shutdown_timed_out",
287 Self::LockPoisoned { .. } => "lock_poisoned",
288 Self::TypeMismatch { .. } => "type_mismatch",
289 Self::UnsupportedOperation { .. } => "unsupported_operation",
290 }
291 }
292}
293
294impl Display for EventBusError {
295 /// Formats the error for logs and assertions.
296 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
297 match self {
298 Self::NotStarted => write!(formatter, "the EventBus has not been started"),
299 Self::StartFailed { message } => {
300 write!(formatter, "failed to start EventBus: {message}")
301 }
302 Self::InvalidArgument { field, message } => {
303 write!(formatter, "invalid argument `{field}`: {message}")
304 }
305 Self::MissingField { field } => write!(formatter, "missing required field `{field}`"),
306 Self::HandlerFailed { message } => write!(formatter, "event handler failed: {message}"),
307 Self::HandlerPanicked => write!(formatter, "event handler panicked"),
308 Self::InterceptorFailed { phase, message } => {
309 write!(formatter, "{phase} interceptor failed: {message}")
310 }
311 Self::ErrorHandlerFailed { phase, message } => {
312 write!(formatter, "{phase} error handler failed: {message}")
313 }
314 Self::DeadLetterFailed { message } => {
315 write!(formatter, "dead-letter routing failed: {message}")
316 }
317 Self::ExecutionRejected { message } => {
318 write!(formatter, "event processing task was rejected: {message}")
319 }
320 Self::ShutdownTimedOut { timeout } => {
321 write!(formatter, "event bus shutdown timed out after {timeout:?}")
322 }
323 Self::LockPoisoned { resource } => {
324 write!(formatter, "shared state lock was poisoned: {resource}")
325 }
326 Self::TypeMismatch { expected, actual } => {
327 write!(
328 formatter,
329 "event payload type mismatch: expected {expected}, got {actual}"
330 )
331 }
332 Self::UnsupportedOperation { operation } => {
333 write!(formatter, "unsupported event bus operation: {operation}")
334 }
335 }
336 }
337}
338
339impl Error for EventBusError {}