Skip to main content

hexeract_core/
error.rs

1use std::time::Duration;
2use thiserror::Error;
3
4/// Top-level error type for the Hexeract framework.
5///
6/// This enum is marked `#[non_exhaustive]` so that new variants can be added
7/// in minor versions without breaking downstream `match` arms.
8#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum HexeractError {
11    /// No handler was registered for the given command or query type.
12    #[error("no handler registered for `{command_type}`")]
13    HandlerNotFound {
14        /// The fully-qualified type name of the unregistered command or query.
15        command_type: &'static str,
16    },
17
18    /// A handler returned an error. The original error is preserved as source.
19    #[error("handler failed: {source}")]
20    HandlerFailed {
21        /// The original error returned by the handler.
22        #[source]
23        source: Box<dyn std::error::Error + Send + Sync>,
24    },
25
26    /// A dispatch exceeded its configured deadline.
27    #[error("dispatch timed out after {elapsed:?}")]
28    Timeout {
29        /// How long the dispatch ran before being cancelled.
30        elapsed: Duration,
31    },
32
33    /// A generic dispatch-level error with a human-readable message.
34    #[error("dispatch error: {0}")]
35    Dispatch(String),
36}
37
38impl HexeractError {
39    /// Wraps any `Send + Sync` error as a [`HexeractError::HandlerFailed`].
40    pub fn handler_failed(source: impl std::error::Error + Send + Sync + 'static) -> Self {
41        Self::HandlerFailed {
42            source: Box::new(source),
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn handler_not_found_display() {
53        let err = HexeractError::HandlerNotFound {
54            command_type: "RegisterUser",
55        };
56        assert_eq!(err.to_string(), "no handler registered for `RegisterUser`");
57    }
58
59    #[test]
60    fn timeout_display_shows_duration() {
61        let err = HexeractError::Timeout {
62            elapsed: Duration::from_secs(5),
63        };
64        assert!(err.to_string().contains("5s"));
65    }
66
67    #[test]
68    fn handler_failed_preserves_source() {
69        let original = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
70        let err = HexeractError::handler_failed(original);
71        assert!(err.to_string().contains("handler failed"));
72        assert!(std::error::Error::source(&err).is_some());
73    }
74}