repotoire 0.3.47

Graph-powered code analysis CLI. 81 detectors for security, architecture, and code quality.
//! Long Methods Detector

use crate::detectors::base::{Detector, DetectorConfig};
use crate::graph::GraphStore;
use crate::models::{Finding, Severity};
use anyhow::Result;
use std::path::PathBuf;
use uuid::Uuid;

pub struct LongMethodsDetector {
    repository_path: PathBuf,
    config: DetectorConfig,
    max_findings: usize,
    threshold: u32,
}

impl LongMethodsDetector {
    pub fn new(repository_path: impl Into<PathBuf>) -> Self {
        Self { 
            repository_path: repository_path.into(), 
            config: DetectorConfig::new(),
            max_findings: 100, 
            threshold: 50,
        }
    }

    /// Create with custom config (reads max_lines threshold from project config)
    pub fn with_config(repository_path: impl Into<PathBuf>, config: DetectorConfig) -> Self {
        let threshold = config.get_option_or("max_lines", 50) as u32;
        Self { 
            repository_path: repository_path.into(), 
            max_findings: 100, 
            threshold,
            config,
        }
    }
}

impl Detector for LongMethodsDetector {
    fn name(&self) -> &'static str { "long-methods" }
    fn description(&self) -> &'static str { "Detects methods/functions over 50 lines" }

    fn detect(&self, graph: &GraphStore) -> Result<Vec<Finding>> {
        let mut findings = vec![];

        for func in graph.get_functions() {
            if findings.len() >= self.max_findings { break; }
            
            let lines = func.line_end.saturating_sub(func.line_start);
            if lines > self.threshold {
                let severity = if lines > 200 { Severity::High } else if lines > 100 { Severity::Medium } else { Severity::Low };
                
                findings.push(Finding {
                    id: Uuid::new_v4().to_string(),
                    detector: "LongMethodsDetector".to_string(),
                    severity,
                    title: format!("Long method: {} ({} lines)", func.name, lines),
                    description: format!("Function '{}' has {} lines (threshold: {}).", func.name, lines, self.threshold),
                    affected_files: vec![PathBuf::from(&func.file_path)],
                    line_start: Some(func.line_start),
                    line_end: Some(func.line_end),
                    suggested_fix: Some("Break into smaller, focused functions.".to_string()),
                    estimated_effort: Some("30 minutes".to_string()),
                    category: Some("maintainability".to_string()),
                    cwe_id: None,
                    why_it_matters: Some("Long methods are hard to understand and test.".to_string()),
                    ..Default::default()
                });
            }
        }
        Ok(findings)
    }
}