#[derive(Debug, Clone)]
pub struct HexGraph {
pub(crate) inner: std::sync::Arc<GraphInner>,
}
#[derive(Debug)]
pub(crate) struct GraphInner {
pub(crate) nodes:
std::collections::HashMap<crate::graph::node_id::NodeId, crate::graph::hex_node::HexNode>,
pub(crate) edges: Vec<crate::graph::hex_edge::HexEdge>,
pub(crate) metadata: crate::graph::metadata::GraphMetadata,
}
impl HexGraph {
pub fn current() -> std::sync::Arc<Self> {
std::sync::Arc::new(crate::registry::component_registry::ComponentRegistry::build_graph())
}
pub fn new() -> Self {
Self {
inner: std::sync::Arc::new(GraphInner {
nodes: std::collections::HashMap::new(),
edges: Vec::new(),
metadata: crate::graph::metadata::GraphMetadata::default(),
}),
}
}
#[cfg(feature = "visualization")]
pub fn to_dot(&self) -> crate::result::hex_result::HexResult<String> {
let exporter = crate::graph::visualization::adapters::dot_exporter::DotExporter::new();
let use_case =
crate::graph::visualization::application::export_graph::ExportGraph::new(&exporter);
use_case.execute(
self,
crate::graph::visualization::domain::visual_style::VisualStyle::default(),
)
}
#[cfg(feature = "visualization")]
pub fn to_mermaid(&self) -> crate::result::hex_result::HexResult<String> {
let exporter = crate::graph::visualization::adapters::mermaid_exporter::MermaidExporter::new();
let use_case =
crate::graph::visualization::application::export_graph::ExportGraph::new(&exporter);
use_case.execute(
self,
crate::graph::visualization::domain::visual_style::VisualStyle::default(),
)
}
#[cfg(feature = "visualization")]
pub fn to_json(&self) -> crate::result::hex_result::HexResult<String> {
let exporter = crate::graph::visualization::adapters::json_exporter::JsonExporter::new();
let use_case =
crate::graph::visualization::application::export_graph::ExportGraph::new(&exporter);
use_case.execute(
self,
crate::graph::visualization::domain::visual_style::VisualStyle::default(),
)
}
#[cfg(feature = "visualization")]
pub fn save_visualization(
&self,
path: &std::path::Path,
exporter: &dyn crate::graph::visualization::ports::format_exporter::FormatExporter,
) -> crate::result::hex_result::HexResult<()> {
let use_case =
crate::graph::visualization::application::export_graph::ExportGraph::new(exporter);
let content = use_case.execute(
self,
crate::graph::visualization::domain::visual_style::VisualStyle::default(),
)?;
std::fs::write(path, content).map_err(|e| {
crate::error::hex_error::Hexserror::adapter(
crate::error::codes::io::IO_FAILURE,
&format!("Failed to write file: {}", e),
)
.with_next_step("Check file path and permissions")
.with_suggestion("Verify directory exists and is writable")
})
}
pub fn builder() -> crate::graph::builder::GraphBuilder {
crate::graph::builder::GraphBuilder::new()
}
pub fn layer_count(&self) -> usize {
let mut layers = std::collections::HashSet::new();
for node in self.nodes() {
layers.insert(node.layer());
}
layers.len()
}
pub fn node_count(&self) -> usize {
self.inner.nodes.len()
}
pub fn edge_count(&self) -> usize {
self.inner.edges.len()
}
#[cfg(feature = "ai")]
pub fn to_ai_context(&self) -> crate::result::hex_result::HexResult<crate::ai::AIContext> {
crate::ai::ContextBuilder::new(self).build()
}
pub fn get_node(
&self,
id: &crate::graph::node_id::NodeId,
) -> Option<&crate::graph::hex_node::HexNode> {
self.inner.nodes.get(id)
}
pub fn pretty_print(&self) {
println!("Hexagonal Architecture Graph:");
println!(" Nodes: {}", self.node_count());
println!(" Edges: {}", self.edge_count());
println!("\nBy Layer:");
for layer in [
crate::graph::layer::Layer::Domain,
crate::graph::layer::Layer::Port,
crate::graph::layer::Layer::Adapter,
crate::graph::layer::Layer::Application,
crate::graph::layer::Layer::Infrastructure,
] {
let count = self.nodes_by_layer(layer).len();
if count > 0 {
println!(" {:?}: {}", layer, count);
}
}
}
pub fn nodes(&self) -> impl Iterator<Item = &crate::graph::hex_node::HexNode> {
self.inner.nodes.values()
}
pub fn edges(&self) -> &[crate::graph::hex_edge::HexEdge] {
&self.inner.edges
}
pub fn nodes_by_layer(
&self,
layer: crate::graph::layer::Layer,
) -> Vec<&crate::graph::hex_node::HexNode> {
self
.inner
.nodes
.values()
.filter(|n| n.layer() == layer)
.collect()
}
pub fn nodes_by_role(
&self,
role: crate::graph::role::Role,
) -> Vec<&crate::graph::hex_node::HexNode> {
self
.inner
.nodes
.values()
.filter(|n| n.role() == role)
.collect()
}
pub fn edges_from(
&self,
source: &crate::graph::node_id::NodeId,
) -> Vec<&crate::graph::hex_edge::HexEdge> {
self
.inner
.edges
.iter()
.filter(|e| e.source() == source)
.collect()
}
pub fn edges_to(
&self,
target: &crate::graph::node_id::NodeId,
) -> Vec<&crate::graph::hex_edge::HexEdge> {
self
.inner
.edges
.iter()
.filter(|e| e.target() == target)
.collect()
}
pub fn metadata(&self) -> &crate::graph::metadata::GraphMetadata {
&self.inner.metadata
}
pub fn is_empty(&self) -> bool {
self.inner.nodes.is_empty()
}
}
impl Default for HexGraph {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_graph() {
let graph = HexGraph::new();
assert_eq!(graph.node_count(), 0);
assert_eq!(graph.edge_count(), 0);
assert!(graph.is_empty());
}
#[test]
fn test_graph_thread_safety() {
let graph = HexGraph::new();
let graph_clone = graph.clone();
std::thread::spawn(move || {
assert_eq!(graph_clone.node_count(), 0);
})
.join()
.unwrap();
}
#[test]
fn test_graph_default() {
let graph = HexGraph::default();
assert!(graph.is_empty());
}
}