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 produced a value that is not the expected output type `{expected}`")]
43 #[non_exhaustive]
44 DowncastFailed {
45 expected: &'static str,
47 },
48
49 #[error("dispatch error: {0}")]
51 Dispatch(String),
52}
53
54impl HexeractError {
55 pub fn handler_failed(source: impl std::error::Error + Send + Sync + 'static) -> Self {
57 Self::HandlerFailed {
58 source: Box::new(source),
59 }
60 }
61
62 #[must_use]
67 pub fn timeout(type_name: &'static str, duration: Duration) -> Self {
68 Self::Timeout {
69 type_name,
70 duration,
71 }
72 }
73
74 #[must_use]
79 pub fn downcast_failed(expected: &'static str) -> Self {
80 Self::DowncastFailed { expected }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn handler_not_found_display() {
90 let err = HexeractError::HandlerNotFound {
91 command_type: "RegisterUser",
92 };
93 assert_eq!(err.to_string(), "no handler registered for `RegisterUser`");
94 }
95
96 #[test]
97 fn timeout_display_shows_type_name_and_duration() {
98 let err = HexeractError::Timeout {
99 type_name: "my::RegisterUser",
100 duration: Duration::from_secs(5),
101 };
102 let rendered = err.to_string();
103 assert!(rendered.contains("RegisterUser"));
104 assert!(rendered.contains("5s"));
105 }
106
107 #[test]
108 fn handler_failed_preserves_source() {
109 let original = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
110 let err = HexeractError::handler_failed(original);
111 assert!(err.to_string().contains("handler failed"));
112 assert!(std::error::Error::source(&err).is_some());
113 }
114
115 #[test]
116 fn downcast_failed_names_the_expected_output_type() {
117 let err = HexeractError::downcast_failed("u32");
118 let rendered = err.to_string();
119 assert!(rendered.contains("u32"));
120 assert!(matches!(err, HexeractError::DowncastFailed { expected } if expected == "u32"));
121 }
122}