1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum Error {
5 #[error("Connection error: {0}")]
6 ConnectionError(String),
7
8 #[error("Configuration error: {0}")]
9 ConfigError(String),
10
11 #[error("IO error: {0}")]
12 IoError(String),
13
14 #[error("Serialization error: {0}")]
15 SerializationError(#[from] postcard::Error),
16
17 #[error("Protocol error: {0}")]
18 ProtocolError(#[from] rivven_protocol::ProtocolError),
19
20 #[error("Server error: {0}")]
21 ServerError(String),
22
23 #[error("Authentication failed: {0}")]
24 AuthenticationFailed(String),
25
26 #[error("Invalid response")]
27 InvalidResponse,
28
29 #[error("Response too large: {0} bytes (max: {1})")]
30 ResponseTooLarge(usize, usize),
31
32 #[error("Circuit breaker open for server: {0}")]
33 CircuitBreakerOpen(String),
34
35 #[error("Connection pool exhausted: {0}")]
36 PoolExhausted(String),
37
38 #[error("All servers unavailable")]
39 AllServersUnavailable,
40
41 #[error("Request timeout")]
42 Timeout,
43
44 #[error("Timeout: {0}")]
45 TimeoutWithMessage(String),
46
47 #[error("{0}")]
48 Other(String),
49}
50
51impl From<std::io::Error> for Error {
52 fn from(err: std::io::Error) -> Self {
53 Error::IoError(err.to_string())
54 }
55}
56
57pub type Result<T> = std::result::Result<T, Error>;
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn test_error_display() {
65 assert_eq!(
66 Error::ConnectionError("refused".to_string()).to_string(),
67 "Connection error: refused"
68 );
69 assert_eq!(
70 Error::ServerError("internal error".to_string()).to_string(),
71 "Server error: internal error"
72 );
73 assert_eq!(
74 Error::AuthenticationFailed("bad password".to_string()).to_string(),
75 "Authentication failed: bad password"
76 );
77 assert_eq!(Error::InvalidResponse.to_string(), "Invalid response");
78 assert_eq!(
79 Error::ResponseTooLarge(1000, 500).to_string(),
80 "Response too large: 1000 bytes (max: 500)"
81 );
82 assert_eq!(
83 Error::CircuitBreakerOpen("server1".to_string()).to_string(),
84 "Circuit breaker open for server: server1"
85 );
86 assert_eq!(
87 Error::PoolExhausted("max connections".to_string()).to_string(),
88 "Connection pool exhausted: max connections"
89 );
90 assert_eq!(
91 Error::AllServersUnavailable.to_string(),
92 "All servers unavailable"
93 );
94 assert_eq!(Error::Timeout.to_string(), "Request timeout");
95 assert_eq!(
96 Error::TimeoutWithMessage("connect".to_string()).to_string(),
97 "Timeout: connect"
98 );
99 assert_eq!(
100 Error::Other("custom error".to_string()).to_string(),
101 "custom error"
102 );
103 }
104
105 #[test]
106 fn test_error_debug() {
107 let err = Error::ConnectionError("test".to_string());
108 let debug = format!("{:?}", err);
109 assert!(debug.contains("ConnectionError"));
110 assert!(debug.contains("test"));
111 }
112
113 #[test]
114 fn test_io_error_from() {
115 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
116 let err: Error = io_err.into();
117 assert!(matches!(err, Error::IoError(_)));
118 assert!(err.to_string().contains("file not found"));
119 }
120
121 #[test]
122 fn test_postcard_error_from() {
123 let invalid_data: &[u8] = &[];
125 let result: std::result::Result<String, postcard::Error> =
126 postcard::from_bytes(invalid_data);
127 assert!(result.is_err());
128 let postcard_err = result.unwrap_err();
129 let err: Error = postcard_err.into();
130 assert!(matches!(err, Error::SerializationError(_)));
131 }
132
133 #[test]
134 fn test_result_type() {
135 fn returns_ok() -> Result<i32> {
136 Ok(42)
137 }
138
139 fn returns_err() -> Result<i32> {
140 Err(Error::InvalidResponse)
141 }
142
143 assert!(returns_ok().is_ok());
144 assert_eq!(returns_ok().unwrap(), 42);
145 assert!(returns_err().is_err());
146 }
147}