use std::{collections::HashMap, future::Future, path::PathBuf, sync::Arc};
use bytes::Bytes;
use tokio::sync::Mutex;
use crate::{
model::FileInfo,
proto::FileSystem,
tokio::local::{TokioLocalStorage, TokioLocalStorageError},
};
#[derive(Debug, Clone)]
pub struct TokioCachedLocalStorage {
internal: TokioLocalStorage,
cache: Arc<Mutex<HashMap<String, FileInfo>>>,
}
impl TokioCachedLocalStorage {
pub fn new(base: PathBuf, ignore_parts: Vec<String>, cache: Vec<FileInfo>) -> Self {
let internal = TokioLocalStorage::new(base, ignore_parts);
let cache: HashMap<_, _> = cache.into_iter().map(|e| (e.local_unix_path.clone(), e)).collect();
let cache = Arc::new(Mutex::new(cache));
Self { internal, cache }
}
}
impl FileSystem for TokioCachedLocalStorage {
type Error = TokioLocalStorageError;
type StorePrepare = (tokio::fs::File, FileInfo);
fn all_files(&mut self) -> impl Future<Output = Result<Vec<FileInfo>, Self::Error>> + Send {
let cache = self.cache.clone();
async move {
let guard = cache.lock().await;
let cache = guard.clone().into_values().collect();
Ok(cache)
}
}
#[expect(clippy::manual_async_fn)]
fn prepare_store_file(
&self,
info: FileInfo,
) -> impl Future<Output = Result<Self::StorePrepare, Self::Error>> + Send {
async move { Ok((self.internal.prepare_store_file(info.clone()).await?, info)) }
}
fn store_file(
&self,
prepared: Self::StorePrepare,
data: Bytes,
) -> impl Future<Output = Result<(), Self::Error>> + Send {
let int_prepared = prepared.0;
let cache = self.cache.clone();
async move {
self.internal.store_file(int_prepared, data).await?;
let mut guard = cache.lock().await;
guard.insert(prepared.1.local_unix_path.clone(), prepared.1);
Ok(())
}
}
fn delete_file(&self, info: FileInfo) -> impl Future<Output = Result<(), Self::Error>> + Send {
let cache = self.cache.clone();
let path = info.local_unix_path.clone();
async move {
self.internal.delete_file(info).await?;
let mut guard = cache.lock().await;
guard.remove(&path);
Ok(())
}
}
}