debtmap 0.17.0

Code complexity and technical debt analyzer
Documentation
use super::{
    ArtifactSeverity, FormattingArtifact, LanguageFeature, LogicalConstruct, RecognizedPattern,
};
use crate::core::Language;
use serde::{Deserialize, Serialize};

/// Trait for tracking complexity sources
pub trait SourceTracker: Send + Sync {
    /// Analyzes an AST node and returns complexity attributions.
    ///
    /// Each attribution identifies a specific source of complexity with
    /// its contribution amount and confidence level.
    fn track_complexity_source(&self, ast_node: &AstNode) -> Vec<ComplexityAttribution>;
}

/// Placeholder AST node for tracking
pub struct AstNode {
    /// The type of AST node (e.g., "function", "if", "loop").
    pub node_type: String,
    /// The base complexity score for this node.
    pub complexity: u32,
    /// The source line number where this node appears.
    pub line: u32,
    /// The source column where this node begins.
    pub column: u32,
}

/// Complexity attribution from source tracking
pub struct ComplexityAttribution {
    /// The type of complexity source that was detected.
    pub source_type: ComplexitySourceType,
    /// The amount this source contributes to total complexity.
    pub contribution: u32,
    /// Confidence level (0.0 to 1.0) in this attribution.
    pub confidence: f32,
}

/// Types of complexity sources
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ComplexitySourceType {
    /// Complexity from logical control flow structures.
    LogicalStructure {
        /// The type of control flow construct.
        construct_type: LogicalConstruct,
        /// How deeply nested this construct is.
        nesting_level: u32,
    },
    /// Complexity from formatting patterns that may inflate metrics.
    FormattingArtifact {
        /// The type of formatting artifact detected.
        artifact_type: FormattingArtifact,
        /// How significantly this artifact inflates complexity.
        severity: ArtifactSeverity,
    },
    /// Complexity adjusted for recognized coding patterns.
    PatternRecognition {
        /// The recognized pattern type.
        pattern_type: RecognizedPattern,
        /// Multiplier applied to complexity (< 1.0 reduces perceived complexity).
        adjustment_factor: f32,
    },
    /// Complexity from language-specific features.
    LanguageSpecific {
        /// The programming language.
        language: Language,
        /// The language feature contributing to complexity.
        feature: LanguageFeature,
    },
}

/// Tracker for logical structure complexity
#[derive(Default)]
pub struct LogicalStructureTracker {
    nesting_stack: Vec<LogicalConstruct>,
}

impl LogicalStructureTracker {
    /// Creates a new logical structure tracker with an empty nesting stack.
    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
    }
}

/// Tracker for formatting artifacts
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 {
    /// Creates a new formatting artifact tracker with default patterns.
    pub fn new() -> Self {
        Self::default()
    }

    fn detect_artifact(&self, node_type: &str) -> Option<(FormattingArtifact, ArtifactSeverity)> {
        // Simplified artifact detection
        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
    }
}

/// Pattern tracker for pattern-based complexity
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 {
    /// Creates a new pattern-based tracker with default code patterns.
    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);
    }
}