1use anyhow::Result;
2use rusqlite::OptionalExtension;
3
4use super::Db;
5
6pub(crate) const KVP_M_1_UP: &str = "
7CREATE TABLE kv_store(
8 key TEXT PRIMARY KEY,
9 value TEXT NOT NULL
10) STRICT;
11";
12
13impl Db {
14 pub fn read_kvp(&self, key: &str) -> Result<Option<String>> {
15 self.real()
16 .map(|db| {
17 let lock = db.connection.lock();
18 let mut stmt = lock.prepare_cached("SELECT value FROM kv_store WHERE key = (?)")?;
19
20 Ok(stmt.query_row([key], |row| row.get(0)).optional()?)
21 })
22 .unwrap_or(Ok(None))
23 }
24
25 pub fn write_kvp(&self, key: &str, value: &str) -> Result<()> {
26 self.real()
27 .map(|db| {
28 let lock = db.connection.lock();
29
30 let mut stmt = lock.prepare_cached(
31 "INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?))",
32 )?;
33
34 stmt.execute([key, value])?;
35
36 Ok(())
37 })
38 .unwrap_or(Ok(()))
39 }
40
41 pub fn delete_kvp(&self, key: &str) -> Result<()> {
42 self.real()
43 .map(|db| {
44 let lock = db.connection.lock();
45
46 let mut stmt = lock.prepare_cached("DELETE FROM kv_store WHERE key = (?)")?;
47
48 stmt.execute([key])?;
49
50 Ok(())
51 })
52 .unwrap_or(Ok(()))
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use anyhow::Result;
59
60 use super::*;
61
62 #[test]
63 fn test_kvp() -> Result<()> {
64 let db = Db::open_in_memory();
65
66 assert_eq!(db.read_kvp("key-1")?, None);
67
68 db.write_kvp("key-1", "one")?;
69 assert_eq!(db.read_kvp("key-1")?, Some("one".to_string()));
70
71 db.write_kvp("key-1", "one-2")?;
72 assert_eq!(db.read_kvp("key-1")?, Some("one-2".to_string()));
73
74 db.write_kvp("key-2", "two")?;
75 assert_eq!(db.read_kvp("key-2")?, Some("two".to_string()));
76
77 db.delete_kvp("key-1")?;
78 assert_eq!(db.read_kvp("key-1")?, None);
79
80 Ok(())
81 }
82}