mod identity;
mod key;
mod segment;
pub mod thumbnails;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, MutexGuard};
pub(crate) use concinnity_core::blob::CacheEntryKind;
pub(crate) use key::{bake_key, expand_key, payload_key};
use segment::Index;
pub(crate) fn load(kind: CacheEntryKind, key: &str) -> Option<Vec<u8>> {
if cfg!(test) {
return None;
}
let index = {
let mut held = lock();
let loaded = open(&mut held)?;
if let Some(bytes) = loaded.stored.get(&(kind, key.to_owned())) {
return Some(bytes.to_vec());
}
Arc::clone(&loaded.index)
};
index.get(kind, key)
}
pub(crate) fn contains(kind: CacheEntryKind, key: &str) -> bool {
if cfg!(test) {
return false;
}
let mut held = lock();
let Some(loaded) = open(&mut held) else {
return false;
};
loaded.stored.contains_key(&(kind, key.to_owned())) || loaded.index.contains(kind, key)
}
pub(crate) fn store(kind: CacheEntryKind, key: &str, bytes: &[u8]) {
if cfg!(test) {
return;
}
let mut held = lock();
let Some(loaded) = open(&mut held) else {
return;
};
loaded
.stored
.insert((kind, key.to_owned()), bytes.to_vec().into());
}
pub(crate) fn flush() -> bool {
let mut held = lock();
let Some(loaded) = held.take() else {
return false;
};
if loaded.stored.is_empty() {
*held = Some(loaded);
return false;
}
write(&loaded)
}
struct Loaded {
path: PathBuf,
token: u32,
index: Arc<Index>,
stored: HashMap<(CacheEntryKind, String), Arc<[u8]>>,
}
fn write(loaded: &Loaded) -> bool {
let mut stored: Vec<(CacheEntryKind, &str, &[u8])> = loaded
.stored
.iter()
.map(|((kind, key), bytes)| (*kind, key.as_str(), &**bytes))
.collect();
stored.sort_by(|a, b| (a.1, a.0 as u8).cmp(&(b.1, b.0 as u8)));
segment::write(&loaded.path, &loaded.index, &stored, loaded.token)
}
fn open<'a>(held: &'a mut MutexGuard<'static, Option<Loaded>>) -> Option<&'a mut Loaded> {
let path = crate::paths::build_cache_path()?;
let token = identity::token()?;
if held.as_ref().is_some_and(|loaded| loaded.path != path) {
if let Some(previous) = held.take() {
write(&previous);
}
}
Some(held.get_or_insert_with(|| Loaded {
index: Arc::new(Index::read(&path, token)),
path,
token,
stored: HashMap::new(),
}))
}
fn lock() -> MutexGuard<'static, Option<Loaded>> {
static LOADED: Mutex<Option<Loaded>> = Mutex::new(None);
LOADED.lock().unwrap_or_else(|e| e.into_inner())
}