Skip to main content

saku_sync/
lib.rs

1pub mod backend;
2pub mod conflict;
3pub mod error;
4pub mod hash;
5pub mod merkle;
6pub mod state_db;
7pub mod sync_engine;
8
9#[cfg(feature = "server")]
10pub mod config;
11
12#[cfg(feature = "server")]
13pub mod kv_sync;
14
15pub use error::SyncError;
16pub use sync_engine::{SyncConfig, SyncEngine, SyncOutcome, TrackedFile};
17
18use backend::local_fs::LocalFsSyncBackend;
19use std::path::Path;
20
21/// Convenience function: build a `SyncEngine<LocalFsSyncBackend>`, run `sync()`,
22/// and return the outcome. Intended for Phase 3 testing with a local directory
23/// acting as the "remote".
24pub fn try_flush_if_online(
25    store_path: &Path,
26    passphrase: &[u8],
27    backend_root: &Path,
28) -> Result<SyncOutcome, SyncError> {
29    let backend = LocalFsSyncBackend::new(backend_root);
30
31    // Build tracked files list — for now we only track the tdo store.json
32    let store_path = store_path.to_path_buf();
33    let tracked = vec![TrackedFile {
34        file_key: "tdo/store.json".to_string(),
35        tool: "tdo".to_string(),
36        relative_path: "store.json".to_string(),
37        local_path: store_path,
38    }];
39
40    let db_path = saku_storage::device::saku_data_dir()
41        .map_err(SyncError::DeviceId)?
42        .join("sync.db");
43
44    let config = SyncConfig {
45        db_path,
46        passphrase: passphrase.to_vec(),
47        tracked_files: tracked,
48    };
49
50    let mut engine = SyncEngine::new(config, backend)?;
51    engine.sync()
52}
53
54/// Convenience function: build a `SyncEngine<ServerSyncBackend>`, run `sync()`,
55/// and return the outcome. Used when the server feature is enabled and configured.
56#[cfg(feature = "server")]
57pub fn try_flush_if_online_server(
58    store_path: &std::path::Path,
59    passphrase: &[u8],
60    server_url: &str,
61    device_id: &str,
62) -> Result<SyncOutcome, SyncError> {
63    let backend = backend::server::ServerSyncBackend::new(server_url, device_id)?;
64
65    let tracked = vec![TrackedFile {
66        file_key: "tdo/store.json".to_string(),
67        tool: "tdo".to_string(),
68        relative_path: "store.json".to_string(),
69        local_path: store_path.to_path_buf(),
70    }];
71
72    let db_path = saku_storage::device::saku_data_dir()
73        .map_err(SyncError::DeviceId)?
74        .join("sync.db");
75
76    let config = SyncConfig {
77        db_path,
78        passphrase: passphrase.to_vec(),
79        tracked_files: tracked,
80    };
81
82    let mut engine = SyncEngine::new(config, backend)?;
83    engine.sync()
84}
85
86/// Convenience function: run per-entry KV sync against the server.
87///
88/// Loads the store from disk, pulls remote changes (paginated), merges via LWW,
89/// pushes dirty entries, and saves the DirtyTracker sidecar.
90#[cfg(feature = "server")]
91pub fn try_kv_sync_server(
92    store_path: &std::path::Path,
93    passphrase: &[u8],
94    server_url: &str,
95    tool: &str,
96) -> Result<kv_sync::KvSyncOutcome, SyncError> {
97    let config = kv_sync::KvSyncConfig {
98        server_url: server_url.to_string(),
99        tool: tool.to_string(),
100        passphrase: passphrase.to_vec(),
101        store_path: store_path.to_path_buf(),
102    };
103    kv_sync::sync_kv(&config)
104}