mod change;
pub mod conflict;
pub mod engine;
pub mod manifest;
pub use change::{LocalChange, RemoteChange, SyncAction, SyncDirection};
pub use conflict::{ConflictInfo, ConflictResolution};
pub use engine::{CloudSyncProvider, SyncEngine};
pub use manifest::{FileSyncState, SyncManifest};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RemoteFileInfo {
pub path: String,
pub size: u64,
pub modified_at: DateTime<Utc>,
pub etag: Option<String>,
pub content_hash: Option<String>,
}
#[derive(Debug)]
pub struct CloudSyncResult {
pub success: bool,
pub files_uploaded: usize,
pub files_downloaded: usize,
pub files_deleted: usize,
pub conflicts: Vec<ConflictInfo>,
pub error: Option<String>,
}
impl CloudSyncResult {
pub fn success(uploaded: usize, downloaded: usize, deleted: usize) -> Self {
Self {
success: true,
files_uploaded: uploaded,
files_downloaded: downloaded,
files_deleted: deleted,
conflicts: Vec::new(),
error: None,
}
}
pub fn failure(error: impl Into<String>) -> Self {
Self {
success: false,
files_uploaded: 0,
files_downloaded: 0,
files_deleted: 0,
conflicts: Vec::new(),
error: Some(error.into()),
}
}
pub fn with_conflicts(conflicts: Vec<ConflictInfo>) -> Self {
Self {
success: false,
files_uploaded: 0,
files_downloaded: 0,
files_deleted: 0,
conflicts,
error: Some("Conflicts detected - user resolution required".to_string()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncProgress {
pub stage: SyncStage,
pub current: usize,
pub total: usize,
pub percent: u8,
pub message: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SyncStage {
DetectingLocal,
DetectingRemote,
Uploading,
Downloading,
Deleting,
Complete,
Error,
}
impl SyncStage {
pub fn description(&self) -> &'static str {
match self {
SyncStage::DetectingLocal => "Scanning local files...",
SyncStage::DetectingRemote => "Fetching remote files...",
SyncStage::Uploading => "Uploading...",
SyncStage::Downloading => "Downloading...",
SyncStage::Deleting => "Cleaning up...",
SyncStage::Complete => "Sync complete!",
SyncStage::Error => "Sync failed",
}
}
}
pub fn compute_content_hash(content: &[u8]) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
format!("{:016x}", hasher.finish())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compute_content_hash() {
let hash1 = compute_content_hash(b"hello world");
let hash2 = compute_content_hash(b"hello world");
let hash3 = compute_content_hash(b"different content");
assert_eq!(hash1, hash2);
assert_ne!(hash1, hash3);
}
#[test]
fn test_cloud_sync_result_success() {
let result = CloudSyncResult::success(5, 3, 1);
assert!(result.success);
assert_eq!(result.files_uploaded, 5);
assert_eq!(result.files_downloaded, 3);
assert_eq!(result.files_deleted, 1);
assert!(result.error.is_none());
}
#[test]
fn test_cloud_sync_result_failure() {
let result = CloudSyncResult::failure("Network error");
assert!(!result.success);
assert_eq!(result.error, Some("Network error".to_string()));
}
}