use std::path::PathBuf;
use sled::{Config, Db, IVec};
use crate::app::AppDescriptor;
static SCAN_KEY: [u8; 4] = 0_i32.to_be_bytes();
pub(crate) struct Cache {
apps_loader: fn() -> Vec<AppDescriptor>,
db: Db,
}
impl Cache {
pub fn new(apps_loader: fn() -> Vec<AppDescriptor>) -> Self {
let config = Config::new().path(Self::resolve_db_file_path());
let db = config.open().unwrap();
Cache { apps_loader, db }
}
pub fn is_empty(&self) -> bool {
self.db.is_empty()
}
pub fn read_all(&self) -> Option<Vec<AppDescriptor>> {
let iter = self.db.range(SCAN_KEY..);
let mut app_descriptors: Vec<AppDescriptor> = vec![];
for item in iter {
let (_, desc_ivec) = item.ok()?;
let mut app_descriptor: AppDescriptor = bincode::deserialize(&desc_ivec[..]).ok()?;
if app_descriptor.lower_title.is_empty() {
app_descriptor.lower_title = app_descriptor.title.to_lowercase();
}
app_descriptors.push(app_descriptor);
}
Some(app_descriptors)
}
pub fn update(&mut self, selected_app: &AppDescriptor) -> anyhow::Result<()> {
let latest_entries = (self.apps_loader)();
let cached_entry_wrappers = self.read_all();
let mut updated_entry_wrappers: Vec<AppDescriptor> =
Vec::with_capacity(latest_entries.len());
for mut latest_entry in latest_entries {
let count = if let Some(ref entry_wrappers) = cached_entry_wrappers {
Cache::find_count(&latest_entry.appid, entry_wrappers).unwrap_or(0)
} else {
0
};
latest_entry.exec_count = if latest_entry.appid == selected_app.appid {
count + 1
} else {
count
};
updated_entry_wrappers.push(latest_entry);
}
updated_entry_wrappers.sort_by(|a, b| a.title.cmp(&b.title));
updated_entry_wrappers.sort_by(|a, b| b.exec_count.cmp(&a.exec_count));
self.db.clear()?; for (count, app_descriptor) in updated_entry_wrappers.into_iter().enumerate() {
let encoded: Vec<u8> = bincode::serialize(&app_descriptor)?;
self.db.insert(count.to_be_bytes(), IVec::from(encoded))?;
}
self.db.flush()?;
Ok(())
}
fn find_count(app_id: &String, entries: &Vec<AppDescriptor>) -> Option<usize> {
for ew in entries {
if ew.appid == *app_id {
return Some(ew.exec_count);
}
}
None
}
fn resolve_db_file_path() -> PathBuf {
let mut path = dirs::cache_dir().unwrap();
path.push(format!(
"{}-{}",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION")
));
path
}
}