use crate::coupling::dependency::BlastRadiusEntry;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum OutputFormat {
Cli,
Json,
Html,
}
#[derive(Debug, Clone)]
pub struct CouplingConfig {
pub root_dir: PathBuf,
pub coupling_window: Duration,
pub analysis_window: Duration,
pub min_score_threshold: f64,
pub output_format: OutputFormat,
}
impl Default for CouplingConfig {
fn default() -> Self {
Self {
root_dir: PathBuf::new(),
coupling_window: Duration::from_secs(24 * 60 * 60),
analysis_window: Duration::from_secs(180 * 24 * 60 * 60),
min_score_threshold: 30.0,
output_format: OutputFormat::Cli,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RepoInfo {
pub name: String,
pub path: PathBuf,
pub commit_count: usize,
pub author_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalDetails {
pub co_commit_count: usize,
pub total_windows: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TeamDetails {
pub shared_authors: usize,
pub total_authors: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyDetails {
pub shared_dependencies: usize,
pub relationship: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CouplingDetails {
pub temporal: TemporalDetails,
pub team: TeamDetails,
pub dependency: DependencyDetails,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CouplingPair {
pub repo_a: String,
pub repo_b: String,
pub temporal_score: f64,
pub team_score: f64,
pub dependency_score: f64,
pub combined_score: f64,
pub details: CouplingDetails,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CouplingReportSummary {
pub total_repos: usize,
pub total_pairs_analyzed: usize,
pub pairs_above_threshold: usize,
pub highest_coupling_score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CouplingReport {
pub repos: Vec<RepoInfo>,
pub pairs: Vec<CouplingPair>,
pub summary: CouplingReportSummary,
pub blast_radius: Vec<BlastRadiusEntry>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_coupling_config_has_24h_coupling_window() {
let config = CouplingConfig::default();
assert_eq!(config.coupling_window, Duration::from_secs(24 * 60 * 60));
}
#[test]
fn default_coupling_config_has_6_month_analysis_window() {
let config = CouplingConfig::default();
assert_eq!(
config.analysis_window,
Duration::from_secs(180 * 24 * 60 * 60)
);
}
#[test]
fn default_coupling_config_has_30_min_score_threshold() {
let config = CouplingConfig::default();
assert!((config.min_score_threshold - 30.0).abs() < f64::EPSILON);
}
#[test]
fn default_coupling_config_has_cli_output_format() {
let config = CouplingConfig::default();
assert_eq!(config.output_format, OutputFormat::Cli);
}
#[test]
fn default_coupling_config_has_empty_root_dir() {
let config = CouplingConfig::default();
assert_eq!(config.root_dir, PathBuf::new());
}
}