use std::{sync::Arc, time::Duration};
use rspack_error::Result;
use rspack_paths::InternedPathSet;
use super::{
CacheFacade, CacheKey, CacheValue, Etag, IdleFileCache, MemoryCache, MemoryCacheGetResult, Meta,
cache_value::CacheValueData,
};
#[derive(Debug)]
struct CacheStorage {
memory_cache: Option<MemoryCache>,
idle_file_cache: Option<IdleFileCache>,
}
#[derive(Debug)]
struct CacheInner {
compiler_path: String,
storage: Option<CacheStorage>,
}
#[derive(Debug, Clone)]
pub struct Cache {
inner: Arc<CacheInner>,
}
impl Cache {
pub fn new(
compiler_path: String,
memory_cache: Option<MemoryCache>,
idle_file_cache: Option<IdleFileCache>,
) -> Self {
let storage = if memory_cache.is_some() || idle_file_cache.is_some() {
Some(CacheStorage {
memory_cache,
idle_file_cache,
})
} else {
None
};
Self {
inner: Arc::new(CacheInner {
compiler_path,
storage,
}),
}
}
pub fn new_disabled(compiler_path: String) -> Self {
Self {
inner: Arc::new(CacheInner {
compiler_path,
storage: None,
}),
}
}
pub(crate) fn facade(&self, name: &str) -> CacheFacade {
let mut cache_name = String::with_capacity(self.inner.compiler_path.len() + name.len());
cache_name.push_str(&self.inner.compiler_path);
cache_name.push_str(name);
CacheFacade::new(self.clone(), cache_name)
}
pub fn get<T: CacheValueData>(&self, key: CacheKey, etag: Option<Etag>) -> Option<CacheValue<T>> {
let Some(storage) = &self.inner.storage else {
return None;
};
if let Some(memory_cache) = &storage.memory_cache {
match memory_cache.get(&key, etag.as_ref()) {
MemoryCacheGetResult::Hit(value) => return Some(value),
MemoryCacheGetResult::Miss => return None,
MemoryCacheGetResult::NotCached => {}
}
}
let Some(file_cache) = &storage.idle_file_cache else {
if let Some(memory_cache) = &storage.memory_cache {
memory_cache.store_miss(key);
}
return None;
};
match file_cache.restore::<T>(key.clone(), etag.clone()) {
Some(value) => {
if let Some(memory_cache) = &storage.memory_cache {
memory_cache.store(key, etag, value.clone());
}
Some(value)
}
None => {
if let Some(memory_cache) = &storage.memory_cache {
memory_cache.store_miss(key);
}
None
}
}
}
pub fn store<T: CacheValueData>(&self, key: CacheKey, etag: Option<Etag>, value: CacheValue<T>) {
let Some(storage) = &self.inner.storage else {
return;
};
if let Some(memory_cache) = &storage.memory_cache {
memory_cache.store(key.clone(), etag.clone(), value.clone());
}
if let Some(file_cache) = &storage.idle_file_cache {
file_cache.store(key, etag, value)
}
}
pub fn store_build_dependencies(&self, dependencies: InternedPathSet) {
let Some(storage) = &self.inner.storage else {
return;
};
if let Some(file_cache) = &storage.idle_file_cache {
file_cache.store_build_dependencies(dependencies);
}
}
pub fn store_meta(&self, meta: Meta) {
let Some(storage) = &self.inner.storage else {
return;
};
if let Some(file_cache) = &storage.idle_file_cache {
file_cache.store_meta(meta);
}
}
pub fn restore_meta(&self) -> Result<Option<Meta>> {
let Some(storage) = &self.inner.storage else {
return Ok(None);
};
if let Some(file_cache) = &storage.idle_file_cache {
file_cache.restore_meta()
} else {
Ok(None)
}
}
pub fn has_file_cache(&self) -> bool {
self
.inner
.storage
.as_ref()
.is_some_and(|storage| storage.idle_file_cache.is_some())
}
pub fn begin_idle(&self, build_time: Duration) {
let Some(storage) = &self.inner.storage else {
return;
};
if let Some(memory_cache) = &storage.memory_cache {
memory_cache.start_next_generation();
}
if let Some(file_cache) = &storage.idle_file_cache {
file_cache.begin_idle(build_time);
}
}
pub fn end_idle(&self) {
let Some(storage) = &self.inner.storage else {
return;
};
if let Some(file_cache) = &storage.idle_file_cache {
file_cache.end_idle();
}
}
pub async fn shutdown(&self) {
let Some(storage) = &self.inner.storage else {
return;
};
if let Some(memory_cache) = &storage.memory_cache {
memory_cache.clear();
}
if let Some(file_cache) = &storage.idle_file_cache {
file_cache.shutdown().await;
}
}
}