db.rs

 1mod kvp;
 2mod migrations;
 3mod serialized_item;
 4
 5use anyhow::Result;
 6use migrations::MIGRATIONS;
 7use parking_lot::Mutex;
 8use rusqlite::Connection;
 9use std::path::Path;
10use std::sync::Arc;
11
12pub use kvp::*;
13pub use serialized_item::*;
14
15pub struct Db {
16    connection: Mutex<Connection>,
17    in_memory: bool,
18}
19
20// To make a migration:
21// Add to the migrations directory, a file with the name:
22//  <NUMBER>_<DESCRIPTION>.sql. Migrations are executed in order of number
23
24impl Db {
25    /// Open or create a database at the given file path. Falls back to in memory database if the
26    /// database at the given path is corrupted
27    pub fn open(path: &Path) -> Result<Arc<Self>> {
28        let conn = Connection::open(path)?;
29
30        Self::initialize(conn, false).or_else(|_| Self::open_in_memory())
31    }
32
33    /// Open a in memory database for testing and as a fallback.
34    pub fn open_in_memory() -> Result<Arc<Self>> {
35        let conn = Connection::open_in_memory()?;
36
37        Self::initialize(conn, true)
38    }
39
40    fn initialize(mut conn: Connection, in_memory: bool) -> Result<Arc<Self>> {
41        MIGRATIONS.to_latest(&mut conn)?;
42
43        Ok(Arc::new(Self {
44            connection: Mutex::new(conn),
45            in_memory,
46        }))
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use tempdir::TempDir;
54
55    #[gpui::test]
56    fn test_db() {
57        let dir = TempDir::new("db-test").unwrap();
58        let fake_db = Db::open_in_memory().unwrap();
59        let real_db = Db::open(&dir.path().join("test.db")).unwrap();
60
61        for db in [&real_db, &fake_db] {
62            assert_eq!(db.read_kvp("key-1").unwrap(), None);
63
64            db.write_kvp("key-1", "one").unwrap();
65            assert_eq!(db.read_kvp("key-1").unwrap(), Some("one".to_string()));
66
67            db.write_kvp("key-2", "two").unwrap();
68            assert_eq!(db.read_kvp("key-2").unwrap(), Some("two".to_string()));
69
70            db.delete_kvp("key-1").unwrap();
71            assert_eq!(db.read_kvp("key-1").unwrap(), None);
72        }
73
74        drop(real_db);
75
76        let real_db = Db::open(&dir.path().join("test.db")).unwrap();
77
78        real_db.write_kvp("key-1", "one").unwrap();
79        assert_eq!(real_db.read_kvp("key-1").unwrap(), None);
80
81        real_db.write_kvp("key-2", "two").unwrap();
82        assert_eq!(real_db.read_kvp("key-2").unwrap(), Some("two".to_string()));
83    }
84}