1use std::path::Path;
14
15use secrecy::SecretBox;
16
17use crate::error::{StoreError, StoreResult};
18use crate::sqlite::{cipher, Connection, DbResult};
19
20#[derive(Debug)]
24pub struct Vault {
25 conn: Connection,
26}
27
28impl Vault {
29 pub fn open<F>(
40 db_path: &Path,
41 key: &SecretBox<[u8; 32]>,
42 ensure_schema: F,
43 ) -> StoreResult<Self>
44 where
45 F: FnOnce(&Connection) -> DbResult<()>,
46 {
47 let conn = cipher::open_encrypted(db_path, key, false)?;
48 ensure_schema(&conn)?;
49 if !cipher::integrity_check(&conn)? {
50 return Err(StoreError::IntegrityCheckFailed(
51 "integrity_check failed".to_string(),
52 ));
53 }
54 Ok(Self { conn })
55 }
56
57 #[must_use]
59 pub const fn connection(&self) -> &Connection {
60 &self.conn
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::Vault;
67 use crate::blobs;
68 use crate::error::StoreError;
69 use crate::test_utils::init_sqlite;
70 use secrecy::SecretBox;
71
72 #[test]
73 #[cfg(not(target_arch = "wasm32"))]
74 fn test_vault_open_runs_schema_callback() {
75 init_sqlite();
76 let dir = tempfile::tempdir().expect("create temp dir");
77 let db_path = dir.path().join("vault.sqlite");
78 let key = SecretBox::init_with(|| [0x42u8; 32]);
79
80 let vault = Vault::open(&db_path, &key, |conn| {
81 blobs::ensure_schema(conn)?;
82 conn.execute_batch(
83 "CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY);",
84 )
85 })
86 .expect("open vault");
87
88 let cid = blobs::put(vault.connection(), 7, b"payload", 1000).expect("put");
89 let bytes = blobs::get(vault.connection(), &cid)
90 .expect("get")
91 .expect("present");
92 assert_eq!(bytes, b"payload");
93 }
94
95 #[test]
96 #[cfg(not(target_arch = "wasm32"))]
97 fn test_vault_open_rejects_wrong_key() {
98 init_sqlite();
99 let dir = tempfile::tempdir().expect("create temp dir");
100 let db_path = dir.path().join("vault.sqlite");
101 let key = SecretBox::init_with(|| [0x11u8; 32]);
102 let _ =
103 Vault::open(&db_path, &key, blobs::ensure_schema).expect("create vault");
104 let wrong = SecretBox::init_with(|| [0x22u8; 32]);
105 let err = Vault::open(&db_path, &wrong, |_| Ok(())).expect_err("wrong key");
106 assert!(matches!(err, StoreError::Db(_)));
107 }
108}