use super::{complexity, context, coverage, git_history, sections};
use crate::formatting::{ColoredFormatter, FormattingConfig};
use crate::priority::classification::Severity;
use crate::priority::{score_formatter, UnifiedDebtItem};
use colored::*;
use std::fmt::Write;
fn format_score_header(
rank: usize,
score: f64,
coverage_indicator: &str,
severity: &str,
severity_color: Color,
) -> String {
format!(
"#{} {}{} [{}]",
rank.to_string().bright_cyan().bold(),
format!("SCORE: {}", score_formatter::format_score(score)).bright_yellow(),
coverage_indicator.bright_red().bold(),
severity.color(severity_color).bold()
)
}
fn format_main_factors(factors: &[String], _formatter: &ColoredFormatter) -> String {
if factors.is_empty() {
String::new()
} else {
format!(
" {} Main factors: {}",
" ",
factors.join(", ").bright_white()
)
}
}
fn collect_scoring_factors(
item: &UnifiedDebtItem,
weights: &crate::config::ScoringWeights,
has_coverage_data: bool,
) -> Vec<String> {
let mut factors = vec![];
if let Some(desc) =
coverage::format_coverage_factor_description(item, weights, has_coverage_data)
{
factors.push(desc);
}
if item.unified_score.complexity_factor > 5.0 {
factors.push("Complexity (weight: 35%)".to_string());
} else if item.unified_score.complexity_factor > 3.0 {
factors.push("Moderate complexity".to_string());
}
if item.unified_score.dependency_factor > 5.0 {
factors.push("Critical path (weight: 15%)".to_string());
}
match &item.debt_type {
crate::priority::DebtType::NestedLoops { depth, .. } => {
factors.push("Performance impact (High)".to_string());
factors.push(format!("{} level nested loops", depth));
}
crate::priority::DebtType::BlockingIO { operation, .. } => {
factors.push("Performance impact (High)".to_string());
factors.push(format!("Blocking {}", operation));
}
crate::priority::DebtType::AllocationInefficiency { pattern, .. } => {
factors.push("Performance impact (Medium)".to_string());
factors.push(format!("Allocation: {}", pattern));
}
_ => {}
}
factors
}
fn get_coverage_indicator(_item: &UnifiedDebtItem, _has_coverage_data: bool) -> &'static str {
"" }
fn format_location_section(output: &mut String, item: &UnifiedDebtItem) {
writeln!(
output,
"├─ {} {}:{} {}()",
"LOCATION:".bright_blue(),
item.location.file.display(),
item.location.line,
item.location.function.bright_green()
)
.unwrap();
}
fn format_impact_section(output: &mut String, item: &UnifiedDebtItem) {
writeln!(
output,
"├─ {} {}",
"IMPACT:".bright_blue(),
crate::priority::formatter::format_impact(&item.expected_impact).bright_cyan()
)
.unwrap();
}
fn format_why_section(output: &mut String, item: &UnifiedDebtItem) {
writeln!(
output,
"{} {}",
"├─ WHY THIS MATTERS:".bright_blue(),
item.recommendation.rationale
)
.unwrap();
}
fn format_action_section(output: &mut String, item: &UnifiedDebtItem) {
writeln!(
output,
"├─ {} {}",
"RECOMMENDED ACTION:".bright_blue(),
item.recommendation.primary_action.bright_green().bold()
)
.unwrap();
}
fn format_item_body(
output: &mut String,
item: &UnifiedDebtItem,
formatter: &ColoredFormatter,
verbosity: u8,
tree_pipe: &str,
has_coverage_data: bool,
) {
format_location_section(output, item);
context::format_context_dampening_section(output, item);
format_impact_section(output, item);
complexity::format_complexity_summary(output, item, formatter);
complexity::format_pattern_detection(output, item);
coverage::format_coverage_section(
output,
item,
formatter,
verbosity,
tree_pipe,
has_coverage_data,
);
git_history::format_git_history_section(output, item);
git_history::format_context_provider_contributions(output, item, verbosity);
context::format_file_context_section(output, item, verbosity);
format_why_section(output, item);
format_action_section(output, item);
sections::format_implementation_steps(
output,
&item.recommendation.implementation_steps,
formatter,
);
sections::format_dependencies_summary(output, item, formatter, tree_pipe);
sections::format_basic_call_graph(output, item, formatter);
if verbosity == 1 {
sections::format_scoring_breakdown(output, item, formatter);
}
sections::format_related_items(output, &item.recommendation.related_items, formatter);
sections::format_pattern_analysis(output, item, verbosity);
}
pub fn format_priority_item_with_config(
output: &mut String,
rank: usize,
item: &UnifiedDebtItem,
verbosity: u8,
config: FormattingConfig,
has_coverage_data: bool,
) {
let formatter = ColoredFormatter::new(config);
let sev = Severity::from_score_100(item.unified_score.final_score);
let severity = sev.as_str();
let severity_color = sev.color();
let tree_pipe = " ";
let coverage_indicator = get_coverage_indicator(item, has_coverage_data);
let score_header = format_score_header(
rank,
item.unified_score.final_score,
coverage_indicator,
severity,
severity_color,
);
writeln!(output, "{}", score_header).unwrap();
if verbosity >= 1 {
let weights = crate::config::get_scoring_weights();
let factors = collect_scoring_factors(item, weights, has_coverage_data);
let factors_line = format_main_factors(&factors, &formatter);
if !factors_line.is_empty() {
writeln!(output, "{}", factors_line).unwrap();
}
}
if verbosity >= 2 {
let score_calc_lines = sections::format_score_calculation_section(item, &formatter);
for line in score_calc_lines {
writeln!(output, "{}", line).unwrap();
}
let complexity_lines = complexity::format_complexity_details_section(item, &formatter);
for line in complexity_lines {
writeln!(output, "{}", line).unwrap();
}
let call_graph_lines = sections::format_call_graph_section(item, &formatter);
for line in call_graph_lines {
writeln!(output, "{}", line).unwrap();
}
}
format_item_body(
output,
item,
&formatter,
verbosity,
tree_pipe,
has_coverage_data,
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::priority::{DebtType, UnifiedDebtItem, UnifiedScore};
use std::path::PathBuf;
#[test]
fn test_format_score_header() {
use colored::Color;
colored::control::set_override(false);
let header = format_score_header(1, 85.5, " [WARN]", "HIGH", Color::Red);
assert!(
header.contains("#"),
"Header should contain #, got: {}",
header
);
assert!(
header.contains("1"),
"Header should contain rank 1, got: {}",
header
);
assert!(
header.contains("SCORE:"),
"Header should contain SCORE:, got: {}",
header
);
assert!(
header.contains("85.5"),
"Header should contain score 85.5, got: {}",
header
);
assert!(
header.contains("HIGH"),
"Header should contain HIGH, got: {}",
header
);
colored::control::unset_override();
}
#[test]
fn test_format_main_factors() {
let formatter = ColoredFormatter::new(FormattingConfig::plain());
let factors: Vec<String> = vec![];
let result = format_main_factors(&factors, &formatter);
assert_eq!(result, "");
let factors = vec!["Factor1".to_string(), "Factor2".to_string()];
let result = format_main_factors(&factors, &formatter);
assert!(result.contains("Main factors:"));
assert!(result.contains("Factor1, Factor2"));
}
#[test]
fn test_collect_scoring_factors() {
let weights = crate::config::ScoringWeights {
complexity: 0.3,
coverage: 0.5,
dependency: 0.2,
semantic: 0.0,
security: 0.0,
organization: 0.0,
};
let mut item = create_test_item();
item.unified_score.coverage_factor = 10.0;
item.unified_score.complexity_factor = 3.5;
item.unified_score.dependency_factor = 1.0;
let factors = collect_scoring_factors(&item, &weights, true); assert!(factors.iter().any(|f| f.contains("UNTESTED")));
assert!(factors.iter().any(|f| f.contains("Moderate complexity")));
item.debt_type = DebtType::NestedLoops {
depth: 3,
complexity_estimate: "O(n^3)".to_string(),
};
let factors = collect_scoring_factors(&item, &weights, true);
assert!(factors
.iter()
.any(|f| f.contains("Performance impact (High)")));
assert!(factors.iter().any(|f| f.contains("3 level nested loops")));
}
fn create_test_item() -> UnifiedDebtItem {
UnifiedDebtItem {
location: crate::priority::Location {
file: PathBuf::from("test.rs"),
function: "test_function".to_string(),
line: 100,
},
debt_type: DebtType::ComplexityHotspot {
cyclomatic: 10,
cognitive: 20,
},
unified_score: UnifiedScore {
complexity_factor: 5.0,
coverage_factor: 5.0,
dependency_factor: 2.0,
role_multiplier: 1.0,
final_score: 10.0,
base_score: None,
exponential_factor: None,
risk_boost: None,
pre_adjustment_score: None,
adjustment_applied: None,
purity_factor: None,
refactorability_factor: None,
pattern_factor: None,
debt_adjustment: None,
pre_normalization_score: None,
structural_multiplier: Some(1.0),
has_coverage_data: false,
contextual_risk_multiplier: None,
pre_contextual_score: None,
debt_type_multiplier: None,
},
function_role: crate::priority::FunctionRole::Unknown,
recommendation: crate::priority::ActionableRecommendation {
primary_action: "Test action".to_string(),
rationale: "Test rationale".to_string(),
implementation_steps: vec![],
related_items: vec![],
steps: None,
estimated_effort_hours: None,
},
expected_impact: crate::priority::ImpactMetrics {
coverage_improvement: 0.5,
lines_reduction: 10,
complexity_reduction: 5.0,
risk_reduction: 2.0,
},
transitive_coverage: None,
file_context: None,
upstream_dependencies: 0,
downstream_dependencies: 0,
upstream_callers: vec![],
downstream_callees: vec![],
upstream_production_callers: vec![],
upstream_test_callers: vec![],
production_blast_radius: 0,
nesting_depth: 2,
function_length: 50,
cyclomatic_complexity: 10,
cognitive_complexity: 20,
is_pure: Some(false),
purity_confidence: Some(0.5),
purity_level: None,
god_object_indicators: None,
tier: None,
function_context: None,
context_confidence: None,
contextual_recommendation: None,
pattern_analysis: None,
context_multiplier: None,
context_type: None,
language_specific: None,
detected_pattern: None,
contextual_risk: None, file_line_count: None,
responsibility_category: None,
error_swallowing_count: None,
error_swallowing_patterns: None,
entropy_analysis: None,
context_suggestion: None,
}
}
}