pub mod graph_config;
pub mod graph_structures;
pub mod graph_generator;
pub mod graph_export;
pub mod graph_3d;
pub mod graph_analysis;
pub mod layout_algorithms;
pub use graph_config::*;
pub use graph_structures::*;
pub use graph_generator::*;
pub use graph_export::*;
pub use graph_3d::*;
pub use graph_analysis::*;
pub use layout_algorithms::*;
pub struct GraphVisualizationFramework {
config: GraphConfig,
generator: TraitGraphGenerator,
exporter: GraphExporter,
analyzer: GraphAnalyzer,
generator_3d: Option<Graph3DGenerator>,
}
impl GraphVisualizationFramework {
pub fn new(config: GraphConfig) -> Result<Self, Box<dyn std::error::Error>> {
let generator = TraitGraphGenerator::new(config.clone())?;
let exporter = GraphExporter::new(config.clone());
let analyzer = GraphAnalyzer::new();
let generator_3d = if config.enable_3d {
Some(Graph3DGenerator::new(ThreeDConfig::from_graph_config(&config)))
} else {
None
};
Ok(Self {
config,
generator,
exporter,
analyzer,
generator_3d,
})
}
pub fn with_defaults() -> Result<Self, Box<dyn std::error::Error>> {
Self::new(GraphConfig::default())
}
pub fn with_performance_config() -> Result<Self, Box<dyn std::error::Error>> {
let config = GraphConfig::new()
.with_layout_algorithm(LayoutAlgorithm::ForceDirected)
.with_optimization_level(OptimizationLevel::Aggressive)
.with_simd_acceleration(true)
.with_gpu_acceleration(true)
.with_parallel_processing(true);
Self::new(config)
}
pub fn with_quality_config() -> Result<Self, Box<dyn std::error::Error>> {
let config = GraphConfig::new()
.with_layout_algorithm(LayoutAlgorithm::ForceDirected)
.with_3d_visualization(true)
.with_advanced_analysis(true)
.with_optimization_level(OptimizationLevel::Quality)
.with_theme(VisualizationTheme::Professional);
Self::new(config)
}
pub fn generate_complete_graph(
&self,
trait_info: &TraitInfo,
implementations: &[String],
) -> Result<TraitGraph, Box<dyn std::error::Error>> {
let mut graph = self.generator.generate_trait_graph(trait_info, implementations)?;
let layout = LayoutAlgorithmFactory::create_layout(self.config.layout_algorithm);
let layout_result = layout.compute_layout(&graph, &self.config)?;
for node in &mut graph.nodes {
if let Some(&position) = layout_result.positions_2d.get(&node.id) {
node.position_2d = Some(position);
}
if let Some(ref positions_3d) = layout_result.positions_3d {
if let Some(&position) = positions_3d.get(&node.id) {
node.position_3d = Some(position);
}
}
}
if self.config.enable_analysis {
let analysis_result = self.analyzer.analyze_graph(&graph)?;
let mut metadata = graph.metadata;
metadata.analysis_results = Some(analysis_result);
metadata.layout_quality = Some(layout_result.quality_metrics);
graph.metadata = metadata;
}
Ok(graph)
}
pub fn generate_ecosystem_graph(
&self,
traits: &[&TraitInfo],
) -> Result<TraitGraph, Box<dyn std::error::Error>> {
let mut graph = self.generator.generate_full_graph(traits)?;
let layout = LayoutAlgorithmFactory::create_layout(self.config.layout_algorithm);
let layout_result = layout.compute_layout(&graph, &self.config)?;
for node in &mut graph.nodes {
if let Some(&position) = layout_result.positions_2d.get(&node.id) {
node.position_2d = Some(position);
}
if let Some(ref positions_3d) = layout_result.positions_3d {
if let Some(&position) = positions_3d.get(&node.id) {
node.position_3d = Some(position);
}
}
}
if self.config.enable_analysis {
let analysis_result = self.analyzer.analyze_graph(&graph)?;
let communities = self.analyzer.detect_communities(&graph, CommunityDetection::Louvain)?;
let critical_paths = self.analyzer.find_critical_paths_comprehensive(&graph)?;
let mut metadata = graph.metadata;
metadata.analysis_results = Some(analysis_result);
metadata.layout_quality = Some(layout_result.quality_metrics);
metadata.communities = Some(communities);
metadata.critical_paths = Some(critical_paths);
graph.metadata = metadata;
}
Ok(graph)
}
pub fn export_all_formats(
&self,
graph: &TraitGraph,
base_path: &str,
) -> Result<ExportResults, Box<dyn std::error::Error>> {
let mut results = ExportResults::default();
results.svg = Some(self.exporter.export_graph(graph, GraphExportFormat::Svg)?);
results.json = Some(self.exporter.export_graph(graph, GraphExportFormat::Json)?);
results.dot = Some(self.exporter.export_graph(graph, GraphExportFormat::Dot)?);
results.interactive_html = Some(self.exporter.export_graph(graph, GraphExportFormat::InteractiveHtml)?);
results.graphml = Some(self.exporter.export_graph(graph, GraphExportFormat::GraphML)?);
results.gexf = Some(self.exporter.export_graph(graph, GraphExportFormat::GEXF)?);
if self.config.enable_3d {
if let Some(ref generator_3d) = self.generator_3d {
results.three_d_scene = Some(generator_3d.generate_3d_scene(graph)?);
}
}
if !base_path.is_empty() {
if let Some(ref svg) = results.svg {
std::fs::write(format!("{}.svg", base_path), svg)?;
}
if let Some(ref json) = results.json {
std::fs::write(format!("{}.json", base_path), json)?;
}
if let Some(ref dot) = results.dot {
std::fs::write(format!("{}.dot", base_path), dot)?;
}
if let Some(ref html) = results.interactive_html {
std::fs::write(format!("{}.html", base_path), html)?;
}
if let Some(ref graphml) = results.graphml {
std::fs::write(format!("{}.graphml", base_path), graphml)?;
}
if let Some(ref gexf) = results.gexf {
std::fs::write(format!("{}.gexf", base_path), gexf)?;
}
if let Some(ref scene_3d) = results.three_d_scene {
std::fs::write(format!("{}_3d.html", base_path), scene_3d)?;
}
}
Ok(results)
}
pub fn analyze_comprehensive(
&self,
graph: &TraitGraph,
) -> Result<ComprehensiveAnalysisResults, Box<dyn std::error::Error>> {
let mut results = ComprehensiveAnalysisResults::default();
results.basic_analysis = self.analyzer.analyze_graph(graph)?;
results.louvain_communities = self.analyzer.detect_communities(graph, CommunityDetection::Louvain)?;
results.critical_paths = self.analyzer.find_critical_paths_comprehensive(graph)?;
results.hub_nodes = self.analyzer.identify_hub_nodes(graph, 0.1)?;
results.bridge_nodes = self.analyzer.identify_bridge_nodes(graph)?;
results.bottleneck_edges = self.analyzer.identify_bottleneck_edges(graph)?;
if !results.louvain_communities.is_empty() {
results.modularity = Some(self.analyzer.calculate_modularity(graph, &results.louvain_communities)?);
}
results.small_world_coefficient = Some(self.analyzer.calculate_small_world_coefficient(graph)?);
Ok(results)
}
pub fn config(&self) -> &GraphConfig {
&self.config
}
pub fn update_config(&mut self, new_config: GraphConfig) -> Result<(), Box<dyn std::error::Error>> {
self.config = new_config.clone();
self.generator = TraitGraphGenerator::new(new_config.clone())?;
self.exporter = GraphExporter::new(new_config.clone());
self.generator_3d = if new_config.enable_3d {
Some(Graph3DGenerator::new(ThreeDConfig::from_graph_config(&new_config)))
} else {
None
};
Ok(())
}
pub fn available_layout_algorithms(&self) -> Vec<LayoutAlgorithm> {
LayoutAlgorithmFactory::available_algorithms()
}
pub fn available_export_formats(&self) -> Vec<GraphExportFormat> {
GraphExporter::available_formats()
}
pub fn available_themes(&self) -> Vec<VisualizationTheme> {
vec![
VisualizationTheme::Light,
VisualizationTheme::Dark,
VisualizationTheme::HighContrast,
VisualizationTheme::ColorblindFriendly,
VisualizationTheme::Minimalist,
VisualizationTheme::Professional,
]
}
}
#[derive(Debug, Default)]
pub struct ExportResults {
pub svg: Option<String>,
pub json: Option<String>,
pub dot: Option<String>,
pub interactive_html: Option<String>,
pub graphml: Option<String>,
pub gexf: Option<String>,
pub three_d_scene: Option<String>,
}
#[derive(Debug, Default)]
pub struct ComprehensiveAnalysisResults {
pub basic_analysis: GraphAnalysisResult,
pub louvain_communities: Vec<Community>,
pub critical_paths: Vec<GraphPath>,
pub hub_nodes: Vec<String>,
pub bridge_nodes: Vec<String>,
pub bottleneck_edges: Vec<String>,
pub modularity: Option<f64>,
pub small_world_coefficient: Option<f64>,
}
pub fn quick_trait_graph(
trait_info: &TraitInfo,
implementations: &[String],
) -> Result<TraitGraph, Box<dyn std::error::Error>> {
let framework = GraphVisualizationFramework::with_defaults()?;
framework.generate_complete_graph(trait_info, implementations)
}
pub fn quick_ecosystem_graph(
traits: &[&TraitInfo],
) -> Result<TraitGraph, Box<dyn std::error::Error>> {
let framework = GraphVisualizationFramework::with_defaults()?;
framework.generate_ecosystem_graph(traits)
}
pub fn quick_html_export(graph: &TraitGraph) -> Result<String, Box<dyn std::error::Error>> {
let exporter = GraphExporter::new(GraphConfig::default());
exporter.export_graph(graph, GraphExportFormat::InteractiveHtml)
}
pub fn quick_analysis(graph: &TraitGraph) -> Result<GraphAnalysisResult, Box<dyn std::error::Error>> {
let analyzer = GraphAnalyzer::new();
analyzer.analyze_graph(graph)
}
pub fn performance_optimized_graph(
trait_info: &TraitInfo,
implementations: &[String],
) -> Result<TraitGraph, Box<dyn std::error::Error>> {
let framework = GraphVisualizationFramework::with_performance_config()?;
framework.generate_complete_graph(trait_info, implementations)
}
pub fn quality_optimized_graph(
trait_info: &TraitInfo,
implementations: &[String],
) -> Result<TraitGraph, Box<dyn std::error::Error>> {
let framework = GraphVisualizationFramework::with_quality_config()?;
framework.generate_complete_graph(trait_info, implementations)
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
use std::env;
fn create_test_trait_info(name: &str) -> TraitInfo {
TraitInfo {
name: name.to_string(),
description: format!("Test trait {}", name),
path: format!("test::{}", name),
generics: vec!["T".to_string()],
associated_types: vec![AssociatedType {
name: "Item".to_string(),
description: "Test associated type".to_string(),
bounds: vec![],
}],
methods: vec!["test_method".to_string()],
supertraits: vec!["Clone".to_string()],
implementations: vec!["TestImpl".to_string()],
}
}
#[test]
fn test_framework_creation() {
let config = GraphConfig::default();
let framework = GraphVisualizationFramework::new(config);
assert!(framework.is_ok());
}
#[test]
fn test_framework_defaults() {
let framework = GraphVisualizationFramework::with_defaults();
assert!(framework.is_ok());
}
#[test]
fn test_framework_performance_config() {
let framework = GraphVisualizationFramework::with_performance_config();
assert!(framework.is_ok());
}
#[test]
fn test_framework_quality_config() {
let framework = GraphVisualizationFramework::with_quality_config();
assert!(framework.is_ok());
}
#[test]
fn test_complete_graph_generation() {
let framework = GraphVisualizationFramework::with_defaults().expect("expected valid value");
let trait_info = create_test_trait_info("TestTrait");
let implementations = vec!["TestImpl".to_string()];
let graph = framework.generate_complete_graph(&trait_info, &implementations);
assert!(graph.is_ok());
let graph = graph.expect("expected valid value");
assert!(!graph.nodes.is_empty());
assert!(!graph.edges.is_empty());
}
#[test]
fn test_ecosystem_graph_generation() {
let framework = GraphVisualizationFramework::with_defaults().expect("expected valid value");
let trait1 = create_test_trait_info("Trait1");
let trait2 = create_test_trait_info("Trait2");
let traits = vec![&trait1, &trait2];
let graph = framework.generate_ecosystem_graph(&traits);
assert!(graph.is_ok());
let graph = graph.expect("expected valid value");
assert!(!graph.nodes.is_empty());
}
#[test]
fn test_export_all_formats() {
let framework = GraphVisualizationFramework::with_defaults().expect("expected valid value");
let trait_info = create_test_trait_info("TestTrait");
let implementations = vec!["TestImpl".to_string()];
let graph = framework.generate_complete_graph(&trait_info, &implementations).expect("generate_complete_graph should succeed");
let results = framework.export_all_formats(&graph, "");
assert!(results.is_ok());
let results = results.expect("expected valid value");
assert!(results.svg.is_some());
assert!(results.json.is_some());
assert!(results.dot.is_some());
assert!(results.interactive_html.is_some());
}
#[test]
fn test_comprehensive_analysis() {
let framework = GraphVisualizationFramework::with_defaults().expect("expected valid value");
let trait_info = create_test_trait_info("TestTrait");
let implementations = vec!["TestImpl".to_string()];
let graph = framework.generate_complete_graph(&trait_info, &implementations).expect("generate_complete_graph should succeed");
let analysis = framework.analyze_comprehensive(&graph);
assert!(analysis.is_ok());
let analysis = analysis.expect("expected valid value");
assert!(!analysis.hub_nodes.is_empty() || analysis.hub_nodes.is_empty()); }
#[test]
fn test_config_update() {
let mut framework = GraphVisualizationFramework::with_defaults().expect("expected valid value");
let new_config = GraphConfig::new()
.with_layout_algorithm(LayoutAlgorithm::Circular)
.with_theme(VisualizationTheme::Dark);
let result = framework.update_config(new_config.clone());
assert!(result.is_ok());
assert_eq!(framework.config().layout_algorithm, LayoutAlgorithm::Circular);
assert_eq!(framework.config().theme, VisualizationTheme::Dark);
}
#[test]
fn test_available_algorithms_and_formats() {
let framework = GraphVisualizationFramework::with_defaults().expect("expected valid value");
let algorithms = framework.available_layout_algorithms();
assert!(!algorithms.is_empty());
let formats = framework.available_export_formats();
assert!(!formats.is_empty());
let themes = framework.available_themes();
assert!(!themes.is_empty());
}
#[test]
fn test_convenience_functions() {
let trait_info = create_test_trait_info("TestTrait");
let implementations = vec!["TestImpl".to_string()];
let graph = quick_trait_graph(&trait_info, &implementations);
assert!(graph.is_ok());
let graph = graph.expect("expected valid value");
let html = quick_html_export(&graph);
assert!(html.is_ok());
let analysis = quick_analysis(&graph);
assert!(analysis.is_ok());
}
#[test]
fn test_optimized_graph_functions() {
let trait_info = create_test_trait_info("TestTrait");
let implementations = vec!["TestImpl".to_string()];
let perf_graph = performance_optimized_graph(&trait_info, &implementations);
assert!(perf_graph.is_ok());
let quality_graph = quality_optimized_graph(&trait_info, &implementations);
assert!(quality_graph.is_ok());
}
#[test]
fn test_temporary_file_handling() {
let temp_dir = env::temp_dir();
let test_path = temp_dir.join("test_graph_framework");
let framework = GraphVisualizationFramework::with_defaults().expect("expected valid value");
let trait_info = create_test_trait_info("TestTrait");
let implementations = vec!["TestImpl".to_string()];
let graph = framework.generate_complete_graph(&trait_info, &implementations).expect("generate_complete_graph should succeed");
let results = framework.export_all_formats(&graph, test_path.to_str().expect("export_all_formats should succeed"));
assert!(results.is_ok());
let svg_file = format!("{}.svg", test_path.to_str().expect("to_str should succeed"));
let json_file = format!("{}.json", test_path.to_str().expect("to_str should succeed"));
if std::path::Path::new(&svg_file).exists() {
std::fs::remove_file(&svg_file).unwrap_or(());
}
if std::path::Path::new(&json_file).exists() {
std::fs::remove_file(&json_file).unwrap_or(());
}
}
#[test]
fn test_module_integration() {
let config = GraphConfig::new()
.with_layout_algorithm(LayoutAlgorithm::ForceDirected)
.with_3d_visualization(true)
.with_advanced_analysis(true);
let framework = GraphVisualizationFramework::new(config).expect("expected valid value");
let trait_info = create_test_trait_info("IntegrationTest");
let implementations = vec!["TestImpl".to_string()];
let graph = framework.generate_complete_graph(&trait_info, &implementations).expect("generate_complete_graph should succeed");
let analysis = framework.analyze_comprehensive(&graph).expect("analyze_comprehensive should succeed");
let exports = framework.export_all_formats(&graph, "").expect("export_all_formats should succeed");
assert!(!graph.nodes.is_empty());
assert!(exports.svg.is_some());
assert!(exports.interactive_html.is_some());
assert!(!analysis.basic_analysis.centrality_measures.is_empty());
}
}