context_creator/core/semantic/
parser_pool.rs

1//! Thread-safe parser pool for tree-sitter parsers
2//! Manages parser lifecycle and prevents resource exhaustion
3
4use crate::utils::error::ContextCreatorError;
5use async_trait::async_trait;
6use deadpool::managed::{self, Manager, Metrics, Pool, RecycleResult};
7use std::collections::HashMap;
8use tree_sitter::{Language, Parser};
9
10/// Type alias for a pooled parser
11pub type PooledParser = managed::Object<ParserManager>;
12
13/// Type alias for a parser pool
14pub type ParserPool = Pool<ParserManager>;
15
16/// Manager for creating and recycling tree-sitter parsers
17pub struct ParserManager {
18    language: Language,
19    language_name: &'static str,
20}
21
22impl ParserManager {
23    /// Create a new parser manager for a specific language
24    pub fn new(language: Language, language_name: &'static str) -> Self {
25        Self {
26            language,
27            language_name,
28        }
29    }
30}
31
32#[async_trait]
33impl Manager for ParserManager {
34    type Type = Parser;
35    type Error = ContextCreatorError;
36
37    async fn create(&self) -> Result<Parser, Self::Error> {
38        let mut parser = Parser::new();
39
40        // Set the language
41        parser.set_language(self.language).map_err(|e| {
42            ContextCreatorError::ParseError(format!(
43                "Failed to set {} language: {}",
44                self.language_name, e
45            ))
46        })?;
47
48        // Set timeout to 5 seconds
49        parser.set_timeout_micros(5_000_000);
50
51        Ok(parser)
52    }
53
54    async fn recycle(&self, parser: &mut Parser, _: &Metrics) -> RecycleResult<Self::Error> {
55        // Reset the parser for reuse
56        parser.reset();
57        Ok(())
58    }
59}
60
61/// Manages parser pools for multiple languages
62pub struct ParserPoolManager {
63    pools: HashMap<&'static str, ParserPool>,
64}
65
66impl ParserPoolManager {
67    /// Create a new parser pool manager with pools for all supported languages
68    pub fn new() -> Self {
69        let mut pools = HashMap::new();
70
71        // Create pools for each supported language
72        // Each pool has a maximum of 16 parsers
73        let pool_config = managed::PoolConfig {
74            max_size: 16,
75            ..Default::default()
76        };
77
78        // Rust
79        pools.insert(
80            "rust",
81            Pool::builder(ParserManager::new(tree_sitter_rust::language(), "rust"))
82                .config(pool_config)
83                .build()
84                .expect("Failed to create Rust parser pool"),
85        );
86
87        // JavaScript
88        pools.insert(
89            "javascript",
90            Pool::builder(ParserManager::new(
91                tree_sitter_javascript::language(),
92                "javascript",
93            ))
94            .config(pool_config)
95            .build()
96            .expect("Failed to create JavaScript parser pool"),
97        );
98
99        // Python
100        pools.insert(
101            "python",
102            Pool::builder(ParserManager::new(tree_sitter_python::language(), "python"))
103                .config(pool_config)
104                .build()
105                .expect("Failed to create Python parser pool"),
106        );
107
108        // TypeScript
109        pools.insert(
110            "typescript",
111            Pool::builder(ParserManager::new(
112                tree_sitter_typescript::language_typescript(),
113                "typescript",
114            ))
115            .config(pool_config)
116            .build()
117            .expect("Failed to create TypeScript parser pool"),
118        );
119
120        // Go
121        pools.insert(
122            "go",
123            Pool::builder(ParserManager::new(tree_sitter_go::language(), "go"))
124                .config(pool_config)
125                .build()
126                .expect("Failed to create Go parser pool"),
127        );
128
129        // Java
130        pools.insert(
131            "java",
132            Pool::builder(ParserManager::new(tree_sitter_java::language(), "java"))
133                .config(pool_config)
134                .build()
135                .expect("Failed to create Java parser pool"),
136        );
137
138        Self { pools }
139    }
140
141    /// Get a parser from the pool for the specified language
142    pub async fn get_parser(&self, language: &str) -> Result<PooledParser, ContextCreatorError> {
143        let pool = self.pools.get(language).ok_or_else(|| {
144            ContextCreatorError::ParseError(format!("Unsupported language: {language}"))
145        })?;
146
147        pool.get().await.map_err(|e| {
148            ContextCreatorError::ParseError(format!(
149                "Failed to get {language} parser from pool: {e}"
150            ))
151        })
152    }
153
154    /// Get pool status for monitoring
155    pub fn get_status(&self, language: &str) -> Option<deadpool::Status> {
156        self.pools.get(language).map(|pool| pool.status())
157    }
158}
159
160impl Default for ParserPoolManager {
161    fn default() -> Self {
162        Self::new()
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[tokio::test]
171    async fn test_parser_creation() {
172        let manager = ParserManager::new(tree_sitter_rust::language(), "rust");
173        let parser = manager.create().await.unwrap();
174
175        // Check timeout is set (tree-sitter returns microseconds as u64, not Option)
176        assert_eq!(parser.timeout_micros(), 5_000_000);
177    }
178
179    #[tokio::test]
180    async fn test_parser_recycling() {
181        let manager = ParserManager::new(tree_sitter_python::language(), "python");
182        let mut parser = manager.create().await.unwrap();
183
184        // Recycling should succeed
185        let result = manager.recycle(&mut parser, &Metrics::default()).await;
186        assert!(result.is_ok());
187    }
188
189    #[tokio::test]
190    async fn test_pool_manager() {
191        let pool_manager = ParserPoolManager::new();
192
193        // Should successfully get parsers
194        let rust_parser = pool_manager.get_parser("rust").await;
195        assert!(rust_parser.is_ok());
196
197        let python_parser = pool_manager.get_parser("python").await;
198        assert!(python_parser.is_ok());
199
200        // Should fail for unsupported language
201        let unknown = pool_manager.get_parser("cobol").await;
202        assert!(unknown.is_err());
203    }
204}