adana_cache_command/
cache.rs

1use 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}