1use std::collections::HashMap;
10
11use async_trait::async_trait;
12
13pub const MIGRATION_SQL: &str = "\
16CREATE TABLE IF NOT EXISTS sync_cursors (
17 device_id TEXT PRIMARY KEY,
18 last_seq INTEGER NOT NULL
19);
20
21CREATE TABLE IF NOT EXISTS sync_state (
22 key TEXT PRIMARY KEY,
23 value TEXT NOT NULL
24);
25
26CREATE TABLE IF NOT EXISTS cloud_outbox (
27 id INTEGER PRIMARY KEY AUTOINCREMENT,
28 operation TEXT NOT NULL CHECK (operation IN ('upload', 'delete')),
29 file_id TEXT NOT NULL,
30 cloud_key TEXT NOT NULL,
31 source_path TEXT,
32 created_at TEXT NOT NULL,
33 min_seq INTEGER,
34 UNIQUE(operation, cloud_key)
35);
36";
37
38#[derive(Debug, thiserror::Error)]
40#[error("sync bookkeeping error: {0}")]
41pub struct DbError(pub String);
42
43#[derive(Debug, Clone)]
45pub struct OutboxEntry {
46 pub id: i64,
47 pub operation: OutboxOperation,
48 pub file_id: String,
49 pub cloud_key: String,
50 pub source_path: Option<String>,
51 pub created_at: String,
52 pub min_seq: Option<u64>,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum OutboxOperation {
58 Upload,
59 Delete,
60}
61
62impl OutboxOperation {
63 pub fn parse(s: &str) -> Option<Self> {
64 match s {
65 "upload" => Some(OutboxOperation::Upload),
66 "delete" => Some(OutboxOperation::Delete),
67 _ => None,
68 }
69 }
70}
71
72#[async_trait]
75pub trait SyncBookkeeping: Send + Sync {
76 async fn get_sync_state(&self, key: &str) -> Result<Option<String>, DbError>;
78
79 async fn set_sync_state(&self, key: &str, value: &str) -> Result<(), DbError>;
81
82 async fn get_all_sync_cursors(&self) -> Result<HashMap<String, u64>, DbError>;
84
85 async fn set_sync_cursor(&self, device_id: &str, seq: u64) -> Result<(), DbError>;
87
88 async fn get_pending_cloud_uploads(&self) -> Result<Vec<OutboxEntry>, DbError>;
90
91 async fn get_pending_cloud_deletes(&self) -> Result<Vec<OutboxEntry>, DbError>;
93
94 async fn has_pending_cloud_uploads(&self) -> Result<bool, DbError>;
96
97 async fn remove_cloud_outbox_entry(&self, id: i64) -> Result<(), DbError>;
99}
100
101#[async_trait]
104pub trait RawDbHandle: Send + Sync {
105 async fn raw_write_handle(&self) -> Result<*mut libsqlite3_sys::sqlite3, DbError>;
112}
113
114pub trait SyncDb: SyncBookkeeping + RawDbHandle {}
117impl<T: SyncBookkeeping + RawDbHandle> SyncDb for T {}