pub mod graph_3d;
pub mod graph_analysis;
pub mod graph_config;
pub mod graph_export;
pub mod graph_generator;
pub mod graph_structures;
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::*;
use crate::api_reference_generator::TraitInfo;
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),
config.theme,
))
} 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::Performance)
.with_simd_acceleration(true)
.with_gpu_acceleration(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::Scientific);
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.clone());
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.clone());
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 {
svg: Some(self.exporter.export_graph(graph, GraphExportFormat::Svg)?),
json: Some(self.exporter.export_graph(graph, GraphExportFormat::Json)?),
dot: Some(self.exporter.export_graph(graph, GraphExportFormat::Dot)?),
interactive_html: Some(
self.exporter
.export_graph(graph, GraphExportFormat::InteractiveHtml)?,
),
graphml: Some(
self.exporter
.export_graph(graph, GraphExportFormat::GraphMl)?,
),
gexf: Some(self.exporter.export_graph(graph, GraphExportFormat::Gexf)?),
..Default::default()
};
if self.config.enable_3d {
if let Some(ref generator_3d) = self.generator_3d {
results.three_d_scene = Some(generator_3d.generate_3d_html(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 {
basic_analysis: self.analyzer.analyze_graph(graph)?,
louvain_communities: self
.analyzer
.detect_communities(graph, CommunityDetection::Louvain)?,
critical_paths: self.analyzer.find_critical_paths_comprehensive(graph)?,
hub_nodes: self.analyzer.identify_hub_nodes(graph, 0.1)?,
bridge_nodes: self.analyzer.identify_bridge_nodes(graph)?,
bottleneck_edges: self.analyzer.identify_bottleneck_edges(graph)?,
small_world_coefficient: Some(self.analyzer.calculate_small_world_coefficient(graph)?),
..Default::default()
};
if !results.louvain_communities.is_empty() {
results.modularity = Some(
self.analyzer
.calculate_modularity(graph, &results.louvain_communities)?,
);
}
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),
new_config.theme,
))
} else {
None
};
Ok(())
}
pub fn available_layout_algorithms(&self) -> Vec<LayoutAlgorithm> {
LayoutAlgorithmFactory::available_algorithms()
}
pub fn available_export_formats(&self) -> Vec<GraphExportFormat> {
GraphExporter::get_supported_formats()
}
pub fn available_themes(&self) -> Vec<VisualizationTheme> {
vec![
VisualizationTheme::Light,
VisualizationTheme::Dark,
VisualizationTheme::HighContrast,
VisualizationTheme::ColorblindFriendly,
VisualizationTheme::Presentation,
VisualizationTheme::Scientific,
VisualizationTheme::Minimalist,
VisualizationTheme::Vibrant,
]
}
}
#[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());
Ok(exporter.export_graph(graph, GraphExportFormat::InteractiveHtml)?)
}
pub fn quick_analysis(
graph: &TraitGraph,
) -> Result<GraphAnalysisResult, Box<dyn std::error::Error>> {
let analyzer = GraphAnalyzer::new();
Ok(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 crate::api_reference_generator::{AssociatedType, MethodInfo, Visibility};
use std::env;
fn create_test_trait_info(name: &str) -> TraitInfo {
TraitInfo {
name: name.to_string(),
docs: Some(format!("Test trait {}", name)),
module_path: Some(format!("test::{}", name)),
visibility: Visibility::Public,
generics: vec!["T".to_string()],
supertraits: vec!["Clone".to_string()],
associated_types: vec![AssociatedType {
name: "Item".to_string(),
bounds: Vec::new(),
default: None,
}],
methods: vec![MethodInfo {
name: "test_method".to_string(),
signature: "fn test_method(&self)".to_string(),
docs: None,
is_required: true,
is_async: false,
is_unsafe: false,
generics: Vec::new(),
return_type: None,
arguments: Vec::new(),
}],
source_file: None,
source_line: None,
feature_flags: Vec::new(),
}
}
#[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());
}
}