use super::{
ArtifactSeverity, FormattingArtifact, LanguageFeature, LogicalConstruct, RecognizedPattern,
};
use crate::core::Language;
use serde::{Deserialize, Serialize};
pub trait SourceTracker: Send + Sync {
fn track_complexity_source(&self, ast_node: &AstNode) -> Vec<ComplexityAttribution>;
}
pub struct AstNode {
pub node_type: String,
pub complexity: u32,
pub line: u32,
pub column: u32,
}
pub struct ComplexityAttribution {
pub source_type: ComplexitySourceType,
pub contribution: u32,
pub confidence: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ComplexitySourceType {
LogicalStructure {
construct_type: LogicalConstruct,
nesting_level: u32,
},
FormattingArtifact {
artifact_type: FormattingArtifact,
severity: ArtifactSeverity,
},
PatternRecognition {
pattern_type: RecognizedPattern,
adjustment_factor: f32,
},
LanguageSpecific {
language: Language,
feature: LanguageFeature,
},
}
#[derive(Default)]
pub struct LogicalStructureTracker {
nesting_stack: Vec<LogicalConstruct>,
}
impl LogicalStructureTracker {
pub fn new() -> Self {
Self::default()
}
fn get_construct_type(node_type: &str) -> Option<LogicalConstruct> {
match node_type {
"function" | "method" => Some(LogicalConstruct::Function),
"if" | "if_else" => Some(LogicalConstruct::If),
"for" | "while" | "loop" => Some(LogicalConstruct::Loop),
"match" | "switch" => Some(LogicalConstruct::Match),
"try" | "try_catch" => Some(LogicalConstruct::Try),
"closure" | "lambda" => Some(LogicalConstruct::Closure),
_ => None,
}
}
}
impl SourceTracker for LogicalStructureTracker {
fn track_complexity_source(&self, ast_node: &AstNode) -> Vec<ComplexityAttribution> {
let mut attributions = Vec::new();
if let Some(construct_type) = Self::get_construct_type(&ast_node.node_type) {
let nesting_level = self.nesting_stack.len() as u32;
attributions.push(ComplexityAttribution {
source_type: ComplexitySourceType::LogicalStructure {
construct_type,
nesting_level,
},
contribution: ast_node.complexity * (1 + nesting_level),
confidence: 0.95,
});
}
attributions
}
}
pub struct FormattingArtifactTracker {
_patterns: Vec<FormattingPattern>,
}
impl Default for FormattingArtifactTracker {
fn default() -> Self {
Self {
_patterns: vec![
FormattingPattern {
_name: "multiline_expression".to_string(),
_artifact_type: FormattingArtifact::MultilineExpression,
_severity: ArtifactSeverity::Medium,
},
FormattingPattern {
_name: "excessive_whitespace".to_string(),
_artifact_type: FormattingArtifact::ExcessiveWhitespace,
_severity: ArtifactSeverity::Low,
},
FormattingPattern {
_name: "inconsistent_indentation".to_string(),
_artifact_type: FormattingArtifact::InconsistentIndentation,
_severity: ArtifactSeverity::High,
},
],
}
}
}
impl FormattingArtifactTracker {
pub fn new() -> Self {
Self::default()
}
fn detect_artifact(&self, node_type: &str) -> Option<(FormattingArtifact, ArtifactSeverity)> {
if node_type.contains("multiline") {
Some((
FormattingArtifact::MultilineExpression,
ArtifactSeverity::Medium,
))
} else if node_type.contains("whitespace") {
Some((
FormattingArtifact::ExcessiveWhitespace,
ArtifactSeverity::Low,
))
} else {
None
}
}
}
impl SourceTracker for FormattingArtifactTracker {
fn track_complexity_source(&self, ast_node: &AstNode) -> Vec<ComplexityAttribution> {
let mut attributions = Vec::new();
if let Some((artifact_type, severity)) = self.detect_artifact(&ast_node.node_type) {
let contribution = match severity {
ArtifactSeverity::Low => 1,
ArtifactSeverity::Medium => 2,
ArtifactSeverity::High => 3,
};
attributions.push(ComplexityAttribution {
source_type: ComplexitySourceType::FormattingArtifact {
artifact_type,
severity,
},
contribution,
confidence: 0.7,
});
}
attributions
}
}
pub struct PatternBasedTracker {
patterns: Vec<CodePattern>,
}
impl Default for PatternBasedTracker {
fn default() -> Self {
Self {
patterns: vec![
CodePattern {
name: "error_handling".to_string(),
pattern_type: RecognizedPattern::ErrorHandling,
adjustment_factor: 0.8,
},
CodePattern {
name: "validation".to_string(),
pattern_type: RecognizedPattern::Validation,
adjustment_factor: 0.9,
},
CodePattern {
name: "data_transformation".to_string(),
pattern_type: RecognizedPattern::DataTransformation,
adjustment_factor: 0.85,
},
],
}
}
}
impl PatternBasedTracker {
pub fn new() -> Self {
Self::default()
}
fn recognize_pattern(&self, node_type: &str) -> Option<(RecognizedPattern, f32)> {
self.patterns
.iter()
.find(|p| node_type.contains(&p.name))
.map(|p| (p.pattern_type.clone(), p.adjustment_factor))
}
}
impl SourceTracker for PatternBasedTracker {
fn track_complexity_source(&self, ast_node: &AstNode) -> Vec<ComplexityAttribution> {
let mut attributions = Vec::new();
if let Some((pattern_type, adjustment_factor)) = self.recognize_pattern(&ast_node.node_type)
{
let adjusted_contribution = (ast_node.complexity as f32 * adjustment_factor) as u32;
attributions.push(ComplexityAttribution {
source_type: ComplexitySourceType::PatternRecognition {
pattern_type,
adjustment_factor,
},
contribution: adjusted_contribution,
confidence: 0.6,
});
}
attributions
}
}
struct FormattingPattern {
_name: String,
_artifact_type: FormattingArtifact,
_severity: ArtifactSeverity,
}
struct CodePattern {
name: String,
pattern_type: RecognizedPattern,
adjustment_factor: f32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logical_structure_tracker() {
let tracker = LogicalStructureTracker::new();
let node = AstNode {
node_type: "function".to_string(),
complexity: 5,
line: 10,
column: 0,
};
let attributions = tracker.track_complexity_source(&node);
assert!(!attributions.is_empty());
let first = &attributions[0];
assert!(matches!(
first.source_type,
ComplexitySourceType::LogicalStructure { .. }
));
}
#[test]
fn test_formatting_artifact_tracker() {
let tracker = FormattingArtifactTracker::new();
let node = AstNode {
node_type: "multiline_expression".to_string(),
complexity: 3,
line: 20,
column: 4,
};
let attributions = tracker.track_complexity_source(&node);
assert!(!attributions.is_empty());
let first = &attributions[0];
assert!(matches!(
first.source_type,
ComplexitySourceType::FormattingArtifact { .. }
));
}
#[test]
fn test_pattern_based_tracker() {
let tracker = PatternBasedTracker::new();
let node = AstNode {
node_type: "error_handling_block".to_string(),
complexity: 10,
line: 30,
column: 0,
};
let attributions = tracker.track_complexity_source(&node);
assert!(!attributions.is_empty());
let first = &attributions[0];
assert!(matches!(
first.source_type,
ComplexitySourceType::PatternRecognition { .. }
));
}
#[test]
fn test_get_construct_type() {
assert_eq!(
LogicalStructureTracker::get_construct_type("function"),
Some(LogicalConstruct::Function)
);
assert_eq!(
LogicalStructureTracker::get_construct_type("if"),
Some(LogicalConstruct::If)
);
assert_eq!(LogicalStructureTracker::get_construct_type("unknown"), None);
}
}