daedalus_core/
errors.rs

1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4/// Stable error codes for `daedalus-core`.
5///
6/// ```
7/// use daedalus_core::errors::CoreErrorCode;
8/// let code = CoreErrorCode::InvalidId;
9/// assert_eq!(format!("{code:?}"), "InvalidId");
10/// ```
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[non_exhaustive]
13#[serde(rename_all = "snake_case")]
14pub enum CoreErrorCode {
15    InvalidId,
16    InvalidTick,
17    InvalidSequence,
18    ChannelClosed,
19    ChannelFull,
20    ChannelEmpty,
21    Unsupported,
22    Internal,
23}
24
25/// Structured core error with a stable code and human-friendly message.
26///
27/// ```
28/// use daedalus_core::errors::{CoreError, CoreErrorCode};
29/// let err = CoreError::new(CoreErrorCode::InvalidId, "bad id");
30/// assert_eq!(err.code(), CoreErrorCode::InvalidId);
31/// assert_eq!(err.message(), "bad id");
32/// ```
33#[derive(Debug, Clone, Error, Serialize, Deserialize)]
34#[non_exhaustive]
35#[error("{code:?}: {message}")]
36pub struct CoreError {
37    code: CoreErrorCode,
38    message: String,
39}
40
41impl CoreError {
42    pub fn new(code: CoreErrorCode, message: impl Into<String>) -> Self {
43        Self {
44            code,
45            message: message.into(),
46        }
47    }
48
49    pub fn code(&self) -> CoreErrorCode {
50        self.code
51    }
52
53    pub fn message(&self) -> &str {
54        &self.message
55    }
56
57    /// Attach additional context while keeping the same code.
58    pub fn with_context(self, context: impl Into<String>) -> Self {
59        let combined = format!("{}: {}", context.into(), self.message);
60        Self {
61            code: self.code,
62            message: combined,
63        }
64    }
65}
66
67/// Convenience alias for core results.
68pub type CoreResult<T> = Result<T, CoreError>;
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn serde_round_trip() {
76        let err = CoreError::new(CoreErrorCode::InvalidId, "bad id");
77        let json = serde_json::to_string(&err).expect("serialize");
78        let back: CoreError = serde_json::from_str(&json).expect("deserialize");
79        assert_eq!(back.code(), CoreErrorCode::InvalidId);
80        assert_eq!(back.message(), "bad id");
81    }
82
83    #[test]
84    fn display_includes_code() {
85        let err = CoreError::new(CoreErrorCode::ChannelFull, "full");
86        let rendered = err.to_string();
87        assert!(rendered.contains("ChannelFull"));
88        assert!(rendered.contains("full"));
89    }
90}