swiftide_integrations/redb/
node_cache.rs1use anyhow::Result;
2use async_trait::async_trait;
3use redb::ReadableDatabase;
4use swiftide_core::{NodeCache, indexing::TextNode};
5
6use super::Redb;
7
8macro_rules! unwrap_or_log {
14 ($result:expr) => {
15 match $result {
16 Ok(value) => value,
17 Err(e) => {
18 tracing::error!("Error: {:#}", e);
19 debug_assert!(
20 true,
21 "Redb should not give errors unless in very weird situations; this is a bug: {:#}",
22 e
23 );
24 return false;
25 }
26 }
27 };
28}
29#[async_trait]
30impl NodeCache for Redb {
31 type Input = String;
32
33 async fn get(&self, node: &TextNode) -> bool {
34 let table_definition = self.table_definition();
35 let read_txn = unwrap_or_log!(self.database.begin_read());
36
37 let result = read_txn.open_table(table_definition);
38
39 let table = match result {
40 Ok(table) => table,
41 Err(redb::TableError::TableDoesNotExist { .. }) => {
42 {
44 let write_txn = unwrap_or_log!(self.database.begin_write());
45
46 unwrap_or_log!(write_txn.open_table(table_definition));
47 unwrap_or_log!(write_txn.commit());
48 }
49
50 let read_tx = unwrap_or_log!(self.database.begin_read());
51 unwrap_or_log!(read_tx.open_table(table_definition))
52 }
53 Err(e) => {
54 tracing::error!("Failed to open table: {e:#}");
55 return false;
56 }
57 };
58
59 match table.get(self.node_key(node)).unwrap() {
60 Some(access_guard) => access_guard.value(),
61 None => false,
62 }
63 }
64
65 async fn set(&self, node: &TextNode) {
66 self.set_by_id(node.parent_id().unwrap_or_else(|| node.id()))
67 .await;
68 }
69
70 async fn set_by_id(&self, id: uuid::Uuid) {
71 let write_txn = self.database.begin_write().unwrap();
72
73 {
74 let mut table = write_txn.open_table(self.table_definition()).unwrap();
75
76 table.insert(self.node_key_for_id(id), true).unwrap();
77 }
78 write_txn.commit().unwrap();
79 }
80
81 async fn clear(&self) -> Result<()> {
83 let write_txn = self.database.begin_write().unwrap();
84 let _ = write_txn.delete_table(self.table_definition());
85
86 write_txn.commit().unwrap();
87
88 Ok(())
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use swiftide_core::indexing::TextNode;
96 use temp_dir::TempDir;
97
98 fn setup_redb() -> Redb {
99 let tempdir = TempDir::new().unwrap();
100 Redb::builder()
101 .database_path(tempdir.child("test_clear"))
102 .build()
103 .unwrap()
104 }
105
106 #[tokio::test]
107 async fn test_get_set() {
108 let redb = setup_redb();
109 let node = TextNode::new("test_get_set");
110 assert!(!redb.get(&node).await);
111 redb.set(&node).await;
112 assert!(redb.get(&node).await);
113 }
114 #[tokio::test]
115 async fn test_clear() {
116 let redb = setup_redb();
117 let node = TextNode::new("test_clear");
118 redb.set(&node).await;
119 assert!(redb.get(&node).await);
120 redb.clear().await.unwrap();
121 assert!(!redb.get(&node).await);
122 }
123}