use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path};
use adana_db::{Batch, DEFAULT_TREE, DbOp, Op, SCRIPT_CACHE_KEY, Tree};
use serde::{Deserialize, Serialize};
const DEFAULT_CACHE_KEY: &str = "$___DEF_CACHE_KEY_LOC___$";
pub fn get_value(
db: &mut impl DbOp<String, String>,
namespace: &str,
key: &str,
) -> Option<String> {
db.open_tree(namespace)?;
db.get_value(key)
}
pub fn list_values(
db: &mut impl DbOp<String, String>,
namespace: &str,
) -> Option<Vec<(String, String)>> {
db.open_tree(namespace)?;
Some(db.list_all().into_iter().collect())
}
pub fn remove_value(
db: &mut impl DbOp<String, String>,
namespace: &str,
key: &str,
bypass_check: bool,
) -> Option<String> {
if !bypass_check {
check_cache_name(namespace)?;
}
let mut consumer = |tree: &mut Tree<String, String>| {
let value = tree.get_value(key)?;
let to_delete: Vec<String> = tree
.iter()
.filter_map(|(k, v)| if v == &value { Some(k) } else { None })
.cloned()
.collect();
for k in to_delete {
tree.remove(&*k)?;
}
Some(value)
};
db.apply_tree(namespace, &mut consumer)
}
pub fn insert_value(
db: &mut impl DbOp<String, String>,
namespace: &str,
aliases: Vec<&str>,
value: &str,
bypass_check: bool,
) -> Option<String> {
if !bypass_check {
check_cache_name(namespace)?;
}
db.open_tree(namespace)?;
let mut batch = Batch::default();
let keys = db.keys();
let aliases: Vec<&str> = aliases
.iter()
.filter_map(|alias| {
if keys.contains(&alias.to_string()) { None } else { Some(*alias) }
})
.collect();
for hash_alias in &aliases {
batch.add_insert(hash_alias.to_string(), value.to_string());
}
if aliases.is_empty() {
return None;
}
db.apply_batch(batch)?;
Some(aliases.join(", "))
}
pub fn clear_values(
db: &mut impl DbOp<String, String>,
cache_name: &str,
bypass_check: bool,
) -> Option<()> {
if !bypass_check {
check_cache_name(cache_name)?;
}
if db.open_tree(cache_name).is_some() {
db.clear();
Some(())
} else {
None
}
}
pub fn remove_cache(
db: &mut impl DbOp<String, String>,
cache_name: &str,
bypass_check: bool,
) -> Option<bool> {
if !bypass_check {
check_cache_name(cache_name)?;
}
Some(db.drop_tree(cache_name))
}
pub fn get_cache_names(db: &mut impl DbOp<String, String>) -> Vec<String> {
db.tree_names()
.into_iter()
.filter(|v| v != DEFAULT_TREE && v != SCRIPT_CACHE_KEY)
.collect()
}
pub fn merge(
db: &mut impl DbOp<String, String>,
key_1: &str,
key_2: &str,
) -> Option<()> {
check_cache_name(key_1)?;
check_cache_name(key_2)?;
db.merge_trees(key_1, key_2)
}
pub fn set_default_cache(
db: &mut impl DbOp<String, String>,
default_cache: &str,
) -> Option<()> {
check_cache_name(default_cache)?;
db.open_tree(DEFAULT_TREE)?;
let _ = db.insert(DEFAULT_CACHE_KEY, default_cache);
db.open_tree(default_cache)?;
Some(())
}
pub fn get_default_cache(db: &mut impl DbOp<String, String>) -> Option<String> {
db.open_tree(DEFAULT_TREE)?;
db.get_value(DEFAULT_CACHE_KEY)
}
pub fn dump(
db: &mut impl DbOp<String, String>,
namespace: Option<&str>,
) -> Option<String> {
if let Some(ns) = namespace {
db.apply_tree(ns, &mut move |t| {
serde_json::to_string_pretty(&CacheJson {
name: ns.to_string(),
values: t.list_all(),
})
.ok()
})
} else {
let caches: Vec<String> = get_cache_names(db)
.iter()
.filter_map(|ns| {
db.apply_tree(ns, &mut move |t| {
serde_json::to_string_pretty(&CacheJson {
name: ns.clone(),
values: t.list_all(),
})
.ok()
})
})
.collect();
Some(format!("[{}]", caches.join(",\n")))
}
}
pub fn backup(db: &mut impl DbOp<String, String>, path: &Path) -> Option<()> {
let json = dump(db, None)?;
std::fs::write(path, json).ok()
}
pub fn flush(db: &impl DbOp<String, String>) -> anyhow::Result<&'static str> {
db.flush()
}
pub fn restore(db: &mut impl DbOp<String, String>, path: &Path) -> Option<()> {
let file = File::open(path).ok()?;
let buf_reader = BufReader::new(file);
let caches: Vec<CacheJson> = serde_json::from_reader(buf_reader).ok()?;
for cache in caches {
let mut batch = Batch::default();
db.open_tree(&cache.name)?;
for (key, value) in cache.values {
batch.add_insert(key, value);
}
db.apply_batch(batch)?;
}
Some(())
}
fn check_cache_name(cache_name: &str) -> Option<()> {
if cache_name != DEFAULT_TREE && cache_name != SCRIPT_CACHE_KEY {
Some(())
} else {
println!(
"{} you cannot do this.",
nu_ansi_term::Color::Red.paint("Warning!")
);
None
}
}
#[derive(Serialize, Deserialize)]
struct CacheJson {
name: String,
values: BTreeMap<String, String>,
}