use crate::analyzers::rust_call_graph::extract_call_graph;
use crate::organization::cohesion_calculator::calculate_cohesion_score;
use crate::organization::cohesion_priority::assign_cohesion_based_priority;
use crate::organization::cycle_detector::detect_circular_dependencies;
use crate::organization::dependency_analyzer::extract_dependencies;
use crate::organization::god_object::types::ModuleSplit;
use crate::organization::struct_ownership::StructOwnershipAnalyzer;
use std::path::Path;
pub fn enhance_splits_with_cohesion(
splits: Vec<ModuleSplit>,
file_path: &Path,
ast: &syn::File,
ownership: &StructOwnershipAnalyzer,
) -> Vec<ModuleSplit> {
let call_graph = extract_call_graph(ast, file_path);
let all_structs: Vec<String> = ownership
.get_struct_names()
.into_iter()
.map(|s| s.to_string())
.collect();
let mut enhanced_splits: Vec<ModuleSplit> = splits
.into_iter()
.map(|mut split| {
split.cohesion_score = Some(calculate_cohesion_score(&split, &call_graph, ownership));
let (deps_in, deps_out) =
extract_dependencies(&split, &call_graph, ownership, &all_structs);
split.dependencies_in = deps_in;
split.dependencies_out = deps_out;
split
})
.collect();
let cycles = detect_circular_dependencies(&enhanced_splits);
assign_cohesion_based_priority(&mut enhanced_splits, &cycles);
enhanced_splits
}
#[cfg(test)]
mod tests {
use super::*;
use crate::organization::god_object::types::{ModuleSplit, Priority};
fn create_test_code() -> String {
r#"
struct ScoringWeights {
base: f64,
}
impl ScoringWeights {
fn get_default() -> Self {
Self { base: 1.0 }
}
fn apply(&self, value: f64) -> f64 {
value * self.base
}
fn get_multipliers(&self) -> RoleMultipliers {
RoleMultipliers::get()
}
}
struct RoleMultipliers {
admin: f64,
}
impl RoleMultipliers {
fn get() -> Self {
Self { admin: 2.0 }
}
fn apply_to_score(&self, base: f64) -> f64 {
base * self.admin
}
}
struct ValidationLimits {
max: f64,
}
impl ValidationLimits {
fn check(&self, value: f64) -> bool {
value <= self.max
}
}
"#
.to_string()
}
#[test]
fn test_enhance_splits_calculates_cohesion() {
let code = create_test_code();
let parsed = syn::parse_file(&code).expect("Failed to parse");
let ownership = StructOwnershipAnalyzer::analyze_file(&parsed);
let splits = vec![ModuleSplit {
suggested_name: "scoring".to_string(),
methods_to_move: vec![],
structs_to_move: vec!["ScoringWeights".to_string(), "RoleMultipliers".to_string()],
responsibility: "scoring".to_string(),
estimated_lines: 200,
method_count: 10,
warning: None,
priority: Priority::Medium,
cohesion_score: None,
dependencies_in: vec![],
dependencies_out: vec![],
domain: String::new(),
rationale: None,
method: crate::organization::SplitAnalysisMethod::None,
severity: None,
interface_estimate: None,
classification_evidence: None,
representative_methods: vec![],
fields_needed: vec![],
trait_suggestion: None,
behavior_category: None,
..Default::default()
}];
let enhanced =
enhance_splits_with_cohesion(splits, Path::new("test.rs"), &parsed, &ownership);
assert_eq!(enhanced.len(), 1);
assert!(
enhanced[0].cohesion_score.is_some(),
"Cohesion score should be calculated"
);
let cohesion = enhanced[0].cohesion_score.unwrap();
assert!(
(0.0..=1.0).contains(&cohesion),
"Cohesion should be between 0 and 1"
);
}
#[test]
fn test_enhance_splits_detects_dependencies() {
let code = create_test_code();
let parsed = syn::parse_file(&code).expect("Failed to parse");
let ownership = StructOwnershipAnalyzer::analyze_file(&parsed);
let splits = vec![
ModuleSplit {
suggested_name: "scoring".to_string(),
methods_to_move: vec![],
structs_to_move: vec!["ScoringWeights".to_string(), "RoleMultipliers".to_string()],
responsibility: "scoring".to_string(),
estimated_lines: 200,
method_count: 10,
warning: None,
priority: Priority::Medium,
cohesion_score: None,
dependencies_in: vec![],
dependencies_out: vec![],
domain: String::new(),
rationale: None,
method: crate::organization::SplitAnalysisMethod::None,
severity: None,
interface_estimate: None,
classification_evidence: None,
representative_methods: vec![],
fields_needed: vec![],
trait_suggestion: None,
behavior_category: None,
..Default::default()
},
ModuleSplit {
suggested_name: "validation".to_string(),
methods_to_move: vec![],
structs_to_move: vec!["ValidationLimits".to_string()],
responsibility: "validation".to_string(),
estimated_lines: 50,
method_count: 3,
warning: None,
priority: Priority::Medium,
cohesion_score: None,
dependencies_in: vec![],
dependencies_out: vec![],
domain: String::new(),
rationale: None,
method: crate::organization::SplitAnalysisMethod::None,
severity: None,
interface_estimate: None,
classification_evidence: None,
representative_methods: vec![],
fields_needed: vec![],
trait_suggestion: None,
behavior_category: None,
..Default::default()
},
];
let enhanced =
enhance_splits_with_cohesion(splits, Path::new("test.rs"), &parsed, &ownership);
assert_eq!(enhanced.len(), 2);
assert!(enhanced[0].dependencies_in.is_empty() || !enhanced[0].dependencies_in.is_empty());
}
#[test]
fn test_enhance_splits_assigns_priority_based_on_cohesion() {
let code = create_test_code();
let parsed = syn::parse_file(&code).expect("Failed to parse");
let ownership = StructOwnershipAnalyzer::analyze_file(&parsed);
let splits = vec![ModuleSplit {
suggested_name: "scoring".to_string(),
methods_to_move: vec![],
structs_to_move: vec!["ScoringWeights".to_string(), "RoleMultipliers".to_string()],
responsibility: "scoring".to_string(),
estimated_lines: 200,
method_count: 10,
warning: None,
priority: Priority::Medium,
cohesion_score: None,
dependencies_in: vec![],
dependencies_out: vec![],
domain: String::new(),
rationale: None,
method: crate::organization::SplitAnalysisMethod::None,
severity: None,
interface_estimate: None,
classification_evidence: None,
representative_methods: vec![],
fields_needed: vec![],
trait_suggestion: None,
behavior_category: None,
..Default::default()
}];
let enhanced =
enhance_splits_with_cohesion(splits, Path::new("test.rs"), &parsed, &ownership);
assert_eq!(enhanced.len(), 1);
assert!(matches!(
enhanced[0].priority,
Priority::High | Priority::Medium | Priority::Low
));
}
}