Skip to main content

max / goingson

2.5 KB · 73 lines History Blame Raw
1 //! GoingsOn's [`BlobPolicy`]: attachment blobs are content-addressed files under
2 //! the app data dir, tracked purely by disk existence (no `cloud_only` flag), so
3 //! the three presence hooks stay no-ops. This mirrors the retiring
4 //! `sync_service/blob_sync.rs`, but the engine now owns the invariant machinery:
5 //! dedup-aware upload, integrity-verified download, and atomic writes.
6
7 use std::path::PathBuf;
8
9 use rusqlite::Connection;
10 use synckit_client::{BlobPolicy, BlobRef, Result, SyncKitError};
11
12 use crate::state::DESKTOP_USER_ID;
13
14 /// Attachment blob policy for the single desktop user.
15 pub(crate) struct AttachmentBlobs {
16 data_dir: PathBuf,
17 user_id: String,
18 }
19
20 impl AttachmentBlobs {
21 pub(crate) fn new(data_dir: PathBuf) -> Self {
22 Self {
23 data_dir,
24 user_id: DESKTOP_USER_ID.to_string(),
25 }
26 }
27
28 /// Every distinct attachment blob for the desktop user. The engine filters
29 /// this candidate set per pass: uploads skip blobs with no local file,
30 /// downloads skip blobs already present on disk (then SHA-256-verify).
31 fn all_blobs(&self, conn: &Connection) -> Result<Vec<BlobRef>> {
32 let mut stmt = conn
33 .prepare("SELECT DISTINCT blob_hash, file_size FROM attachments WHERE user_id = ?1")
34 .map_err(map_err)?;
35 let rows = stmt
36 .query_map([&self.user_id], |r| {
37 let hash: String = r.get(0)?;
38 let size: i64 = r.get(1)?;
39 Ok(BlobRef {
40 hash,
41 ext: String::new(),
42 size: size.max(0) as u64,
43 })
44 })
45 .map_err(map_err)?;
46 rows.collect::<rusqlite::Result<Vec<_>>>().map_err(map_err)
47 }
48 }
49
50 #[allow(
51 clippy::needless_pass_by_value,
52 reason = "used as a `.map_err(map_err)` callback, which passes the error by value"
53 )]
54 fn map_err(e: rusqlite::Error) -> SyncKitError {
55 SyncKitError::Internal(format!("attachment blob query: {e}"))
56 }
57
58 impl BlobPolicy for AttachmentBlobs {
59 fn pending_uploads(&self, conn: &Connection) -> Result<Vec<BlobRef>> {
60 self.all_blobs(conn)
61 }
62
63 fn pending_downloads(&self, conn: &Connection) -> Result<Vec<BlobRef>> {
64 self.all_blobs(conn)
65 }
66
67 fn local_path(&self, blob: &BlobRef) -> PathBuf {
68 crate::commands::attachment::blob_path(&self.data_dir, &blob.hash)
69 }
70 // reconcile / on_uploaded / on_downloaded: inherited no-ops (presence is disk
71 // existence, so there is no flag to reconcile).
72 }
73