fn generate_styled_architecture_diagram(
components: &[Component],
edges: &[ComponentEdge],
metrics: &FxHashMap<String, ComponentMetrics>,
) -> Result<String> {
let mut diagram = String::from("graph TD\n");
render_component_nodes(&mut diagram, components, metrics);
render_component_edges(&mut diagram, edges);
diagram.push('\n');
diagram.push_str(" classDef high-complexity fill:#ff9999,stroke:#ff0000,stroke-width:3px\n");
diagram
.push_str(" classDef medium-complexity fill:#ffcc99,stroke:#ff8800,stroke-width:2px\n");
diagram.push_str(" classDef low-complexity fill:#99ff99,stroke:#00aa00,stroke-width:1px\n");
diagram
.push_str(" classDef unknown-complexity fill:#cccccc,stroke:#888888,stroke-width:1px\n");
Ok(diagram)
}
fn render_component_nodes(
diagram: &mut String,
components: &[Component],
metrics: &FxHashMap<String, ComponentMetrics>,
) {
for component in components {
let complexity_class = if let Some(m) = metrics.get(&component.id) {
match m.avg_complexity {
x if x > 15.0 => "high-complexity",
x if x > 10.0 => "medium-complexity",
_ => "low-complexity",
}
} else {
"unknown-complexity"
};
diagram.push_str(&format!(" {}[\"{}\"]\n", component.id, component.label));
diagram.push_str(&format!(
" class {} {}\n",
component.id, complexity_class
));
}
}
fn render_component_edges(diagram: &mut String, edges: &[ComponentEdge]) {
for edge in edges {
let edge_class = match edge.weight {
w if w > 10 => "strong-coupling",
w if w > 5 => "medium-coupling",
_ => "weak-coupling",
};
let arrow_style = match edge.edge_type {
ComponentEdgeType::Import => "-->",
ComponentEdgeType::Call => "-.->",
ComponentEdgeType::Inheritance => "==>>",
ComponentEdgeType::Composition => "--o",
};
diagram.push_str(&format!(" {} {} {}\n", edge.from, arrow_style, edge.to));
diagram.push_str(&format!(
" linkStyle {} stroke:{}\n",
edges.len(),
match edge_class {
"strong-coupling" => "#ff0000",
"medium-coupling" => "#ff8800",
_ => "#888888",
}
));
}
}
fn sanitize_component_id(name: &str) -> String {
name.chars()
.map(|c| if c.is_alphanumeric() { c } else { '_' })
.collect::<String>()
.trim_matches('_')
.to_string()
}
fn humanize_component_name(name: &str) -> String {
name.split('_')
.map(|part| {
let mut chars = part.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
}
})
.collect::<Vec<String>>()
.join(" ")
}
fn collect_component_nodes(
dag: &crate::models::dag::DependencyGraph,
module_name: &str,
) -> Vec<String> {
dag.nodes
.iter()
.filter(|(node_id, _)| node_id.starts_with(module_name))
.map(|(node_id, _)| node_id.clone())
.collect()
}
fn merge_coupled_components(
_components: &mut [Component],
_dag: &crate::models::dag::DependencyGraph,
) {
}
fn calculate_graph_diameter(_components: &[Component], _edges: &[ComponentEdge]) -> usize {
5
}
impl Clone for ComponentEdgeType {
fn clone(&self) -> Self {
match self {
ComponentEdgeType::Import => ComponentEdgeType::Import,
ComponentEdgeType::Call => ComponentEdgeType::Call,
ComponentEdgeType::Inheritance => ComponentEdgeType::Inheritance,
ComponentEdgeType::Composition => ComponentEdgeType::Composition,
}
}
}
impl std::hash::Hash for ComponentEdgeType {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
}
}
impl PartialEq for ComponentEdgeType {
fn eq(&self, other: &Self) -> bool {
std::mem::discriminant(self) == std::mem::discriminant(other)
}
}
impl Eq for ComponentEdgeType {}