gitstack 5.3.0

Git history viewer with insights - Author stats, file heatmap, code ownership
Documentation
//! Statistics module
//!
//! Calculates per-author statistics and file change frequency for repositories

mod analysis;
mod health;
mod quality;

// =============================================================================
// Re-exports from analysis
// =============================================================================
pub use analysis::{
    calculate_activity_timeline, calculate_file_heatmap, calculate_ownership, calculate_stats,
    ActivityTimeline, AuthorStats, CodeOwnership, CodeOwnershipEntry, FileHeatmap,
    FileHeatmapEntry, RepoStats,
};

// =============================================================================
// Re-exports from quality
// =============================================================================
pub use quality::{
    calculate_change_coupling, calculate_impact_scores, calculate_quality_scores,
    ChangeCouplingAnalysis, CommitImpactAnalysis, CommitImpactScore, CommitQualityAnalysis,
    CommitQualityScore, FileCoupling,
};

// =============================================================================
// Re-exports from health
// =============================================================================
pub use health::{
    calculate_bus_factor, calculate_project_health, calculate_tech_debt, is_test_file,
    AlertSeverity, ConfidenceLevel, HealthAlert, HealthAlertKind, HealthConfidence,
    HealthScoreComponent, ProjectHealth,
};

// =============================================================================
// Shared types (used across sub-modules)
// =============================================================================

/// Aggregation level (for heatmap display)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AggregationLevel {
    /// File level (default)
    #[default]
    Files,
    /// Shallow aggregation (up to 2 levels, e.g., src/auth/)
    Shallow,
    /// Deep aggregation (top level only, e.g., src/)
    Deep,
}

impl AggregationLevel {
    /// Switch to the next aggregation level
    pub fn next(&self) -> Self {
        match self {
            AggregationLevel::Files => AggregationLevel::Shallow,
            AggregationLevel::Shallow => AggregationLevel::Deep,
            AggregationLevel::Deep => AggregationLevel::Files,
        }
    }

    /// Switch to the previous aggregation level
    pub fn prev(&self) -> Self {
        match self {
            AggregationLevel::Files => AggregationLevel::Deep,
            AggregationLevel::Shallow => AggregationLevel::Files,
            AggregationLevel::Deep => AggregationLevel::Shallow,
        }
    }

    /// Get display name
    pub fn display_name(&self) -> &'static str {
        match self {
            AggregationLevel::Files => "Files",
            AggregationLevel::Shallow => "Directories (2 levels)",
            AggregationLevel::Deep => "Directories (top level)",
        }
    }
}

// =============================================================================
// Bus Factor types (shared, used by health.rs)
// =============================================================================

/// Bus factor entry
#[derive(Debug, Clone)]
pub struct BusFactorEntry {
    /// Path (directory or file)
    pub path: String,
    /// Bus factor value (number of people with knowledge)
    pub bus_factor: usize,
    /// List of major contributors (sorted by commit count)
    pub contributors: Vec<ContributorInfo>,
    /// Total commit count
    pub total_commits: usize,
    /// Risk level
    pub risk_level: BusFactorRisk,
    /// Whether this is a directory
    pub is_directory: bool,
}

/// Contributor information
#[derive(Debug, Clone)]
pub struct ContributorInfo {
    /// Author name
    pub name: String,
    /// Commit count
    pub commit_count: usize,
    /// Contribution percentage
    pub contribution_percent: f64,
}

/// Bus factor risk level
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BusFactorRisk {
    /// High risk (1 person only)
    High,
    /// Medium risk (2 people)
    Medium,
    /// Low risk (3 or more people)
    Low,
}

impl BusFactorRisk {
    /// Display name for the risk level
    pub fn display_name(&self) -> &'static str {
        match self {
            BusFactorRisk::High => "High Risk",
            BusFactorRisk::Medium => "Medium Risk",
            BusFactorRisk::Low => "Low Risk",
        }
    }

    /// Color for the risk level
    pub fn color(&self) -> &'static str {
        match self {
            BusFactorRisk::High => "red",
            BusFactorRisk::Medium => "yellow",
            BusFactorRisk::Low => "green",
        }
    }
}

/// Bus factor analysis result
#[derive(Debug, Clone, Default)]
pub struct BusFactorAnalysis {
    /// Entry list (sorted by risk)
    pub entries: Vec<BusFactorEntry>,
    /// Number of high risk areas
    pub high_risk_count: usize,
    /// Number of medium risk areas
    pub medium_risk_count: usize,
    /// Number of paths analyzed
    pub total_paths_analyzed: usize,
}

impl BusFactorAnalysis {
    /// Get entry count
    pub fn entry_count(&self) -> usize {
        self.entries.len()
    }
}

// =============================================================================
// Technical Debt types (shared, used by health.rs)
// =============================================================================

/// Technical debt entry
#[derive(Debug, Clone)]
pub struct TechDebtEntry {
    /// File path
    pub path: String,
    /// Technical debt score (0.0-1.0)
    pub score: f64,
    /// Churn score (0.0-1.0)
    pub churn_score: f64,
    /// Complexity score (0.0-1.0) - based on file size
    pub complexity_score: f64,
    /// Age score (0.0-1.0) - time since last change
    pub age_score: f64,
    /// Change count
    pub change_count: usize,
    /// Total lines changed
    pub total_changes: usize,
    /// Debt level
    pub debt_level: TechDebtLevel,
}

/// Technical debt level
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TechDebtLevel {
    /// High debt (action required)
    High,
    /// Medium debt (caution)
    Medium,
    /// Low debt (good)
    Low,
}

impl TechDebtLevel {
    /// Display name for the debt level
    pub fn display_name(&self) -> &'static str {
        match self {
            TechDebtLevel::High => "High Debt",
            TechDebtLevel::Medium => "Medium Debt",
            TechDebtLevel::Low => "Low Debt",
        }
    }

    /// Color for the debt level
    pub fn color(&self) -> &'static str {
        match self {
            TechDebtLevel::High => "red",
            TechDebtLevel::Medium => "yellow",
            TechDebtLevel::Low => "green",
        }
    }
}

/// Technical debt analysis result
#[derive(Debug, Clone, Default)]
pub struct TechDebtAnalysis {
    /// Entry list (sorted by score descending)
    pub entries: Vec<TechDebtEntry>,
    /// Average score
    pub avg_score: f64,
    /// Number of high debt files
    pub high_debt_count: usize,
    /// Number of files analyzed
    pub total_files_analyzed: usize,
}

impl TechDebtAnalysis {
    /// Get entry count
    pub fn entry_count(&self) -> usize {
        self.entries.len()
    }
}