1use std::time::Duration;
2use thiserror::Error;
3
4#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum HexeractError {
11 #[error("no handler registered for `{command_type}`")]
13 HandlerNotFound {
14 command_type: &'static str,
16 },
17
18 #[error("handler failed: {source}")]
20 HandlerFailed {
21 #[source]
23 source: Box<dyn std::error::Error + Send + Sync>,
24 },
25
26 #[error("dispatch of `{type_name}` timed out after {duration:?}")]
28 #[non_exhaustive]
29 Timeout {
30 type_name: &'static str,
32 duration: Duration,
34 },
35
36 #[error("dispatch error: {0}")]
38 Dispatch(String),
39}
40
41impl HexeractError {
42 pub fn handler_failed(source: impl std::error::Error + Send + Sync + 'static) -> Self {
44 Self::HandlerFailed {
45 source: Box::new(source),
46 }
47 }
48
49 #[must_use]
54 pub fn timeout(type_name: &'static str, duration: Duration) -> Self {
55 Self::Timeout {
56 type_name,
57 duration,
58 }
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn handler_not_found_display() {
68 let err = HexeractError::HandlerNotFound {
69 command_type: "RegisterUser",
70 };
71 assert_eq!(err.to_string(), "no handler registered for `RegisterUser`");
72 }
73
74 #[test]
75 fn timeout_display_shows_type_name_and_duration() {
76 let err = HexeractError::Timeout {
77 type_name: "my::RegisterUser",
78 duration: Duration::from_secs(5),
79 };
80 let rendered = err.to_string();
81 assert!(rendered.contains("RegisterUser"));
82 assert!(rendered.contains("5s"));
83 }
84
85 #[test]
86 fn handler_failed_preserves_source() {
87 let original = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
88 let err = HexeractError::handler_failed(original);
89 assert!(err.to_string().contains("handler failed"));
90 assert!(std::error::Error::source(&err).is_some());
91 }
92}