1use thiserror::Error;
5
6#[derive(Debug, Error)]
8pub enum RepError {
9 #[error("node is not master: current master is {master:?}")]
13 NotMaster {
14 master: Option<String>,
16 },
17
18 #[error("node is not replica")]
21 NotReplica,
22
23 #[error("replication group does not exist: {0}")]
26 GroupNotFound(String),
27
28 #[error("node {0} not found in group")]
31 NodeNotFound(String),
32
33 #[error("election failed: {0}")]
36 ElectionFailed(String),
37
38 #[error("insufficient acks: needed {needed}, got {received}")]
41 InsufficientAcks {
42 needed: u32,
44 received: u32,
46 },
47
48 #[error("replica consistency timeout after {0:?}")]
51 ConsistencyTimeout(std::time::Duration),
52
53 #[error("replica lag too high: {lag_ms}ms exceeds limit {limit_ms}ms")]
55 ReplicaLagExceeded {
56 lag_ms: u64,
58 limit_ms: u64,
60 },
61
62 #[error("rollback required: from VLSN {from} to {to}")]
65 RollbackRequired {
66 from: i64,
68 to: i64,
70 },
71
72 #[error("network error: {0}")]
74 NetworkError(String),
75
76 #[error(
81 "frame corrupted: vlsn={vlsn} expected_crc={expected:#010x} actual_crc={actual:#010x}"
82 )]
83 FrameCorrupted { vlsn: u64, expected: u32, actual: u32 },
84
85 #[error("protocol error: {0}")]
88 ProtocolError(String),
89
90 #[error("node state error: {0}")]
92 StateError(String),
93
94 #[error("database error: {0}")]
96 DatabaseError(String),
97
98 #[error("configuration error: {0}")]
100 ConfigError(String),
101
102 #[error("shutdown in progress")]
104 ShutdownInProgress,
105
106 #[error("invalid state transition: {0}")]
108 InvalidStateTransition(String),
109
110 #[error("node already exists: {0}")]
112 NodeAlreadyExists(String),
113
114 #[error("channel closed: {0}")]
116 ChannelClosed(String),
117
118 #[error("service not found: {0}")]
120 ServiceNotFound(String),
121
122 #[error("subscription error: {0}")]
124 SubscriptionError(String),
125
126 #[error("network restore error: {0}")]
128 NetworkRestoreError(String),
129
130 #[error("environment closed")]
132 EnvironmentClosed,
133
134 #[error("invalid state: {0}")]
136 InvalidState(String),
137}
138
139pub type Result<T> = std::result::Result<T, RepError>;
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use std::time::Duration;
146
147 #[test]
148 fn test_not_master_with_master() {
149 let err = RepError::NotMaster { master: Some("node1".to_string()) };
150 assert_eq!(
151 err.to_string(),
152 "node is not master: current master is Some(\"node1\")"
153 );
154 }
155
156 #[test]
157 fn test_not_master_without_master() {
158 let err = RepError::NotMaster { master: None };
159 assert_eq!(
160 err.to_string(),
161 "node is not master: current master is None"
162 );
163 }
164
165 #[test]
166 fn test_not_replica() {
167 let err = RepError::NotReplica;
168 assert_eq!(err.to_string(), "node is not replica");
169 }
170
171 #[test]
172 fn test_group_not_found() {
173 let err = RepError::GroupNotFound("mygroup".to_string());
174 assert_eq!(
175 err.to_string(),
176 "replication group does not exist: mygroup"
177 );
178 }
179
180 #[test]
181 fn test_node_not_found() {
182 let err = RepError::NodeNotFound("node2".to_string());
183 assert_eq!(err.to_string(), "node node2 not found in group");
184 }
185
186 #[test]
187 fn test_election_failed() {
188 let err = RepError::ElectionFailed("no quorum".to_string());
189 assert_eq!(err.to_string(), "election failed: no quorum");
190 }
191
192 #[test]
193 fn test_insufficient_acks() {
194 let err = RepError::InsufficientAcks { needed: 3, received: 1 };
195 assert_eq!(err.to_string(), "insufficient acks: needed 3, got 1");
196 }
197
198 #[test]
199 fn test_consistency_timeout() {
200 let err = RepError::ConsistencyTimeout(Duration::from_secs(5));
201 assert_eq!(err.to_string(), "replica consistency timeout after 5s");
202 }
203
204 #[test]
205 fn test_replica_lag_exceeded() {
206 let err = RepError::ReplicaLagExceeded { lag_ms: 5000, limit_ms: 1000 };
207 assert_eq!(
208 err.to_string(),
209 "replica lag too high: 5000ms exceeds limit 1000ms"
210 );
211 }
212
213 #[test]
214 fn test_rollback_required() {
215 let err = RepError::RollbackRequired { from: 100, to: 50 };
216 assert_eq!(err.to_string(), "rollback required: from VLSN 100 to 50");
217 }
218
219 #[test]
220 fn test_network_error() {
221 let err = RepError::NetworkError("connection refused".to_string());
222 assert_eq!(err.to_string(), "network error: connection refused");
223 }
224
225 #[test]
226 fn test_protocol_error() {
227 let err = RepError::ProtocolError("version mismatch".to_string());
228 assert_eq!(err.to_string(), "protocol error: version mismatch");
229 }
230
231 #[test]
232 fn test_state_error() {
233 let err = RepError::StateError("not initialized".to_string());
234 assert_eq!(err.to_string(), "node state error: not initialized");
235 }
236
237 #[test]
238 fn test_database_error() {
239 let err = RepError::DatabaseError("corrupt log".to_string());
240 assert_eq!(err.to_string(), "database error: corrupt log");
241 }
242
243 #[test]
244 fn test_config_error() {
245 let err = RepError::ConfigError("invalid port".to_string());
246 assert_eq!(err.to_string(), "configuration error: invalid port");
247 }
248
249 #[test]
250 fn test_shutdown_in_progress() {
251 let err = RepError::ShutdownInProgress;
252 assert_eq!(err.to_string(), "shutdown in progress");
253 }
254
255 #[test]
256 fn test_result_type_alias() {
257 let ok: Result<u32> = Ok(42);
258 assert!(ok.is_ok_and(|v| v == 42));
259
260 let err: Result<u32> = Err(RepError::NotReplica);
261 assert!(err.is_err());
262 }
263}