1use std::fmt;
9
10#[derive(Debug, Clone)]
16pub enum ClientError {
17 ConnectionFailed(String),
19 ConnectionTimeout,
21 ConnectionClosed,
23 QueryFailed(String),
25 TransactionFailed(String),
27 InvalidConfig(String),
29 InvalidUrl(String),
31 AuthenticationFailed(String),
33 PoolExhausted,
35 PoolTimeout,
37 SerializationError(String),
39 IoError(String),
41 ProtocolError(String),
43 ServerError { code: i32, message: String },
45 NotConnected,
47 NoTransaction,
49 TransactionAlreadyStarted,
51}
52
53impl fmt::Display for ClientError {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 Self::ConnectionFailed(msg) => write!(f, "Connection failed: {}", msg),
57 Self::ConnectionTimeout => write!(f, "Connection timeout"),
58 Self::ConnectionClosed => write!(f, "Connection closed"),
59 Self::QueryFailed(msg) => write!(f, "Query failed: {}", msg),
60 Self::TransactionFailed(msg) => write!(f, "Transaction failed: {}", msg),
61 Self::InvalidConfig(msg) => write!(f, "Invalid configuration: {}", msg),
62 Self::InvalidUrl(msg) => write!(f, "Invalid URL: {}", msg),
63 Self::AuthenticationFailed(msg) => write!(f, "Authentication failed: {}", msg),
64 Self::PoolExhausted => write!(f, "Connection pool exhausted"),
65 Self::PoolTimeout => write!(f, "Connection pool timeout"),
66 Self::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
67 Self::IoError(msg) => write!(f, "IO error: {}", msg),
68 Self::ProtocolError(msg) => write!(f, "Protocol error: {}", msg),
69 Self::ServerError { code, message } => {
70 write!(f, "Server error [{}]: {}", code, message)
71 }
72 Self::NotConnected => write!(f, "Not connected"),
73 Self::NoTransaction => write!(f, "No transaction in progress"),
74 Self::TransactionAlreadyStarted => write!(f, "Transaction already started"),
75 }
76 }
77}
78
79impl std::error::Error for ClientError {}
80
81impl ClientError {
82 pub fn is_retryable(&self) -> bool {
84 matches!(
85 self,
86 Self::ConnectionTimeout
87 | Self::ConnectionClosed
88 | Self::PoolExhausted
89 | Self::PoolTimeout
90 | Self::IoError(_)
91 )
92 }
93
94 pub fn is_connection_error(&self) -> bool {
96 matches!(
97 self,
98 Self::ConnectionFailed(_)
99 | Self::ConnectionTimeout
100 | Self::ConnectionClosed
101 | Self::NotConnected
102 )
103 }
104
105 pub fn is_server_error(&self) -> bool {
107 matches!(self, Self::ServerError { .. })
108 }
109}
110
111#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_error_display() {
121 let err = ClientError::ConnectionFailed("refused".to_string());
122 assert_eq!(err.to_string(), "Connection failed: refused");
123
124 let err = ClientError::ServerError {
125 code: 500,
126 message: "Internal error".to_string(),
127 };
128 assert_eq!(err.to_string(), "Server error [500]: Internal error");
129 }
130
131 #[test]
132 fn test_is_retryable() {
133 assert!(ClientError::ConnectionTimeout.is_retryable());
134 assert!(ClientError::PoolExhausted.is_retryable());
135 assert!(!ClientError::QueryFailed("syntax".to_string()).is_retryable());
136 }
137
138 #[test]
139 fn test_is_connection_error() {
140 assert!(ClientError::ConnectionFailed("refused".to_string()).is_connection_error());
141 assert!(ClientError::NotConnected.is_connection_error());
142 assert!(!ClientError::QueryFailed("error".to_string()).is_connection_error());
143 }
144}