sierradb_protocol/
error.rs

1use std::fmt;
2
3use strum::{AsRefStr, Display, EnumString, IntoStaticStr};
4
5/// Error codes used in Sierra server responses.
6///
7/// These codes follow Redis conventions where applicable, with Sierra-specific
8/// extensions for domain-specific error conditions.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, AsRefStr, Display, EnumString, IntoStaticStr)]
10#[strum(serialize_all = "UPPERCASE")]
11pub enum ErrorCode {
12    // Redis-standard error codes
13    /// Cluster is down or partitions unavailable
14    ClusterDown,
15    /// Temporary failure, client should retry
16    TryAgain,
17    /// Request timed out
18    Timeout,
19    /// Invalid syntax in command arguments
20    Syntax,
21    /// Invalid argument provided
22    InvalidArg,
23    /// Resource not found
24    NotFound,
25
26    // Sierra-specific error codes
27    /// Too many request forwards (loop prevention)
28    MaxForwards,
29    /// Circuit breaker is open
30    CircuitOpen,
31    /// Confirmation operation failed
32    ConfirmFailed,
33    /// Replication confirmation failed
34    ReplConfirmFailed,
35    /// System clock error
36    ClockErr,
37    /// Read operation error
38    ReadErr,
39    /// Write operation error
40    WriteErr,
41    /// Remote communication error
42    RemoteErr,
43    /// Network noise protocol error
44    NoiseErr,
45    /// Transport layer error
46    TransportErr,
47    /// Swarm initialization error
48    SwarmErr,
49    /// Actor not running
50    ActorDown,
51    /// Actor stopped or died
52    ActorStopped,
53    /// Actor mailbox is full
54    MailboxFull,
55
56    // WriteError-specific codes
57    /// All replicas failed
58    AllReplicasFailed,
59    /// Buffer was evicted
60    BufferEvicted,
61    /// Buffer is full
62    BufferFull,
63    /// Database operation failed
64    DbOpFailed,
65    /// Insufficient healthy replicas
66    InsufficientReplicas,
67    /// Invalid sender
68    InvalidSender,
69    /// Stale write detected
70    StaleWrite,
71    /// Missing expected partition sequence
72    MissingPartSeq,
73    /// Partition not owned by this node
74    PartNotOwned,
75    /// Replication quorum failed
76    QuorumFailed,
77    /// Remote operation failed
78    RemoteOpFailed,
79    /// Sequence conflict detected
80    SeqConflict,
81    /// Wrong expected sequence
82    WrongSeq,
83    /// Wrong expected version
84    WrongVer,
85
86    // Subscription-specific error codes
87    /// Redirect request to another node
88    Redirect,
89}
90
91impl ErrorCode {
92    /// Format this error code with a message.
93    ///
94    /// # Example
95    /// ```
96    /// use sierradb_protocol::ErrorCode;
97    ///
98    /// let error = ErrorCode::InvalidArg.with_message("invalid timestamp format");
99    /// assert_eq!(error, "INVALIDARG invalid timestamp format");
100    /// ```
101    pub fn with_message(&self, message: impl fmt::Display) -> String {
102        format!("{self} {message}")
103    }
104
105    /// Check if this error code indicates a temporary failure that should be
106    /// retried.
107    pub fn is_retryable(&self) -> bool {
108        matches!(
109            self,
110            ErrorCode::TryAgain
111                | ErrorCode::Timeout
112                | ErrorCode::CircuitOpen
113                | ErrorCode::ActorDown
114                | ErrorCode::ActorStopped
115                | ErrorCode::MailboxFull
116        )
117    }
118
119    /// Check if this error code indicates a cluster availability issue.
120    pub fn is_cluster_issue(&self) -> bool {
121        matches!(
122            self,
123            ErrorCode::ClusterDown
124                | ErrorCode::ReadErr
125                | ErrorCode::WriteErr
126                | ErrorCode::RemoteErr
127                | ErrorCode::NoiseErr
128                | ErrorCode::TransportErr
129                | ErrorCode::SwarmErr
130        )
131    }
132
133    /// Check if this error code indicates a client argument error.
134    pub fn is_client_error(&self) -> bool {
135        matches!(self, ErrorCode::Syntax | ErrorCode::InvalidArg)
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_error_code_parsing() {
145        assert_eq!(
146            "CLUSTERDOWN".parse::<ErrorCode>().unwrap(),
147            ErrorCode::ClusterDown
148        );
149        assert_eq!(
150            "INVALIDARG".parse::<ErrorCode>().unwrap(),
151            ErrorCode::InvalidArg
152        );
153        assert!("UNKNOWN".parse::<ErrorCode>().is_err());
154    }
155
156    #[test]
157    fn test_error_categories() {
158        assert!(ErrorCode::TryAgain.is_retryable());
159        assert!(!ErrorCode::Syntax.is_retryable());
160
161        assert!(ErrorCode::ClusterDown.is_cluster_issue());
162        assert!(!ErrorCode::InvalidArg.is_cluster_issue());
163
164        assert!(ErrorCode::InvalidArg.is_client_error());
165        assert!(!ErrorCode::ClusterDown.is_client_error());
166    }
167
168    #[test]
169    fn test_with_message() {
170        let error = ErrorCode::InvalidArg.with_message("invalid timestamp format");
171        assert_eq!(error, "INVALIDARG invalid timestamp format");
172    }
173}