use std::collections::HashMap;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::sync::Arc;
use lru::LruCache;
use tokio::sync::{Mutex, RwLock};
use super::engine::{StackGraphEngine, load_rust_language};
use tree_sitter_stack_graphs::StackGraphLanguage;
#[derive(Debug, Clone)]
pub struct CachedDependencyPackage {
pub package_id: u64,
pub crate_name: String,
pub entry_file: PathBuf,
pub files: Vec<(PathBuf, String)>,
pub paths_count: usize,
}
pub struct ProjectEnginePool {
engines: Arc<RwLock<LruCache<u64, Arc<Mutex<StackGraphEngine>>>>>,
dependency_cache: Arc<RwLock<HashMap<u64, CachedDependencyPackage>>>,
sgl: Arc<StackGraphLanguage>,
max_active_engines: usize,
}
impl ProjectEnginePool {
pub fn new(max_active_engines: usize) -> anyhow::Result<Self> {
let capacity = NonZeroUsize::new(max_active_engines.max(1))
.ok_or_else(|| anyhow::anyhow!("Capacity must be non-zero"))?;
let sgl = Arc::new(load_rust_language()?);
Ok(Self {
engines: Arc::new(RwLock::new(LruCache::new(capacity))),
dependency_cache: Arc::new(RwLock::new(HashMap::new())),
sgl,
max_active_engines,
})
}
pub fn language(&self) -> &Arc<StackGraphLanguage> {
&self.sgl
}
pub fn capacity(&self) -> usize {
self.max_active_engines
}
pub async fn len(&self) -> usize {
let guard = self.engines.read().await;
guard.len()
}
pub async fn is_empty(&self) -> bool {
let guard = self.engines.read().await;
guard.is_empty()
}
pub async fn get_or_create(&self, project_id: u64) -> Arc<Mutex<StackGraphEngine>> {
let mut guard = self.engines.write().await;
if let Some(engine) = guard.get(&project_id) {
return Arc::clone(engine);
}
let engine = Arc::new(Mutex::new(StackGraphEngine::new(Arc::clone(&self.sgl))));
guard.put(project_id, Arc::clone(&engine));
engine
}
pub async fn contains(&self, project_id: u64) -> bool {
let guard = self.engines.read().await;
guard.peek(&project_id).is_some()
}
pub async fn evict(&self, project_id: u64) -> Option<Arc<Mutex<StackGraphEngine>>> {
let mut guard = self.engines.write().await;
guard.pop(&project_id)
}
pub async fn clear(&self) {
let mut guard = self.engines.write().await;
guard.clear();
}
pub async fn cache_dependency(&self, dep: CachedDependencyPackage) {
let mut guard = self.dependency_cache.write().await;
guard.insert(dep.package_id, dep);
}
pub async fn get_cached_dependency(&self, package_id: u64) -> Option<CachedDependencyPackage> {
let guard = self.dependency_cache.read().await;
guard.get(&package_id).cloned()
}
pub async fn apply_dependencies_to_engine(
&self,
engine: &mut StackGraphEngine,
package_ids: &[u64],
) -> anyhow::Result<usize> {
let guard = self.dependency_cache.read().await;
let mut total_added = 0;
for &pkg_id in package_ids {
if let Some(dep) = guard.get(&pkg_id) {
let _ = engine.register_crate_root(&dep.crate_name, &dep.entry_file);
for (path, content) in &dep.files {
if let Ok(file_handle) = engine.add_file(path, content) {
engine.dependency_files.insert(file_handle);
engine
.file_to_crate
.insert(file_handle, dep.crate_name.clone());
total_added += 1;
}
}
}
}
Ok(total_added)
}
}