Skip to main content

cqlite_core/cql/
factory.rs

1//! Parser factory for creating parser instances
2//!
3//! This module provides factory functions for creating parser instances
4//! with different backends and configurations.
5
6use crate::error::{Error, Result};
7use std::sync::{Arc, Mutex, OnceLock};
8
9use super::{
10    antlr_backend::AntlrParser,
11    config::{ParserBackend, ParserConfig},
12    nom_backend::NomParser,
13    traits::{CqlParser, CqlParserFactory, FactoryInfo, ParserBackendInfo},
14};
15
16/// Main parser factory
17#[derive(Debug, Default)]
18pub struct ParserFactory;
19
20impl ParserFactory {
21    /// Create a parser with the default configuration.
22    pub fn create_default() -> Result<Arc<dyn CqlParser + Send + Sync>> {
23        Self::create(ParserConfig::default())
24    }
25
26    /// Create a parser with the specified configuration.
27    pub fn create(mut config: ParserConfig) -> Result<Arc<dyn CqlParser + Send + Sync>> {
28        config.validate().map_err(Error::configuration)?;
29
30        if matches!(config.backend, ParserBackend::Auto) {
31            config.backend = Self::select_optimal_backend(&config);
32        }
33
34        match config.backend.clone() {
35            ParserBackend::Nom => Ok(Arc::new(NomParser::new(config)?)),
36            ParserBackend::Antlr => Ok(Arc::new(AntlrParser::new(config)?)),
37            ParserBackend::Auto => unreachable!("Auto resolved above"),
38            ParserBackend::Custom(name) => Err(Error::configuration(format!(
39                "Custom parser '{}' not available",
40                name
41            ))),
42        }
43    }
44
45    /// Select the best backend given the feature set in `config`.
46    ///
47    /// ANTLR is preferred when features require rich diagnostics or
48    /// tooling support; otherwise nom is used.
49    fn select_optimal_backend(config: &ParserConfig) -> ParserBackend {
50        use super::config::ParserFeature;
51
52        let needs_antlr = config.has_feature(&ParserFeature::ErrorRecovery)
53            || config.has_feature(&ParserFeature::SyntaxHighlighting)
54            || config.has_feature(&ParserFeature::CodeCompletion)
55            || config.strict_validation;
56
57        if needs_antlr {
58            ParserBackend::Antlr
59        } else {
60            ParserBackend::Nom
61        }
62    }
63
64    /// Get information about available backends.
65    pub fn get_available_backends() -> Vec<ParserBackendInfo> {
66        vec![NomParser::backend_info(), AntlrParser::backend_info()]
67    }
68
69    /// Check whether the given backend can be instantiated.
70    pub fn is_backend_available(backend: &ParserBackend) -> bool {
71        match backend {
72            ParserBackend::Nom | ParserBackend::Antlr | ParserBackend::Auto => true,
73            ParserBackend::Custom(_) => false,
74        }
75    }
76
77    /// Recommend a backend for a high-level use case.
78    pub fn recommend_backend(use_case: UseCase) -> ParserBackend {
79        match use_case {
80            UseCase::HighPerformance | UseCase::Embedded | UseCase::Batch => ParserBackend::Nom,
81            UseCase::Development | UseCase::Interactive => ParserBackend::Antlr,
82            UseCase::Production => ParserBackend::Auto,
83        }
84    }
85
86    /// Create a parser optimized for a specific use case.
87    pub fn create_for_use_case(use_case: UseCase) -> Result<Arc<dyn CqlParser + Send + Sync>> {
88        let backend = Self::recommend_backend(use_case.clone());
89        Self::create(Self::create_config_for_use_case(use_case, backend))
90    }
91
92    fn create_config_for_use_case(use_case: UseCase, backend: ParserBackend) -> ParserConfig {
93        use super::config::ParserFeature;
94        use std::time::Duration;
95
96        match use_case {
97            UseCase::HighPerformance => ParserConfig::fast().with_backend(backend),
98            UseCase::Development => ParserConfig::development().with_backend(backend),
99            UseCase::Production => ParserConfig::default().with_backend(backend),
100            UseCase::Embedded => ParserConfig::minimal().with_backend(backend),
101            UseCase::Interactive => ParserConfig::development()
102                .with_backend(backend)
103                .with_feature(ParserFeature::CodeCompletion)
104                .with_feature(ParserFeature::SyntaxHighlighting)
105                .with_timeout(Duration::from_millis(100)),
106            UseCase::Batch => ParserConfig::fast()
107                .with_backend(backend)
108                .with_feature(ParserFeature::Parallel)
109                .with_timeout(Duration::from_secs(300)),
110        }
111    }
112}
113
114impl CqlParserFactory for ParserFactory {
115    fn create_parser(&self) -> Result<Box<dyn CqlParser>> {
116        self.create_parser_with_config(ParserConfig::default())
117    }
118
119    fn create_parser_with_config(&self, config: ParserConfig) -> Result<Box<dyn CqlParser>> {
120        Ok(Box::new(ParserWrapper {
121            inner: Self::create(config)?,
122        }))
123    }
124
125    fn factory_info(&self) -> FactoryInfo {
126        FactoryInfo {
127            name: "DefaultParserFactory".to_string(),
128            supported_backends: vec!["nom".to_string(), "antlr".to_string()],
129            default_backend: "auto".to_string(),
130        }
131    }
132}
133
134/// Adapter turning the shared `Arc<dyn CqlParser + Send + Sync>` returned by
135/// [`ParserFactory::create`] into the `Box<dyn CqlParser>` required by
136/// [`CqlParserFactory`].
137#[derive(Debug)]
138struct ParserWrapper {
139    inner: Arc<dyn CqlParser + Send + Sync>,
140}
141
142#[async_trait::async_trait]
143impl CqlParser for ParserWrapper {
144    async fn parse(&self, input: &str) -> Result<super::ast::CqlStatement> {
145        self.inner.parse(input).await
146    }
147
148    async fn parse_type(&self, input: &str) -> Result<super::ast::CqlDataType> {
149        self.inner.parse_type(input).await
150    }
151
152    async fn parse_expression(&self, input: &str) -> Result<super::ast::CqlExpression> {
153        self.inner.parse_expression(input).await
154    }
155
156    async fn parse_identifier(&self, input: &str) -> Result<super::ast::CqlIdentifier> {
157        self.inner.parse_identifier(input).await
158    }
159
160    async fn parse_literal(&self, input: &str) -> Result<super::ast::CqlLiteral> {
161        self.inner.parse_literal(input).await
162    }
163
164    async fn parse_column_definitions(&self, input: &str) -> Result<Vec<super::ast::CqlColumnDef>> {
165        self.inner.parse_column_definitions(input).await
166    }
167
168    async fn parse_table_options(&self, input: &str) -> Result<super::ast::CqlTableOptions> {
169        self.inner.parse_table_options(input).await
170    }
171
172    fn validate_syntax(&self, input: &str) -> bool {
173        self.inner.validate_syntax(input)
174    }
175
176    fn backend_info(&self) -> super::traits::ParserBackendInfo {
177        self.inner.backend_info()
178    }
179}
180
181/// Use case categories for parser optimization.
182#[derive(Debug, Clone, PartialEq)]
183pub enum UseCase {
184    /// High-performance parsing with minimal overhead.
185    HighPerformance,
186    /// Development environment with rich error messages and debugging.
187    Development,
188    /// Production environment with balanced performance and reliability.
189    Production,
190    /// Embedded systems with strict resource constraints.
191    Embedded,
192    /// Interactive environments requiring fast response times.
193    Interactive,
194    /// Batch processing with large volumes of queries.
195    Batch,
196}
197
198/// Registry for custom parser backends.
199#[derive(Debug, Default)]
200pub struct ParserRegistry {
201    custom_factories: std::collections::HashMap<String, Box<dyn CqlParserFactory + Send + Sync>>,
202}
203
204impl ParserRegistry {
205    pub fn new() -> Self {
206        Self::default()
207    }
208
209    pub fn register_factory(
210        &mut self,
211        name: String,
212        factory: Box<dyn CqlParserFactory + Send + Sync>,
213    ) {
214        self.custom_factories.insert(name, factory);
215    }
216
217    pub fn get_factory(&self, name: &str) -> Option<&(dyn CqlParserFactory + Send + Sync)> {
218        self.custom_factories.get(name).map(|f| f.as_ref())
219    }
220
221    pub fn list_factories(&self) -> Vec<&str> {
222        self.custom_factories.keys().map(|s| s.as_str()).collect()
223    }
224
225    /// Create a parser using a registered factory.
226    pub fn create_with_factory(
227        &self,
228        factory_name: &str,
229        config: ParserConfig,
230    ) -> Result<Box<dyn CqlParser>> {
231        self.get_factory(factory_name)
232            .ok_or_else(|| Error::configuration(format!("Factory '{}' not found", factory_name)))?
233            .create_parser_with_config(config)
234    }
235}
236
237static GLOBAL_REGISTRY: OnceLock<Mutex<ParserRegistry>> = OnceLock::new();
238
239/// Register a factory in the process-wide registry.
240pub fn register_global_factory(name: String, factory: Box<dyn CqlParserFactory + Send + Sync>) {
241    let registry = GLOBAL_REGISTRY.get_or_init(|| Mutex::new(ParserRegistry::new()));
242    let mut guard = registry.lock().unwrap_or_else(|e| e.into_inner());
243    guard.register_factory(name, factory);
244}
245
246/// Benchmark helpers for comparing parser backends.
247pub mod benchmarks {
248    use super::*;
249    use std::time::{Duration, Instant};
250
251    #[derive(Debug, Clone)]
252    pub struct BenchmarkResult {
253        pub backend: String,
254        pub avg_parse_time: Duration,
255        pub min_parse_time: Duration,
256        pub max_parse_time: Duration,
257        pub success_rate: f64,
258        pub errors: Vec<String>,
259    }
260
261    impl BenchmarkResult {
262        fn failed(backend: &ParserBackend, error: String) -> Self {
263            Self {
264                backend: format!("{:?}", backend),
265                avg_parse_time: Duration::ZERO,
266                min_parse_time: Duration::ZERO,
267                max_parse_time: Duration::ZERO,
268                success_rate: 0.0,
269                errors: vec![error],
270            }
271        }
272    }
273
274    #[derive(Debug, Clone)]
275    pub struct BenchmarkConfig {
276        pub iterations: u32,
277        pub timeout: Duration,
278        pub test_cases: Vec<String>,
279    }
280
281    impl Default for BenchmarkConfig {
282        fn default() -> Self {
283            Self {
284                iterations: 100,
285                timeout: Duration::from_secs(1),
286                test_cases: vec![
287                    "SELECT * FROM users".to_string(),
288                    "INSERT INTO users (id, name) VALUES (?, ?)".to_string(),
289                    "UPDATE users SET name = ? WHERE id = ?".to_string(),
290                    "DELETE FROM users WHERE id = ?".to_string(),
291                    "CREATE TABLE test (id UUID PRIMARY KEY, data TEXT)".to_string(),
292                ],
293            }
294        }
295    }
296
297    /// Run benchmarks on all available parser backends.
298    pub async fn benchmark_parsers(config: BenchmarkConfig) -> Vec<BenchmarkResult> {
299        let mut results = Vec::new();
300        for backend in [ParserBackend::Nom, ParserBackend::Antlr] {
301            if ParserFactory::is_backend_available(&backend) {
302                results.push(benchmark_backend(backend, &config).await);
303            }
304        }
305        results
306    }
307
308    async fn benchmark_backend(
309        backend: ParserBackend,
310        config: &BenchmarkConfig,
311    ) -> BenchmarkResult {
312        let parser_config = ParserConfig::default().with_backend(backend.clone());
313        let parser = match ParserFactory::create(parser_config) {
314            Ok(p) => p,
315            Err(e) => {
316                return BenchmarkResult::failed(&backend, format!("Failed to create parser: {}", e))
317            }
318        };
319
320        let mut times = Vec::new();
321        let mut errors = Vec::new();
322        let mut successes: u32 = 0;
323
324        for _ in 0..config.iterations {
325            for test_case in &config.test_cases {
326                let start = Instant::now();
327                match tokio::time::timeout(config.timeout, parser.parse(test_case)).await {
328                    Ok(Ok(_)) => {
329                        successes += 1;
330                        times.push(start.elapsed());
331                    }
332                    Ok(Err(e)) => errors.push(format!("Parse error: {}", e)),
333                    Err(_) => errors.push("Timeout".to_string()),
334                }
335            }
336        }
337
338        let total_attempts = config.iterations * config.test_cases.len() as u32;
339        let success_rate = f64::from(successes) / f64::from(total_attempts);
340        let avg_parse_time = if times.is_empty() {
341            Duration::ZERO
342        } else {
343            times.iter().sum::<Duration>() / times.len() as u32
344        };
345
346        BenchmarkResult {
347            backend: format!("{:?}", backend),
348            avg_parse_time,
349            min_parse_time: times.iter().min().copied().unwrap_or_default(),
350            max_parse_time: times.iter().max().copied().unwrap_or_default(),
351            success_rate,
352            errors,
353        }
354    }
355}
356
357#[cfg(test)]
358mod tests {
359    use super::*;
360
361    #[test]
362    fn test_factory_creation() {
363        let factory = ParserFactory;
364        let info = factory.factory_info();
365        assert_eq!(info.name, "DefaultParserFactory");
366        assert!(!info.supported_backends.is_empty());
367    }
368
369    #[test]
370    fn test_backend_availability() {
371        assert!(ParserFactory::is_backend_available(&ParserBackend::Nom));
372        assert!(ParserFactory::is_backend_available(&ParserBackend::Auto));
373        assert!(!ParserFactory::is_backend_available(
374            &ParserBackend::Custom("unknown".to_string())
375        ));
376    }
377
378    #[test]
379    fn test_backend_recommendation() {
380        assert_eq!(
381            ParserFactory::recommend_backend(UseCase::HighPerformance),
382            ParserBackend::Nom
383        );
384        assert_eq!(
385            ParserFactory::recommend_backend(UseCase::Development),
386            ParserBackend::Antlr
387        );
388    }
389
390    #[test]
391    fn test_auto_backend_selection() {
392        let config = ParserConfig::fast();
393        let backend = ParserFactory::select_optimal_backend(&config);
394        assert_eq!(backend, ParserBackend::Nom);
395
396        let config = ParserConfig::strict();
397        let backend = ParserFactory::select_optimal_backend(&config);
398        assert_eq!(backend, ParserBackend::Antlr);
399    }
400
401    #[test]
402    fn test_parser_registry() {
403        let registry = ParserRegistry::new();
404        assert!(registry.list_factories().is_empty());
405    }
406}