use crate::utils::error::ContextCreatorError;
use async_trait::async_trait;
use deadpool::managed::{self, Manager, Metrics, Pool, RecycleResult};
use std::collections::HashMap;
use tree_sitter::{Language, Parser};
pub type PooledParser = managed::Object<ParserManager>;
pub type ParserPool = Pool<ParserManager>;
pub struct ParserManager {
language: Language,
language_name: &'static str,
}
impl ParserManager {
pub fn new(language: Language, language_name: &'static str) -> Self {
Self {
language,
language_name,
}
}
}
#[async_trait]
impl Manager for ParserManager {
type Type = Parser;
type Error = ContextCreatorError;
async fn create(&self) -> Result<Parser, Self::Error> {
let mut parser = Parser::new();
parser.set_language(self.language).map_err(|e| {
ContextCreatorError::ParseError(format!(
"Failed to set {} language: {}",
self.language_name, e
))
})?;
parser.set_timeout_micros(5_000_000);
Ok(parser)
}
async fn recycle(&self, parser: &mut Parser, _: &Metrics) -> RecycleResult<Self::Error> {
parser.reset();
Ok(())
}
}
pub struct ParserPoolManager {
pools: HashMap<&'static str, ParserPool>,
}
impl ParserPoolManager {
pub fn new() -> Self {
let mut pools = HashMap::new();
let pool_config = managed::PoolConfig {
max_size: 16,
..Default::default()
};
pools.insert(
"rust",
Pool::builder(ParserManager::new(tree_sitter_rust::language(), "rust"))
.config(pool_config)
.build()
.expect("Failed to create Rust parser pool"),
);
pools.insert(
"javascript",
Pool::builder(ParserManager::new(
tree_sitter_javascript::language(),
"javascript",
))
.config(pool_config)
.build()
.expect("Failed to create JavaScript parser pool"),
);
pools.insert(
"python",
Pool::builder(ParserManager::new(tree_sitter_python::language(), "python"))
.config(pool_config)
.build()
.expect("Failed to create Python parser pool"),
);
pools.insert(
"typescript",
Pool::builder(ParserManager::new(
tree_sitter_typescript::language_typescript(),
"typescript",
))
.config(pool_config)
.build()
.expect("Failed to create TypeScript parser pool"),
);
pools.insert(
"go",
Pool::builder(ParserManager::new(tree_sitter_go::language(), "go"))
.config(pool_config)
.build()
.expect("Failed to create Go parser pool"),
);
pools.insert(
"java",
Pool::builder(ParserManager::new(tree_sitter_java::language(), "java"))
.config(pool_config)
.build()
.expect("Failed to create Java parser pool"),
);
Self { pools }
}
pub async fn get_parser(&self, language: &str) -> Result<PooledParser, ContextCreatorError> {
let pool = self.pools.get(language).ok_or_else(|| {
ContextCreatorError::ParseError(format!("Unsupported language: {language}"))
})?;
pool.get().await.map_err(|e| {
ContextCreatorError::ParseError(format!(
"Failed to get {language} parser from pool: {e}"
))
})
}
pub fn get_status(&self, language: &str) -> Option<deadpool::Status> {
self.pools.get(language).map(|pool| pool.status())
}
}
impl Default for ParserPoolManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_parser_creation() {
let manager = ParserManager::new(tree_sitter_rust::language(), "rust");
let parser = manager.create().await.unwrap();
assert_eq!(parser.timeout_micros(), 5_000_000);
}
#[tokio::test]
async fn test_parser_recycling() {
let manager = ParserManager::new(tree_sitter_python::language(), "python");
let mut parser = manager.create().await.unwrap();
let result = manager.recycle(&mut parser, &Metrics::default()).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_pool_manager() {
let pool_manager = ParserPoolManager::new();
let rust_parser = pool_manager.get_parser("rust").await;
assert!(rust_parser.is_ok());
let python_parser = pool_manager.get_parser("python").await;
assert!(python_parser.is_ok());
let unknown = pool_manager.get_parser("cobol").await;
assert!(unknown.is_err());
}
}