adana_cache_command/
cache.rs1use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path};
2
3use adana_db::{Batch, DEFAULT_TREE, DbOp, Op, SCRIPT_CACHE_KEY, Tree};
4use serde::{Deserialize, Serialize};
5
6const DEFAULT_CACHE_KEY: &str = "$___DEF_CACHE_KEY_LOC___$";
7
8pub fn get_value(
9 db: &mut impl DbOp<String, String>,
10 namespace: &str,
11 key: &str,
12) -> Option<String> {
13 db.open_tree(namespace)?;
14 db.get_value(key)
15}
16
17pub fn list_values(
18 db: &mut impl DbOp<String, String>,
19 namespace: &str,
20) -> Option<Vec<(String, String)>> {
21 db.open_tree(namespace)?;
22 Some(db.list_all().into_iter().collect())
23}
24
25pub fn remove_value(
26 db: &mut impl DbOp<String, String>,
27 namespace: &str,
28 key: &str,
29 bypass_check: bool,
30) -> Option<String> {
31 if !bypass_check {
32 check_cache_name(namespace)?;
33 }
34 let mut consumer = |tree: &mut Tree<String, String>| {
35 let value = tree.get_value(key)?;
36 let to_delete: Vec<String> = tree
37 .iter()
38 .filter_map(|(k, v)| if v == &value { Some(k) } else { None })
39 .cloned()
40 .collect();
41 for k in to_delete {
42 tree.remove(&*k)?;
43 }
44 Some(value)
45 };
46
47 db.apply_tree(namespace, &mut consumer)
48}
49
50pub fn insert_value(
51 db: &mut impl DbOp<String, String>,
52 namespace: &str,
53 aliases: Vec<&str>,
54 value: &str,
55 bypass_check: bool,
56) -> Option<String> {
57 if !bypass_check {
58 check_cache_name(namespace)?;
59 }
60 db.open_tree(namespace)?;
61 let mut batch = Batch::default();
62 let keys = db.keys();
63
64 let aliases: Vec<&str> = aliases
65 .iter()
66 .filter_map(|alias| {
67 if keys.contains(&alias.to_string()) { None } else { Some(*alias) }
68 })
69 .collect();
70
71 for hash_alias in &aliases {
72 batch.add_insert(hash_alias.to_string(), value.to_string());
73 }
74
75 if aliases.is_empty() {
76 return None;
77 }
78
79 db.apply_batch(batch)?;
80
81 Some(aliases.join(", "))
82}
83
84pub fn clear_values(
85 db: &mut impl DbOp<String, String>,
86 cache_name: &str,
87 bypass_check: bool,
88) -> Option<()> {
89 if !bypass_check {
90 check_cache_name(cache_name)?;
91 }
92 if db.open_tree(cache_name).is_some() {
93 db.clear();
94 Some(())
95 } else {
96 None
97 }
98}
99
100pub fn remove_cache(
101 db: &mut impl DbOp<String, String>,
102 cache_name: &str,
103 bypass_check: bool,
104) -> Option<bool> {
105 if !bypass_check {
106 check_cache_name(cache_name)?;
107 }
108 Some(db.drop_tree(cache_name))
109}
110
111pub fn get_cache_names(db: &mut impl DbOp<String, String>) -> Vec<String> {
112 db.tree_names()
113 .into_iter()
114 .filter(|v| v != DEFAULT_TREE && v != SCRIPT_CACHE_KEY)
115 .collect()
116}
117
118pub fn merge(
119 db: &mut impl DbOp<String, String>,
120 key_1: &str,
121 key_2: &str,
122) -> Option<()> {
123 check_cache_name(key_1)?;
124 check_cache_name(key_2)?;
125 db.merge_trees(key_1, key_2)
126}
127
128pub fn set_default_cache(
129 db: &mut impl DbOp<String, String>,
130 default_cache: &str,
131) -> Option<()> {
132 check_cache_name(default_cache)?;
133 db.open_tree(DEFAULT_TREE)?;
134 let _ = db.insert(DEFAULT_CACHE_KEY, default_cache);
135 db.open_tree(default_cache)?;
136 Some(())
137}
138
139pub fn get_default_cache(db: &mut impl DbOp<String, String>) -> Option<String> {
140 db.open_tree(DEFAULT_TREE)?;
141 db.get_value(DEFAULT_CACHE_KEY)
142}
143
144pub fn dump(
145 db: &mut impl DbOp<String, String>,
146 namespace: Option<&str>,
147) -> Option<String> {
148 if let Some(ns) = namespace {
149 db.apply_tree(ns, &mut move |t| {
150 serde_json::to_string_pretty(&CacheJson {
151 name: ns.to_string(),
152 values: t.list_all(),
153 })
154 .ok()
155 })
156 } else {
157 let caches: Vec<String> = get_cache_names(db)
158 .iter()
159 .filter_map(|ns| {
160 db.apply_tree(ns, &mut move |t| {
161 serde_json::to_string_pretty(&CacheJson {
162 name: ns.clone(),
163 values: t.list_all(),
164 })
165 .ok()
166 })
167 })
168 .collect();
169
170 Some(format!("[{}]", caches.join(",\n")))
171 }
172}
173
174pub fn backup(db: &mut impl DbOp<String, String>, path: &Path) -> Option<()> {
175 let json = dump(db, None)?;
176 std::fs::write(path, json).ok()
177}
178
179pub fn flush(db: &impl DbOp<String, String>) -> anyhow::Result<&'static str> {
180 db.flush()
181}
182
183pub fn restore(db: &mut impl DbOp<String, String>, path: &Path) -> Option<()> {
184 let file = File::open(path).ok()?;
185 let buf_reader = BufReader::new(file);
186 let caches: Vec<CacheJson> = serde_json::from_reader(buf_reader).ok()?;
187 for cache in caches {
188 let mut batch = Batch::default();
189 db.open_tree(&cache.name)?;
190
191 for (key, value) in cache.values {
192 batch.add_insert(key, value);
193 }
194 db.apply_batch(batch)?;
195 }
196 Some(())
197}
198
199fn check_cache_name(cache_name: &str) -> Option<()> {
200 if cache_name != DEFAULT_TREE && cache_name != SCRIPT_CACHE_KEY {
201 Some(())
202 } else {
203 println!(
204 "{} you cannot do this.",
205 nu_ansi_term::Color::Red.paint("Warning!")
206 );
207 None
208 }
209}
210
211#[derive(Serialize, Deserialize)]
212struct CacheJson {
213 name: String,
214 values: BTreeMap<String, String>,
215}