use crate::types::Doc;
use async_trait::async_trait;
use thread_utilities::RapidMap;
use crate::error::{AnalysisError, ServiceResult};
use crate::types::{AnalysisContext, CodeMatch, CrossFileRelationship, ParsedDocument};
#[async_trait]
pub trait CodeAnalyzer<D: Doc + Send + Sync>: Send + Sync {
async fn find_pattern(
&self,
document: &ParsedDocument<D>,
pattern: &str,
context: &AnalysisContext,
) -> ServiceResult<Vec<CodeMatch<'_, D>>>;
async fn find_all_patterns(
&self,
document: &ParsedDocument<D>,
patterns: &[&str],
context: &AnalysisContext,
) -> ServiceResult<Vec<CodeMatch<'_, D>>>;
async fn replace_pattern(
&self,
document: &mut ParsedDocument<D>,
pattern: &str,
replacement: &str,
context: &AnalysisContext,
) -> ServiceResult<usize>;
async fn analyze_cross_file_relationships(
&self,
documents: &[ParsedDocument<D>],
context: &AnalysisContext,
) -> ServiceResult<Vec<CrossFileRelationship>>;
fn capabilities(&self) -> AnalyzerCapabilities;
async fn find_nodes_by_kind(
&self,
document: &ParsedDocument<D>,
node_kind: &str,
context: &AnalysisContext,
) -> ServiceResult<Vec<CodeMatch<'_, D>>> {
let pattern = match node_kind {
"function_declaration" => "fn $NAME($$$PARAMS) { $$$BODY }",
"class_declaration" => "class $NAME { $$$BODY }",
"variable_declaration" => "let $VAR = $VALUE",
_ => {
return Err(AnalysisError::InvalidPattern {
pattern: format!("Unknown node kind: {}", node_kind),
}
.into());
}
};
self.find_pattern(document, pattern, context).await
}
fn validate_pattern(&self, pattern: &str) -> ServiceResult<()> {
if pattern.is_empty() {
return Err(AnalysisError::InvalidPattern {
pattern: "Pattern cannot be empty".to_string(),
}
.into());
}
if pattern.contains('$') {
let mut chars = pattern.chars();
let mut _found_metavar = false;
while let Some(ch) = chars.next() {
if ch == '$' {
_found_metavar = true;
if let Some(next_ch) = chars.next()
&& !next_ch.is_alphabetic()
&& next_ch != '_'
{
return Err(AnalysisError::MetaVariable {
variable: format!("${}", next_ch),
message: "Invalid meta-variable format".to_string(),
}
.into());
}
}
}
}
Ok(())
}
#[cfg(feature = "matching")]
async fn compile_pattern(&self, pattern: &str) -> ServiceResult<CompiledPattern> {
Ok(CompiledPattern {
pattern: pattern.to_string(),
compiled_data: None,
})
}
async fn batch_analyze(
&self,
documents: &[ParsedDocument<D>],
patterns: &[&str],
context: &AnalysisContext,
) -> ServiceResult<Vec<Vec<CodeMatch<'_, D>>>> {
let mut results = Vec::new();
for document in documents {
let doc_results = self.find_all_patterns(document, patterns, context).await?;
results.push(doc_results);
}
Ok(results)
}
async fn extract_symbols(
&self,
_document: &mut ParsedDocument<D>,
_context: &AnalysisContext,
) -> ServiceResult<()> {
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct AnalyzerCapabilities {
pub max_concurrent_patterns: Option<usize>,
pub max_matches_per_pattern: Option<usize>,
pub supports_pattern_compilation: bool,
pub supports_cross_file_analysis: bool,
pub supports_batch_optimization: bool,
pub supports_incremental_analysis: bool,
pub supported_analysis_depths: Vec<AnalysisDepth>,
pub performance_profile: AnalysisPerformanceProfile,
pub capability_flags: RapidMap<String, bool>,
}
impl Default for AnalyzerCapabilities {
fn default() -> Self {
Self {
max_concurrent_patterns: Some(50),
max_matches_per_pattern: Some(1000),
supports_pattern_compilation: false,
supports_cross_file_analysis: false,
supports_batch_optimization: true,
supports_incremental_analysis: false,
supported_analysis_depths: vec![AnalysisDepth::Syntax, AnalysisDepth::Local],
performance_profile: AnalysisPerformanceProfile::Balanced,
capability_flags: thread_utilities::get_map(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AnalysisDepth {
Syntax,
Local,
CrossFile,
Deep,
}
#[derive(Debug, Clone, PartialEq)]
pub enum AnalysisPerformanceProfile {
LowMemory,
FastAnalysis,
Balanced,
ComplexPatterns,
LargeCodebase,
}
#[derive(Debug, Clone)]
pub struct CompiledPattern {
pub pattern: String,
pub compiled_data: Option<std::sync::Arc<dyn std::any::Any + Send + Sync>>,
}
#[derive(Debug, Clone)]
pub struct AnalysisConfig {
pub max_depth: AnalysisDepth,
pub collect_relationships: bool,
pub enable_pattern_caching: bool,
pub performance_profile: Option<AnalysisPerformanceProfile>,
pub custom_options: RapidMap<String, String>,
}
impl Default for AnalysisConfig {
fn default() -> Self {
Self {
max_depth: AnalysisDepth::Local,
collect_relationships: false,
enable_pattern_caching: true,
performance_profile: None, custom_options: thread_utilities::get_map(),
}
}
}
pub trait AnalyzerFactory<D: Doc + Send + Sync>: Send + Sync {
fn create_analyzer(&self) -> Box<dyn CodeAnalyzer<D>>;
fn create_configured_analyzer(&self, config: AnalysisConfig) -> Box<dyn CodeAnalyzer<D>>;
fn available_analyzers(&self) -> Vec<String>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_analyzer_capabilities_default() {
let caps = AnalyzerCapabilities::default();
assert!(!caps.supports_cross_file_analysis);
assert!(caps.supports_batch_optimization);
assert!(!caps.supports_pattern_compilation);
assert_eq!(
caps.performance_profile,
AnalysisPerformanceProfile::Balanced
);
}
#[test]
fn test_analysis_config_default() {
let config = AnalysisConfig::default();
assert_eq!(config.max_depth, AnalysisDepth::Local);
assert!(!config.collect_relationships);
assert!(config.enable_pattern_caching);
}
}