use super::vector_clock::VectorClock;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MessageType {
SyncRequest = 0x01,
SyncResponse = 0x02,
RowDelta = 0x03,
Acknowledgment = 0x04,
Heartbeat = 0x05,
ConflictNotification = 0x06,
Error = 0xFF,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum SyncMode {
Incremental, Full, }
pub type RowId = Vec<u8>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Operation {
Insert,
Update { columns: Vec<String> }, Delete,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncRequest {
pub client_id: Uuid,
pub last_sync_version: u64,
pub changed_tables: Vec<String>,
pub pending_changes: u32,
pub vector_clock: VectorClock,
pub sync_mode: SyncMode,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncResponse {
pub server_version: u64,
pub delta: Vec<RowDelta>,
pub conflicts: Vec<super::conflicts::Conflict>,
pub continuation_token: Option<String>,
pub vector_clock: VectorClock,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RowDelta {
pub table: String,
pub operation: Operation,
pub row_id: RowId,
pub data: Vec<u8>,
pub vector_clock: VectorClock,
pub timestamp: DateTime<Utc>,
pub checksum: u32,
}
impl RowDelta {
pub fn calculate_checksum(&self) -> u32 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
self.data.hash(&mut hasher);
hasher.finish() as u32
}
pub fn verify_checksum(&self) -> bool {
self.checksum == self.calculate_checksum()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchDelta {
pub batch_id: Uuid,
pub deltas: Vec<RowDelta>,
pub compressed: bool, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Acknowledgment {
pub new_version: u64,
pub applied_count: u32,
pub failed: Vec<FailedChange>,
pub vector_clock: VectorClock,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FailedChange {
pub row_id: RowId,
pub reason: String,
pub conflict: Option<super::conflicts::Conflict>,
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn test_row_delta_checksum() {
let delta = RowDelta {
table: "users".to_string(),
operation: Operation::Insert,
row_id: vec![1, 2, 3],
data: vec![4, 5, 6],
vector_clock: VectorClock::default(),
timestamp: Utc::now(),
checksum: 0,
};
let checksum = delta.calculate_checksum();
assert!(checksum > 0);
let mut delta = delta;
delta.checksum = checksum;
assert!(delta.verify_checksum());
}
#[test]
fn test_sync_request_serialization() {
let request = SyncRequest {
client_id: Uuid::new_v4(),
last_sync_version: 42,
changed_tables: vec!["users".to_string(), "orders".to_string()],
pending_changes: 10,
vector_clock: VectorClock::default(),
sync_mode: SyncMode::Incremental,
};
let bytes = bincode::serialize(&request).unwrap();
let deserialized: SyncRequest = bincode::deserialize(&bytes).unwrap();
assert_eq!(request.client_id, deserialized.client_id);
assert_eq!(request.last_sync_version, deserialized.last_sync_version);
}
}