mod segment;
use std::path::PathBuf;
use std::sync::{Mutex, MutexGuard};
pub use concinnity_core::blob::CacheEntryKind;
pub use segment::Segment;
use super::paths;
pub const CACHE_BUDGET_BYTES: u64 = 64 * 1024 * 1024;
pub fn load(kind: CacheEntryKind, key: &str) -> Option<Vec<u8>> {
with(|segment| segment.get(kind, key).map(<[u8]>::to_vec)).flatten()
}
pub fn load_bundled(kind: CacheEntryKind, key: &str) -> Option<Vec<u8>> {
bundled(|segment| segment.get(kind, key).map(<[u8]>::to_vec)).flatten()
}
pub fn store(kind: CacheEntryKind, key: &str, bytes: &[u8]) -> bool {
with(|segment| segment.put(kind, key, bytes)).unwrap_or(false)
}
pub fn delete(kind: CacheEntryKind, key: &str) {
with(|segment| segment.remove(kind, key));
}
pub fn verify_toolchain(id: &str) -> bool {
with(|segment| segment.adopt_toolchain(id)).unwrap_or(false)
}
pub fn flush() -> bool {
match lock().as_mut() {
Some(loaded) => loaded.segment.write_to(&loaded.path, CACHE_BUDGET_BYTES),
None => false,
}
}
struct Loaded {
path: PathBuf,
segment: Segment,
}
fn with<R>(f: impl FnOnce(&mut Segment) -> R) -> Option<R> {
let path = paths::runtime_cache_path()?;
let mut held = lock();
if held.as_ref().is_some_and(|loaded| loaded.path != path)
&& let Some(mut previous) = held.take()
{
previous
.segment
.write_to(&previous.path, CACHE_BUDGET_BYTES);
}
let loaded = held.get_or_insert_with(|| Loaded {
segment: Segment::read_from(&path),
path,
});
Some(f(&mut loaded.segment))
}
fn lock() -> MutexGuard<'static, Option<Loaded>> {
static LOADED: Mutex<Option<Loaded>> = Mutex::new(None);
LOADED.lock().unwrap_or_else(|e| e.into_inner())
}
fn bundled<R>(f: impl FnOnce(&mut Segment) -> R) -> Option<R> {
let path = shipped_path(
paths::bundled_runtime_cache_path()?,
paths::runtime_cache_path(),
)?;
static LOADED: Mutex<Option<Loaded>> = Mutex::new(None);
let mut held = LOADED.lock().unwrap_or_else(|e| e.into_inner());
if held.as_ref().is_some_and(|loaded| loaded.path != path) {
*held = None;
}
let loaded = held.get_or_insert_with(|| Loaded {
segment: Segment::read_from(&path),
path,
});
Some(f(&mut loaded.segment))
}
fn shipped_path(shipped: PathBuf, writable: Option<PathBuf>) -> Option<PathBuf> {
(writable.as_deref() != Some(shipped.as_path())).then_some(shipped)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_writable_bundle_has_no_second_tier() {
let one = PathBuf::from("/bundle/cache/0");
assert_eq!(shipped_path(one.clone(), Some(one.clone())), None);
}
#[test]
fn a_read_only_install_reads_the_shipped_segment() {
let shipped = PathBuf::from("/opt/app/cache/0");
let writable = PathBuf::from("/home/u/.local/share/app/cache/0");
assert_eq!(
shipped_path(shipped.clone(), Some(writable)),
Some(shipped.clone())
);
assert_eq!(shipped_path(shipped.clone(), None), Some(shipped));
}
}