use super::filtering::{filter_with_metrics, ClassifiedItem, FilterConfig, FilterResult};
use super::tiers::{classify_tier, TierConfig};
use super::UnifiedDebtItem;
use std::cmp::Ordering;
pub fn sort_by_score(mut items: Vec<ClassifiedItem>) -> Vec<ClassifiedItem> {
items.sort_by(|a, b| {
let order = b.score.partial_cmp(&a.score).unwrap_or(Ordering::Equal);
if order != Ordering::Equal {
return order;
}
compare_debt_items_stably(&a.item, &b.item)
});
items
}
fn compare_debt_items_stably(a: &super::DebtItem, b: &super::DebtItem) -> Ordering {
match (a, b) {
(super::DebtItem::Function(fa), super::DebtItem::Function(fb)) => fa
.location
.file
.cmp(&fb.location.file)
.then_with(|| fa.location.line.cmp(&fb.location.line))
.then_with(|| fa.location.function.cmp(&fb.location.function))
.then_with(|| {
format!("{:?}", fa.debt_type).cmp(&format!("{:?}", fb.debt_type))
}),
(super::DebtItem::File(fa), super::DebtItem::File(fb)) => {
fa.metrics.path.cmp(&fb.metrics.path)
}
(super::DebtItem::Function(_), super::DebtItem::File(_)) => Ordering::Less,
(super::DebtItem::File(_), super::DebtItem::Function(_)) => Ordering::Greater,
}
}
pub fn take_top(items: Vec<ClassifiedItem>, limit: usize) -> Vec<ClassifiedItem> {
items.into_iter().take(limit).collect()
}
pub fn analyze_and_filter(
items: Vec<UnifiedDebtItem>,
tier_config: &TierConfig,
filter_config: &FilterConfig,
limit: usize,
) -> FilterResult {
let classified: Vec<ClassifiedItem> = items
.into_iter()
.map(|item| {
let tier = classify_tier(&item, tier_config);
let score = item.unified_score.final_score;
ClassifiedItem {
item: super::DebtItem::Function(Box::new(item)),
tier,
score,
}
})
.collect();
let sorted = sort_by_score(classified);
let filtered_result = filter_with_metrics(sorted, filter_config);
let limited: Vec<super::DebtItem> = filtered_result.included.into_iter().take(limit).collect();
FilterResult {
included: limited,
metrics: filtered_result.metrics,
}
}
pub fn filter_sort_limit(
classified: Vec<ClassifiedItem>,
filter_config: &FilterConfig,
limit: usize,
) -> FilterResult {
let sorted = sort_by_score(classified);
let filtered_result = filter_with_metrics(sorted, filter_config);
let limited: Vec<super::DebtItem> = filtered_result.included.into_iter().take(limit).collect();
FilterResult {
included: limited,
metrics: filtered_result.metrics,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::priority::filtering::ClassifiedItem;
use crate::priority::tiers::RecommendationTier;
use crate::priority::{
ActionableRecommendation, DebtItem, DebtType, FunctionRole, ImpactMetrics, Location,
UnifiedDebtItem, UnifiedScore,
};
fn create_classified_item(score: f64, tier: RecommendationTier) -> ClassifiedItem {
let item = UnifiedDebtItem {
location: Location {
file: "test.rs".into(),
function: "test_fn".into(),
line: 1,
},
debt_type: DebtType::ComplexityHotspot {
cyclomatic: 10,
cognitive: 10,
},
unified_score: UnifiedScore {
complexity_factor: 1.0,
coverage_factor: 1.0,
dependency_factor: 1.0,
role_multiplier: 1.0,
final_score: score.max(0.0),
base_score: Some(score),
exponential_factor: Some(1.0),
risk_boost: Some(1.0),
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: FunctionRole::PureLogic,
recommendation: ActionableRecommendation {
primary_action: "Test".into(),
rationale: "Test".into(),
implementation_steps: vec![],
related_items: vec![],
steps: None,
estimated_effort_hours: None,
},
expected_impact: ImpactMetrics {
coverage_improvement: 0.0,
lines_reduction: 0,
complexity_reduction: 0.0,
risk_reduction: 0.0,
},
transitive_coverage: 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: 1,
function_length: 10,
cyclomatic_complexity: 10,
cognitive_complexity: 10,
is_pure: None,
purity_confidence: None,
purity_level: None,
god_object_indicators: None,
tier: None,
function_context: None,
context_confidence: None,
contextual_recommendation: None,
pattern_analysis: None,
file_context: 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,
};
ClassifiedItem {
item: DebtItem::Function(Box::new(item)),
tier,
score,
}
}
#[test]
fn test_sort_by_score_descending() {
let items = vec![
create_classified_item(50.0, RecommendationTier::T2ComplexUntested),
create_classified_item(95.0, RecommendationTier::T1CriticalArchitecture),
create_classified_item(70.0, RecommendationTier::T2ComplexUntested),
];
let sorted = sort_by_score(items);
assert_eq!(sorted[0].score, 95.0);
assert_eq!(sorted[1].score, 70.0);
assert_eq!(sorted[2].score, 50.0);
}
#[test]
fn test_sort_is_stable_for_equal_scores() {
let items = vec![
create_classified_item(50.0, RecommendationTier::T1CriticalArchitecture),
create_classified_item(50.0, RecommendationTier::T2ComplexUntested),
create_classified_item(50.0, RecommendationTier::T3TestingGaps),
];
let sorted = sort_by_score(items);
for item in sorted {
assert_eq!(item.score, 50.0);
}
}
#[test]
fn test_take_top_limits_correctly() {
let items = vec![
create_classified_item(95.0, RecommendationTier::T1CriticalArchitecture),
create_classified_item(85.0, RecommendationTier::T2ComplexUntested),
create_classified_item(75.0, RecommendationTier::T2ComplexUntested),
create_classified_item(65.0, RecommendationTier::T3TestingGaps),
];
let top = take_top(items, 2);
assert_eq!(top.len(), 2);
assert_eq!(top[0].score, 95.0);
assert_eq!(top[1].score, 85.0);
}
#[test]
fn test_take_top_handles_limit_larger_than_items() {
let items = vec![
create_classified_item(95.0, RecommendationTier::T1CriticalArchitecture),
create_classified_item(85.0, RecommendationTier::T2ComplexUntested),
];
let top = take_top(items, 10);
assert_eq!(top.len(), 2); }
#[test]
fn test_take_top_handles_empty_input() {
let items: Vec<ClassifiedItem> = vec![];
let top = take_top(items, 10);
assert_eq!(top.len(), 0);
}
#[test]
fn test_take_top_handles_zero_limit() {
let items = vec![
create_classified_item(95.0, RecommendationTier::T1CriticalArchitecture),
create_classified_item(85.0, RecommendationTier::T2ComplexUntested),
];
let top = take_top(items, 0);
assert_eq!(top.len(), 0);
}
#[test]
fn test_sort_maintains_item_data() {
let items = vec![
create_classified_item(50.0, RecommendationTier::T2ComplexUntested),
create_classified_item(95.0, RecommendationTier::T1CriticalArchitecture),
];
let sorted = sort_by_score(items);
assert_eq!(sorted[0].tier, RecommendationTier::T1CriticalArchitecture);
assert_eq!(sorted[1].tier, RecommendationTier::T2ComplexUntested);
}
}