synwire_storage/
concurrency.rs1use crate::StorageError;
13use rusqlite::Connection;
14use std::io;
15use std::path::Path;
16
17pub fn ensure_wal_mode(conn: &Connection) -> Result<(), StorageError> {
28 conn.pragma_update(None, "journal_mode", "WAL")?;
29 conn.pragma_update(None, "synchronous", "NORMAL")?;
30 conn.pragma_update(None, "foreign_keys", "ON")?;
31 Ok(())
32}
33
34pub fn atomic_write(dest: &Path, data: &[u8]) -> io::Result<()> {
46 let parent = dest.parent().ok_or_else(|| {
47 io::Error::new(io::ErrorKind::InvalidInput, "dest has no parent directory")
48 })?;
49 let tmp_path = parent.join(format!(
51 ".tmp-{}",
52 dest.file_name()
53 .and_then(|n| n.to_str())
54 .unwrap_or("atomic")
55 ));
56 std::fs::write(&tmp_path, data)?;
57 std::fs::rename(&tmp_path, dest)
59}
60
61pub fn open_wal_database(path: &Path) -> Result<Connection, StorageError> {
69 if let Some(parent) = path.parent() {
70 std::fs::create_dir_all(parent)?;
71 }
72 let conn = Connection::open(path)?;
73 ensure_wal_mode(&conn)?;
74 Ok(conn)
75}
76
77#[cfg(test)]
78#[allow(clippy::expect_used, clippy::unwrap_used)]
79mod tests {
80 use super::*;
81 use tempfile::tempdir;
82
83 #[test]
84 fn wal_mode_is_set() {
85 let dir = tempdir().expect("tempdir");
86 let db_path = dir.path().join("test.db");
87 let conn = open_wal_database(&db_path).expect("open_wal_database");
88 let mode: String = conn
89 .query_row("PRAGMA journal_mode", [], |r| r.get(0))
90 .expect("PRAGMA journal_mode");
91 assert_eq!(mode, "wal");
92 }
93
94 #[test]
95 fn atomic_write_creates_file() {
96 let dir = tempdir().expect("tempdir");
97 let dest = dir.path().join("output.bin");
98 atomic_write(&dest, b"hello world").expect("atomic_write");
99 let contents = std::fs::read(&dest).expect("read");
100 assert_eq!(contents, b"hello world");
101 }
102
103 #[test]
104 fn atomic_write_replaces_existing() {
105 let dir = tempdir().expect("tempdir");
106 let dest = dir.path().join("output.bin");
107 atomic_write(&dest, b"v1").expect("atomic_write v1");
108 atomic_write(&dest, b"v2").expect("atomic_write v2");
109 let contents = std::fs::read(&dest).expect("read");
110 assert_eq!(contents, b"v2");
111 }
112}