137 lines
5.0 KiB
Rust
137 lines
5.0 KiB
Rust
use archlens_domain::{
|
|
CodeElement, CodeGraph, DomainError, RelationshipKind, RenderOutput, RenderedFile,
|
|
ports::DiagramRenderer,
|
|
};
|
|
use archlens_rendering_primitives::non_import_rels;
|
|
|
|
pub struct AsciiRenderer;
|
|
|
|
impl Default for AsciiRenderer {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl AsciiRenderer {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
|
|
fn format_kind(element: &CodeElement) -> &'static str {
|
|
match element.kind() {
|
|
archlens_domain::CodeElementKind::Class => "cls",
|
|
archlens_domain::CodeElementKind::Struct => "str",
|
|
archlens_domain::CodeElementKind::Trait => "trt",
|
|
archlens_domain::CodeElementKind::Interface => "ifc",
|
|
archlens_domain::CodeElementKind::Enum => "enm",
|
|
archlens_domain::CodeElementKind::Project => "prj",
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DiagramRenderer for AsciiRenderer {
|
|
fn render(&self, graph: &CodeGraph) -> Result<RenderOutput, DomainError> {
|
|
let mut lines = Vec::new();
|
|
|
|
let total_elements = graph.elements().len();
|
|
let total_rels = graph.relationships().len();
|
|
let total_modules = graph.modules().len();
|
|
|
|
lines.push("╔══════════════════════════════════════╗".to_string());
|
|
lines.push("║ Architecture Overview ║".to_string());
|
|
lines.push("╠══════════════════════════════════════╣".to_string());
|
|
lines.push(format!(
|
|
"║ Elements: {:<5} Modules: {:<5} ║",
|
|
total_elements, total_modules
|
|
));
|
|
lines.push(format!("║ Relationships: {:<19} ║", total_rels));
|
|
lines.push("╚══════════════════════════════════════╝".to_string());
|
|
|
|
if graph.elements().is_empty() {
|
|
lines.push(" (no elements found)".to_string());
|
|
let content = lines.join("\n");
|
|
let file = RenderedFile::new("diagram.txt", &content)?;
|
|
return Ok(RenderOutput::single(file));
|
|
}
|
|
|
|
let (grouped, ungrouped) = graph.elements_by_module();
|
|
|
|
if !ungrouped.is_empty() {
|
|
lines.push(String::new());
|
|
lines.push("┌─ (ungrouped)".to_string());
|
|
for el in &ungrouped {
|
|
lines.push(format!("│ [{}] {}", Self::format_kind(el), el.name()));
|
|
}
|
|
lines.push("└───".to_string());
|
|
}
|
|
|
|
let mut module_names: Vec<&String> = grouped.keys().collect();
|
|
module_names.sort();
|
|
|
|
for module in module_names {
|
|
let elements = &grouped[module];
|
|
lines.push(String::new());
|
|
lines.push(format!("┌─ {} ({} types)", module, elements.len()));
|
|
lines.push("│".to_string());
|
|
for (i, el) in elements.iter().enumerate() {
|
|
let prefix = if i == elements.len() - 1 {
|
|
"└──"
|
|
} else {
|
|
"├──"
|
|
};
|
|
let generics = if el.generics().is_empty() {
|
|
String::new()
|
|
} else {
|
|
format!("<{}>", el.generics().join(", "))
|
|
};
|
|
lines.push(format!(
|
|
"│ {} [{}] {}{}",
|
|
prefix,
|
|
Self::format_kind(el),
|
|
el.name(),
|
|
generics
|
|
));
|
|
}
|
|
lines.push("└───".to_string());
|
|
}
|
|
|
|
let filtered_rels: Vec<_> = non_import_rels(graph.relationships()).collect();
|
|
|
|
if !filtered_rels.is_empty() {
|
|
lines.push(String::new());
|
|
lines.push("── Relationships ──".to_string());
|
|
for rel in &filtered_rels {
|
|
let arrow = match rel.kind() {
|
|
RelationshipKind::Inheritance => "extends",
|
|
RelationshipKind::Composition => "has",
|
|
RelationshipKind::Import => unreachable!("imports filtered by non_import_rels"),
|
|
};
|
|
lines.push(format!(
|
|
" {} ─[{}]─> {}",
|
|
rel.source(),
|
|
arrow,
|
|
rel.target()
|
|
));
|
|
}
|
|
}
|
|
|
|
let import_rels: Vec<_> = graph
|
|
.relationships()
|
|
.iter()
|
|
.filter(|r| r.kind() == RelationshipKind::Import)
|
|
.collect();
|
|
|
|
if !import_rels.is_empty() {
|
|
lines.push(String::new());
|
|
lines.push(format!("── Imports ({}) ──", import_rels.len()));
|
|
for rel in &import_rels {
|
|
lines.push(format!(" {} ···> {}", rel.source(), rel.target()));
|
|
}
|
|
}
|
|
|
|
let content = lines.join("\n");
|
|
let file = RenderedFile::new("diagram.txt", &content)?;
|
|
Ok(RenderOutput::single(file))
|
|
}
|
|
}
|