use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ParserConfig {
pub backend: ParserBackend,
pub timeout: Duration,
pub max_expression_depth: u32,
pub max_collection_size: u32,
pub max_string_length: u32,
pub max_parameters: u32,
pub strict_validation: bool,
pub allow_experimental: bool,
pub backend_options: HashMap<String, serde_json::Value>,
pub features: Vec<ParserFeature>,
pub memory_limits: MemoryLimits,
pub performance: PerformanceSettings,
pub error_handling: ErrorHandlingSettings,
pub memory_settings: MemorySettings,
pub security_settings: SecuritySettings,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ParserBackend {
Nom,
Antlr,
Auto,
Custom(String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ParserFeature {
Streaming,
ErrorRecovery,
SyntaxHighlighting,
CodeCompletion,
AstTransformation,
CustomOperators,
Parallel,
Caching,
OnlineValidation,
Profiling,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MemoryLimits {
pub max_ast_size: u64,
pub max_temp_memory: u64,
pub max_stack_depth: u32,
pub max_cache_entries: u32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PerformanceSettings {
pub worker_threads: u32,
pub stream_buffer_size: u32,
pub enable_caching: bool,
pub cache_ttl: Duration,
pub enable_jit: bool,
pub optimization_level: u8,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ErrorHandlingSettings {
pub max_errors: u32,
pub continue_on_error: bool,
pub error_context_lines: u32,
pub include_suggestions: bool,
pub collect_error_stats: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MemorySettings {
pub max_parser_memory: u64,
pub allocation_strategy: MemoryAllocationStrategy,
pub enable_memory_pooling: bool,
pub memory_pool_size: usize,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum MemoryAllocationStrategy {
Standard,
Pooled,
Arena,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SecuritySettings {
pub max_query_depth: u32,
pub max_token_count: u32,
pub enable_input_sanitization: bool,
pub restrict_dangerous_operations: bool,
pub blocked_keywords: Vec<String>,
}
impl Default for ParserConfig {
fn default() -> Self {
Self {
backend: ParserBackend::Auto,
timeout: Duration::from_secs(30),
max_expression_depth: 100,
max_collection_size: 10_000,
max_string_length: 1_000_000,
max_parameters: 1000,
strict_validation: true,
allow_experimental: false,
backend_options: HashMap::new(),
features: vec![
ParserFeature::ErrorRecovery,
ParserFeature::OnlineValidation,
],
memory_limits: MemoryLimits::default(),
performance: PerformanceSettings::default(),
error_handling: ErrorHandlingSettings::default(),
memory_settings: MemorySettings::default(),
security_settings: SecuritySettings::default(),
}
}
}
impl Default for MemoryLimits {
fn default() -> Self {
Self {
max_ast_size: 100 * 1024 * 1024, max_temp_memory: 50 * 1024 * 1024, max_stack_depth: 1000,
max_cache_entries: 10_000,
}
}
}
impl Default for PerformanceSettings {
fn default() -> Self {
Self {
worker_threads: num_cpus::get() as u32,
stream_buffer_size: 64 * 1024, enable_caching: true,
cache_ttl: Duration::from_secs(300), enable_jit: false,
optimization_level: 2,
}
}
}
impl Default for ErrorHandlingSettings {
fn default() -> Self {
Self {
max_errors: 100,
continue_on_error: true,
error_context_lines: 3,
include_suggestions: true,
collect_error_stats: false,
}
}
}
impl Default for MemorySettings {
fn default() -> Self {
Self {
max_parser_memory: 50 * 1024 * 1024, allocation_strategy: MemoryAllocationStrategy::Standard,
enable_memory_pooling: false,
memory_pool_size: 1024,
}
}
}
impl Default for SecuritySettings {
fn default() -> Self {
Self {
max_query_depth: 100,
max_token_count: 10_000,
enable_input_sanitization: true,
restrict_dangerous_operations: true,
blocked_keywords: vec![],
}
}
}
impl ParserConfig {
pub fn new() -> Self {
Self::default()
}
pub fn fast() -> Self {
Self {
backend: ParserBackend::Nom,
timeout: Duration::from_secs(10),
strict_validation: false,
features: vec![
ParserFeature::Parallel,
ParserFeature::Caching,
ParserFeature::Streaming,
],
performance: PerformanceSettings {
optimization_level: 3,
enable_jit: true,
worker_threads: (num_cpus::get() as u32).max(2),
..Default::default()
},
..Default::default()
}
}
pub fn strict() -> Self {
Self {
backend: ParserBackend::Antlr,
strict_validation: true,
allow_experimental: false,
features: vec![
ParserFeature::ErrorRecovery,
ParserFeature::OnlineValidation,
ParserFeature::Profiling,
ParserFeature::SyntaxHighlighting,
],
error_handling: ErrorHandlingSettings {
max_errors: 1,
continue_on_error: false,
include_suggestions: true,
collect_error_stats: true,
..Default::default()
},
..Default::default()
}
}
pub fn development() -> Self {
Self {
backend: ParserBackend::Auto,
allow_experimental: true,
features: vec![
ParserFeature::ErrorRecovery,
ParserFeature::SyntaxHighlighting,
ParserFeature::CodeCompletion,
ParserFeature::AstTransformation,
ParserFeature::OnlineValidation,
ParserFeature::Profiling,
],
error_handling: ErrorHandlingSettings {
continue_on_error: true,
include_suggestions: true,
collect_error_stats: true,
..Default::default()
},
..Default::default()
}
}
pub fn minimal() -> Self {
Self {
backend: ParserBackend::Nom,
timeout: Duration::from_secs(5),
max_expression_depth: 50,
max_collection_size: 1000,
max_string_length: 10_000,
max_parameters: 100,
strict_validation: false,
features: vec![],
memory_limits: MemoryLimits {
max_ast_size: 10 * 1024 * 1024, max_temp_memory: 5 * 1024 * 1024, max_stack_depth: 100,
max_cache_entries: 100,
},
performance: PerformanceSettings {
worker_threads: 1,
enable_caching: false,
optimization_level: 1,
..Default::default()
},
error_handling: ErrorHandlingSettings {
max_errors: 10,
error_context_lines: 1,
include_suggestions: false,
collect_error_stats: false,
..Default::default()
},
..Default::default()
}
}
pub fn with_backend(mut self, backend: ParserBackend) -> Self {
self.backend = backend;
self
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn with_strict_validation(mut self, strict: bool) -> Self {
self.strict_validation = strict;
self
}
pub fn with_feature(mut self, feature: ParserFeature) -> Self {
if !self.features.contains(&feature) {
self.features.push(feature);
}
self
}
pub fn has_feature(&self, feature: &ParserFeature) -> bool {
self.features.contains(feature)
}
pub fn validate(&self) -> Result<(), String> {
if self.timeout.as_secs() == 0 {
return Err("Timeout must be greater than 0".to_string());
}
if self.max_expression_depth == 0 {
return Err("Max expression depth must be greater than 0".to_string());
}
if self.max_collection_size == 0 {
return Err("Max collection size must be greater than 0".to_string());
}
if self.memory_limits.max_ast_size == 0 {
return Err("Max AST size must be greater than 0".to_string());
}
if self.memory_limits.max_stack_depth == 0 {
return Err("Max stack depth must be greater than 0".to_string());
}
if self.performance.worker_threads == 0 {
return Err("Worker threads must be greater than 0".to_string());
}
if self.performance.stream_buffer_size < 1024 {
return Err("Stream buffer size should be at least 1KB for efficiency".to_string());
}
if self.performance.optimization_level > 3 {
return Err("Optimization level must be 0-3".to_string());
}
if self.error_handling.max_errors == 0 {
return Err("Max errors must be greater than 0".to_string());
}
if self.has_feature(&ParserFeature::Parallel) && self.performance.worker_threads == 1 {
return Err("Parallel parsing requires at least 2 worker threads. Use ParserConfig::fast() for automatic thread count adjustment.".to_string());
}
if self.has_feature(&ParserFeature::Streaming)
&& matches!(self.backend, ParserBackend::Antlr)
{
return Err("Streaming is not supported with ANTLR backend".to_string());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = ParserConfig::default();
assert!(matches!(config.backend, ParserBackend::Auto));
assert!(config.strict_validation);
assert!(!config.allow_experimental);
assert!(config.validate().is_ok());
}
#[test]
fn test_preset_configs() {
let fast = ParserConfig::fast();
assert!(matches!(fast.backend, ParserBackend::Nom));
assert!(!fast.strict_validation);
assert!(fast.validate().is_ok());
let strict = ParserConfig::strict();
assert!(matches!(strict.backend, ParserBackend::Antlr));
assert!(strict.strict_validation);
assert!(strict.validate().is_ok());
let minimal = ParserConfig::minimal();
assert_eq!(minimal.memory_limits.max_ast_size, 10 * 1024 * 1024);
assert!(minimal.validate().is_ok());
}
#[test]
fn test_config_validation() {
let mut config = ParserConfig::default();
assert!(config.validate().is_ok());
config.timeout = Duration::from_secs(0);
assert!(config.validate().is_err());
config = ParserConfig::default();
config.performance.optimization_level = 5;
assert!(config.validate().is_err());
}
#[test]
fn test_feature_management() {
let mut config = ParserConfig::default();
assert!(!config.has_feature(&ParserFeature::Streaming));
config = config.with_feature(ParserFeature::Streaming);
assert!(config.has_feature(&ParserFeature::Streaming));
}
#[test]
fn test_parallel_requires_multiple_workers() {
let mut config = ParserConfig::minimal().with_feature(ParserFeature::Parallel);
config.performance.worker_threads = 1;
let err = config
.validate()
.expect_err("parallel parsing should require >1 worker");
assert!(err.contains("Parallel parsing requires at least 2 worker threads"));
}
#[test]
fn test_streaming_not_allowed_with_antlr() {
let config = ParserConfig::strict().with_feature(ParserFeature::Streaming);
let err = config
.validate()
.expect_err("streaming should be incompatible with ANTLR backend");
assert!(err.contains("Streaming is not supported with ANTLR backend"));
}
}