1use anyhow::Result;
2use indoc::indoc;
3
4use sqlez::{domain::Domain, thread_safe_connection::ThreadSafeConnection};
5use std::ops::Deref;
6
7lazy_static::lazy_static! {
8 pub static ref KEY_VALUE_STORE: KeyValueStore =
9 KeyValueStore(crate::open_file_db());
10}
11
12#[derive(Clone)]
13pub struct KeyValueStore(ThreadSafeConnection<KeyValueStore>);
14
15impl Domain for KeyValueStore {
16 fn name() -> &'static str {
17 "kvp"
18 }
19
20 fn migrations() -> &'static [&'static str] {
21 &[indoc! {"
22 CREATE TABLE kv_store(
23 key TEXT PRIMARY KEY,
24 value TEXT NOT NULL
25 ) STRICT;
26 "}]
27 }
28}
29
30impl Deref for KeyValueStore {
31 type Target = ThreadSafeConnection<KeyValueStore>;
32
33 fn deref(&self) -> &Self::Target {
34 &self.0
35 }
36}
37
38impl KeyValueStore {
39 pub fn read_kvp(&self, key: &str) -> Result<Option<String>> {
40 self.select_row_bound("SELECT value FROM kv_store WHERE key = (?)")?(key)
41 }
42
43 pub fn write_kvp(&self, key: &str, value: &str) -> Result<()> {
44 self.exec_bound("INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?))")?((
45 key, value,
46 ))?;
47
48 Ok(())
49 }
50
51 pub fn delete_kvp(&self, key: &str) -> Result<()> {
52 self.exec_bound("DELETE FROM kv_store WHERE key = (?)")?(key)
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use anyhow::Result;
59
60 use crate::kvp::KeyValueStore;
61
62 #[test]
63 fn test_kvp() -> Result<()> {
64 let db = KeyValueStore(crate::open_memory_db(Some("test_kvp")));
65
66 assert_eq!(db.read_kvp("key-1").unwrap(), None);
67
68 db.write_kvp("key-1", "one").unwrap();
69 assert_eq!(db.read_kvp("key-1").unwrap(), Some("one".to_string()));
70
71 db.write_kvp("key-1", "one-2").unwrap();
72 assert_eq!(db.read_kvp("key-1").unwrap(), Some("one-2".to_string()));
73
74 db.write_kvp("key-2", "two").unwrap();
75 assert_eq!(db.read_kvp("key-2").unwrap(), Some("two".to_string()));
76
77 db.delete_kvp("key-1").unwrap();
78 assert_eq!(db.read_kvp("key-1").unwrap(), None);
79
80 Ok(())
81 }
82}