mod formatters;
mod io;
mod processors;
mod scoring;
pub mod types;
pub use types::{OutputFormat, ValidateImprovementConfig, ValidationResult};
use anyhow::Result;
use std::collections::HashMap;
use crate::comparison::types::ComparisonResult;
use io::{
load_comparison, load_previous_validation, print_validation_summary, write_validation_result,
};
use processors::{process_project_health, process_regressions, process_target_improvements};
use scoring::{
build_project_summary, build_target_summary, calculate_composite_score,
calculate_trend_analysis, determine_status,
};
use types::ValidationResult as VResult;
pub fn validate_improvement(config: ValidateImprovementConfig) -> Result<()> {
let comparison = load_comparison(&config.comparison_path)?;
let previous = config
.previous_validation
.as_ref()
.map(|path| load_previous_validation(path))
.transpose()?;
let result = validate_improvement_internal(&comparison, previous.as_ref())?;
write_validation_result(&config.output_path, &result, config.format)?;
if !config.quiet {
print_validation_summary(&result);
}
Ok(())
}
fn validate_improvement_internal(
comparison: &ComparisonResult,
previous: Option<&VResult>,
) -> Result<VResult> {
let target_result = process_target_improvements(comparison);
let regression_result = process_regressions(comparison);
let health_result = process_project_health(comparison);
let improvements = merge_improvements(&target_result.improvements, &health_result.improvements);
let remaining_issues = merge_issues(
&target_result.remaining_issues,
®ression_result.remaining_issues,
);
let gaps = merge_gaps(target_result.gaps, regression_result.gaps);
let improvement_score = calculate_composite_score(
target_result.component_score,
health_result.component_score,
regression_result.component_score,
);
let status = determine_status(improvement_score);
let target_summary = build_target_summary(&comparison.target_item);
let project_summary = build_project_summary(comparison);
let trend_analysis = previous.map(|prev| calculate_trend_analysis(prev, improvement_score));
let attempt_number = previous.map(|prev| prev.attempt_number.unwrap_or(1) + 1);
Ok(VResult {
completion_percentage: improvement_score,
status,
improvements,
remaining_issues,
gaps,
target_summary,
project_summary,
trend_analysis,
attempt_number,
})
}
fn merge_improvements(target: &[String], health: &[String]) -> Vec<String> {
target.iter().chain(health.iter()).cloned().collect()
}
fn merge_issues(target: &[String], regression: &[String]) -> Vec<String> {
target.iter().chain(regression.iter()).cloned().collect()
}
fn merge_gaps(
mut target: HashMap<String, types::GapDetail>,
regression: HashMap<String, types::GapDetail>,
) -> HashMap<String, types::GapDetail> {
target.extend(regression);
target
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_merge_improvements() {
let target = vec!["A".to_string()];
let health = vec!["B".to_string(), "C".to_string()];
let merged = merge_improvements(&target, &health);
assert_eq!(merged.len(), 3);
assert_eq!(merged[0], "A");
assert_eq!(merged[1], "B");
assert_eq!(merged[2], "C");
}
#[test]
fn test_merge_gaps() {
use types::GapDetail;
let mut target = HashMap::new();
target.insert(
"target".to_string(),
GapDetail {
description: "Target gap".to_string(),
location: "a.rs".to_string(),
severity: "high".to_string(),
suggested_fix: "Fix".to_string(),
score_before: None,
score_after: None,
current_score: None,
},
);
let mut regression = HashMap::new();
regression.insert(
"regression".to_string(),
GapDetail {
description: "Regression gap".to_string(),
location: "b.rs".to_string(),
severity: "high".to_string(),
suggested_fix: "Fix".to_string(),
score_before: None,
score_after: None,
current_score: None,
},
);
let merged = merge_gaps(target, regression);
assert_eq!(merged.len(), 2);
assert!(merged.contains_key("target"));
assert!(merged.contains_key("regression"));
}
}