use super::VirtualFileSystem;
use crate::vfs::vercel_kv_github::GitHubConfig;
use crate::vfs::vercel_kv_store::VercelKvStore;
use crate::vfs::vercel_kv_types::*;
use crate::vfs::VfsError;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Clone)]
pub struct VercelKvVfs {
pub(crate) store: Arc<VercelKvStore>,
pub(crate) github_config: GitHubConfig,
}
impl VercelKvVfs {
pub fn new() -> Result<Self, VfsError> {
let github_config = match GitHubConfig::from_env() {
Ok(config) => {
log::info!(
"Initialized GitHub config: repo={}/{}, branch={}, root_path={}",
config.owner,
config.repo,
config.branch,
config.root_path
);
config
}
Err(e) => {
log::warn!("Failed to initialize GitHub config: {}", e);
return Err(e);
}
};
Ok(Self {
store: Arc::new(VercelKvStore::new()),
github_config,
})
}
pub(crate) fn memory_cache(&self) -> Arc<RwLock<HashMap<String, Vec<u8>>>> {
self.store.get_memory_cache()
}
pub(crate) fn metadata_cache(&self) -> Arc<RwLock<HashMap<String, FileAttributes>>> {
self.store.get_metadata_cache()
}
pub(crate) async fn initialize_github_load_impl(&self) -> Result<bool, VfsError> {
log::debug!("Checking if GitHub load initialization is needed for root...");
let mut github_load_triggered = false;
let cached_metadata_exists = self.store.exists_in_metadata_cache("").await; let mut skip_github_due_to_cache = false;
if cached_metadata_exists {
log::debug!("Root directory metadata found in cache, skipping GitHub load trigger.");
skip_github_due_to_cache = true;
}
if !skip_github_due_to_cache {
match self.store.read_directory_metadata_from_kv("").await {
Ok(metadata) if !metadata.files.is_empty() => {
log::debug!("Root directory metadata found in KV and is not empty, skipping GitHub load trigger.");
}
Ok(_) => {
log::debug!(
"Root directory metadata in KV is empty or missing. Triggering load..."
);
match self.load_github_directory_impl(true, true).await {
Ok(_) => {
log::info!("GitHub load triggered successfully for root directory during initialization.");
github_load_triggered = true;
}
Err(load_err) => {
log::error!(
"GitHub load trigger failed during initialization: {:?}. VFS might be empty.",
load_err
);
return Err(load_err); }
}
}
Err(e) => {
log::warn!("Error checking root metadata in KV during initialization: {:?}. Attempting load anyway.", e);
match self.load_github_directory_impl(true, true).await {
Ok(_) => {
log::info!("GitHub load triggered successfully for root directory after KV check error.");
github_load_triggered = true;
}
Err(load_err) => {
log::error!(
"GitHub load trigger failed after KV check error: {:?}. VFS might be empty.",
load_err
);
return Err(load_err); }
}
}
}
}
log::debug!(
"GitHub load initialization check complete. Triggered: {}",
github_load_triggered
);
Ok(github_load_triggered)
}
}
impl VirtualFileSystem for VercelKvVfs {
fn read_file(
&self,
path: &str,
) -> impl std::future::Future<Output = Result<Vec<u8>, VfsError>> {
async move { self.read_file_impl(path).await }
}
fn write_file(
&self,
path: &str,
content: Vec<u8>,
) -> impl std::future::Future<Output = Result<(), VfsError>> {
async move { self.write_file_impl(path, content).await }
}
fn exists(&self, path: &str) -> impl std::future::Future<Output = Result<bool, VfsError>> {
async move { self.exists_impl(path).await }
}
fn delete_file(&self, path: &str) -> impl std::future::Future<Output = Result<(), VfsError>> {
async move { self.delete_file_impl(path).await }
}
fn read_file_attributes(
&self,
path: &str,
) -> impl std::future::Future<Output = Result<FileAttributes, VfsError>> {
async move { self.read_file_attributes_impl(path).await }
}
fn list_directory(
&self,
path: &str,
) -> impl std::future::Future<Output = Result<Vec<DirectoryEntry>, VfsError>> {
async move { self.list_directory_impl(path, false).await }
}
fn list_directory_skip_github(
&self,
path: &str,
) -> impl std::future::Future<Output = Result<Vec<DirectoryEntry>, VfsError>> {
async move { self.list_directory_impl(path, true).await }
}
fn create_directory(
&self,
path: &str,
) -> impl std::future::Future<Output = Result<(), VfsError>> {
async move { self.create_directory_impl(path).await }
}
fn load_github_directory(
&self,
_directory_path: &str,
shallow: bool,
) -> impl std::future::Future<Output = Result<LoadDirectoryResult, VfsError>> {
async move { self.load_github_directory_impl(shallow, true).await }
}
fn load_github_directory_flat(
&self,
_directory_path: &str,
shallow: bool,
) -> impl std::future::Future<Output = Result<LoadDirectoryResult, VfsError>> {
async move { self.load_github_directory_impl(shallow, false).await }
}
fn initialize_github_load(&self) -> impl std::future::Future<Output = Result<bool, VfsError>> {
async move { self.initialize_github_load_impl().await }
}
}