use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::error::AnalysisError;
use crate::input::{validate_node_id, DependencyGraphInput};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CouplingTopologyResult {
pub node_count: usize,
pub edge_count: usize,
pub density: f64,
pub cycle_count: usize,
pub top_hubs: Vec<(String, usize)>,
pub component_count: usize,
}
pub fn compute_coupling_topology(
graph: &DependencyGraphInput,
) -> Result<CouplingTopologyResult, AnalysisError> {
for node in &graph.nodes {
validate_node_id(&node.id)?;
}
let node_paths: Vec<String> = graph.nodes.iter().map(|n| n.id.clone()).collect();
let id_to_idx: BTreeMap<&str, usize> = graph
.nodes
.iter()
.enumerate()
.map(|(i, n)| (n.id.as_str(), i))
.collect();
let raw_edges: Vec<(usize, usize)> = graph
.edges
.iter()
.filter_map(|e| {
let from = *id_to_idx.get(e.source.as_str())?;
let to = *id_to_idx.get(e.target.as_str())?;
if from != to {
Some((from, to))
} else {
None
}
})
.collect();
let dg =
sdivi_graph::dependency_graph::build_dependency_graph_from_edges(&node_paths, &raw_edges);
let metrics = sdivi_graph::metrics::compute_metrics(&dg);
let top_hubs: Vec<(String, usize)> = metrics
.top_hubs
.into_iter()
.map(|(path, deg)| (path.to_string_lossy().into_owned(), deg))
.collect();
Ok(CouplingTopologyResult {
node_count: metrics.node_count,
edge_count: metrics.edge_count,
density: metrics.density,
cycle_count: metrics.cycle_count,
top_hubs,
component_count: metrics.component_count,
})
}