1use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub enum KVOperation {
11 Set { key: String, value: String },
13 Get { key: String },
15 Delete { key: String },
17 Exists { key: String },
19}
20
21impl KVOperation {
22 pub fn key(&self) -> &str {
24 match self {
25 KVOperation::Set { key, .. } => key,
26 KVOperation::Get { key } => key,
27 KVOperation::Delete { key } => key,
28 KVOperation::Exists { key } => key,
29 }
30 }
31
32 pub fn operation_type(&self) -> &'static str {
34 match self {
35 KVOperation::Set { .. } => "SET",
36 KVOperation::Get { .. } => "GET",
37 KVOperation::Delete { .. } => "DELETE",
38 KVOperation::Exists { .. } => "EXISTS",
39 }
40 }
41
42 pub fn is_write_operation(&self) -> bool {
44 matches!(self, KVOperation::Set { .. } | KVOperation::Delete { .. })
45 }
46
47 pub fn is_read_operation(&self) -> bool {
49 !self.is_write_operation()
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
55pub enum KVResult {
56 Success,
58 NotFound,
60 Error(String),
62}
63
64impl KVResult {
65 pub fn is_success(&self) -> bool {
67 matches!(self, KVResult::Success)
68 }
69
70 pub fn is_not_found(&self) -> bool {
72 matches!(self, KVResult::NotFound)
73 }
74
75 pub fn is_error(&self) -> bool {
77 matches!(self, KVResult::Error(_))
78 }
79
80 pub fn error_message(&self) -> Option<&str> {
82 match self {
83 KVResult::Error(msg) => Some(msg),
84 _ => None,
85 }
86 }
87}
88
89impl From<StoreError> for KVResult {
90 fn from(error: StoreError) -> Self {
91 KVResult::Error(error.to_string())
92 }
93}
94
95#[derive(Error, Debug, Clone, PartialEq)]
97pub enum StoreError {
98 #[error("Invalid key: {0}")]
100 InvalidKey(String),
101
102 #[error("Value too large")]
104 ValueTooLarge,
105
106 #[error("Store is full")]
108 StoreFull,
109
110 #[error("Serialization error: {0}")]
112 SerializationError(String),
113
114 #[error("Invalid snapshot")]
116 InvalidSnapshot,
117
118 #[error("IO error: {0}")]
120 IoError(String),
121
122 #[error("Network error: {0}")]
124 NetworkError(String),
125
126 #[error("Consensus error: {0}")]
128 ConsensusError(String),
129
130 #[error("Operation timed out")]
132 Timeout,
133
134 #[error("Store is shutting down")]
136 ShuttingDown,
137
138 #[error("Internal error: {0}")]
140 Internal(String),
141}
142
143impl StoreError {
144 pub fn is_recoverable(&self) -> bool {
146 matches!(
147 self,
148 StoreError::NetworkError(_) | StoreError::Timeout | StoreError::ConsensusError(_)
149 )
150 }
151
152 pub fn is_client_error(&self) -> bool {
154 matches!(
155 self,
156 StoreError::InvalidKey(_) | StoreError::ValueTooLarge | StoreError::StoreFull
157 )
158 }
159
160 pub fn is_server_error(&self) -> bool {
162 matches!(
163 self,
164 StoreError::IoError(_) | StoreError::Internal(_) | StoreError::ShuttingDown
165 )
166 }
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct OperationBatch {
172 pub operations: Vec<KVOperation>,
174 pub batch_id: String,
176 pub created_at: u64,
178}
179
180impl OperationBatch {
181 pub fn new(operations: Vec<KVOperation>) -> Self {
183 Self {
184 operations,
185 batch_id: uuid::Uuid::new_v4().to_string(),
186 created_at: std::time::SystemTime::now()
187 .duration_since(std::time::UNIX_EPOCH)
188 .unwrap()
189 .as_millis() as u64,
190 }
191 }
192
193 pub fn size(&self) -> usize {
195 self.operations.len()
196 }
197
198 pub fn has_write_operations(&self) -> bool {
200 self.operations.iter().any(|op| op.is_write_operation())
201 }
202
203 pub fn is_read_only(&self) -> bool {
205 self.operations.iter().all(|op| op.is_read_operation())
206 }
207
208 pub fn affected_keys(&self) -> Vec<&str> {
210 self.operations.iter().map(|op| op.key()).collect()
211 }
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct BatchResult {
217 pub batch_id: String,
219 pub results: Vec<KVResult>,
221 pub success_count: usize,
223 pub failure_count: usize,
225 pub execution_time_ms: u64,
227}
228
229impl BatchResult {
230 pub fn new(batch_id: String, results: Vec<KVResult>, execution_time_ms: u64) -> Self {
232 let success_count = results.iter().filter(|r| r.is_success()).count();
233 let failure_count = results.len() - success_count;
234
235 Self {
236 batch_id,
237 results,
238 success_count,
239 failure_count,
240 execution_time_ms,
241 }
242 }
243
244 pub fn all_succeeded(&self) -> bool {
246 self.failure_count == 0
247 }
248
249 pub fn has_failures(&self) -> bool {
251 self.failure_count > 0
252 }
253
254 pub fn success_rate(&self) -> f64 {
256 if self.results.is_empty() {
257 0.0
258 } else {
259 (self.success_count as f64 / self.results.len() as f64) * 100.0
260 }
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 #[test]
269 fn test_kv_operation_properties() {
270 let set_op = KVOperation::Set {
271 key: "test_key".to_string(),
272 value: "test_value".to_string(),
273 };
274 let get_op = KVOperation::Get {
275 key: "test_key".to_string(),
276 };
277 let delete_op = KVOperation::Delete {
278 key: "test_key".to_string(),
279 };
280 let exists_op = KVOperation::Exists {
281 key: "test_key".to_string(),
282 };
283
284 assert_eq!(set_op.key(), "test_key");
286 assert_eq!(get_op.key(), "test_key");
287 assert_eq!(delete_op.key(), "test_key");
288 assert_eq!(exists_op.key(), "test_key");
289
290 assert_eq!(set_op.operation_type(), "SET");
292 assert_eq!(get_op.operation_type(), "GET");
293 assert_eq!(delete_op.operation_type(), "DELETE");
294 assert_eq!(exists_op.operation_type(), "EXISTS");
295
296 assert!(set_op.is_write_operation());
298 assert!(!get_op.is_write_operation());
299 assert!(delete_op.is_write_operation());
300 assert!(!exists_op.is_write_operation());
301
302 assert!(!set_op.is_read_operation());
303 assert!(get_op.is_read_operation());
304 assert!(!delete_op.is_read_operation());
305 assert!(exists_op.is_read_operation());
306 }
307
308 #[test]
309 fn test_kv_result_properties() {
310 let success = KVResult::Success;
311 let not_found = KVResult::NotFound;
312 let error = KVResult::Error("Test error".to_string());
313
314 assert!(success.is_success());
315 assert!(!success.is_not_found());
316 assert!(!success.is_error());
317
318 assert!(!not_found.is_success());
319 assert!(not_found.is_not_found());
320 assert!(!not_found.is_error());
321
322 assert!(!error.is_success());
323 assert!(!error.is_not_found());
324 assert!(error.is_error());
325 assert_eq!(error.error_message().unwrap(), "Test error");
326 }
327
328 #[test]
329 fn test_store_error_classification() {
330 let client_error = StoreError::InvalidKey("test".to_string());
331 let server_error = StoreError::Internal("test".to_string());
332 let recoverable_error = StoreError::NetworkError("test".to_string());
333
334 assert!(client_error.is_client_error());
335 assert!(!client_error.is_server_error());
336 assert!(!client_error.is_recoverable());
337
338 assert!(!server_error.is_client_error());
339 assert!(server_error.is_server_error());
340 assert!(!server_error.is_recoverable());
341
342 assert!(!recoverable_error.is_client_error());
343 assert!(!recoverable_error.is_server_error());
344 assert!(recoverable_error.is_recoverable());
345 }
346
347 #[test]
348 fn test_operation_batch() {
349 let operations = vec![
350 KVOperation::Set {
351 key: "key1".to_string(),
352 value: "value1".to_string(),
353 },
354 KVOperation::Get {
355 key: "key2".to_string(),
356 },
357 KVOperation::Delete {
358 key: "key3".to_string(),
359 },
360 ];
361
362 let batch = OperationBatch::new(operations);
363
364 assert_eq!(batch.size(), 3);
365 assert!(batch.has_write_operations());
366 assert!(!batch.is_read_only());
367
368 let affected_keys = batch.affected_keys();
369 assert_eq!(affected_keys, vec!["key1", "key2", "key3"]);
370 }
371
372 #[test]
373 fn test_batch_result() {
374 let results = vec![
375 KVResult::Success,
376 KVResult::NotFound,
377 KVResult::Error("test".to_string()),
378 ];
379
380 let batch_result = BatchResult::new("test_batch".to_string(), results, 100);
381
382 assert_eq!(batch_result.success_count, 1);
383 assert_eq!(batch_result.failure_count, 2);
384 assert!(!batch_result.all_succeeded());
385 assert!(batch_result.has_failures());
386 assert!((batch_result.success_rate() - 33.333333333333336).abs() < 0.0001);
387 }
388}