hydro_lang/graph/
debug.rs

1//! Debugging utilities for Hydro IR graph visualization.
2//!
3//! Similar to the DFIR debugging utilities, this module provides convenient
4//! methods for opening graphs in web browsers and VS Code.
5
6use std::fmt::Write;
7use std::io::Result;
8
9use super::render::{HydroWriteConfig, render_hydro_ir_dot, render_hydro_ir_mermaid};
10use super::template::get_template;
11use crate::ir::HydroRoot;
12
13/// Opens Hydro IR roots as a single mermaid diagram.
14pub fn open_mermaid(roots: &[HydroRoot], config: Option<HydroWriteConfig>) -> Result<()> {
15    let mermaid_src = render_with_config(roots, config, render_hydro_ir_mermaid);
16    open_mermaid_browser(&mermaid_src)
17}
18
19/// Opens Hydro IR roots as a single DOT diagram.
20pub fn open_dot(roots: &[HydroRoot], config: Option<HydroWriteConfig>) -> Result<()> {
21    let dot_src = render_with_config(roots, config, render_hydro_ir_dot);
22    open_dot_browser(&dot_src)
23}
24
25/// Opens Hydro IR roots as a ReactFlow.js visualization in a browser.
26/// Creates a complete HTML file with ReactFlow.js interactive graph visualization.
27pub fn open_reactflow_browser(
28    roots: &[HydroRoot],
29    filename: Option<&str>,
30    config: Option<HydroWriteConfig>,
31) -> Result<()> {
32    let reactflow_json = render_with_config(roots, config, render_hydro_ir_reactflow);
33    let filename = filename.unwrap_or("hydro_graph.html");
34    save_and_open_reactflow_browser(&reactflow_json, filename)
35}
36
37/// Saves Hydro IR roots as a ReactFlow.js JSON file.
38/// If no filename is provided, saves to temporary directory.
39pub fn save_reactflow_json(
40    roots: &[HydroRoot],
41    filename: Option<&str>,
42    config: Option<HydroWriteConfig>,
43) -> Result<std::path::PathBuf> {
44    let content = render_with_config(roots, config, render_hydro_ir_reactflow);
45    save_to_file(content, filename, "hydro_graph.json", "ReactFlow.js JSON")
46}
47
48/// Saves Hydro IR roots as a Mermaid diagram file.
49/// If no filename is provided, saves to temporary directory.
50pub fn save_mermaid(
51    roots: &[HydroRoot],
52    filename: Option<&str>,
53    config: Option<HydroWriteConfig>,
54) -> Result<std::path::PathBuf> {
55    let content = render_with_config(roots, config, render_hydro_ir_mermaid);
56    save_to_file(content, filename, "hydro_graph.mermaid", "Mermaid diagram")
57}
58
59/// Saves Hydro IR roots as a DOT/Graphviz file.
60/// If no filename is provided, saves to temporary directory.
61pub fn save_dot(
62    roots: &[HydroRoot],
63    filename: Option<&str>,
64    config: Option<HydroWriteConfig>,
65) -> Result<std::path::PathBuf> {
66    let content = render_with_config(roots, config, render_hydro_ir_dot);
67    save_to_file(content, filename, "hydro_graph.dot", "DOT/Graphviz file")
68}
69
70fn open_mermaid_browser(mermaid_src: &str) -> Result<()> {
71    // Debug: Print the mermaid source being sent to browser
72    println!("=== MERMAID SOURCE BEING SENT TO BROWSER ===");
73    println!("{}", mermaid_src);
74    println!("=== END MERMAID SOURCE ===");
75
76    let state = serde_json::json!({
77        "code": mermaid_src,
78        "mermaid": serde_json::json!({
79            "theme": "default"
80        }),
81        "autoSync": true,
82        "updateDiagram": true
83    });
84    let state_json = serde_json::to_vec(&state)?;
85    let state_base64 = data_encoding::BASE64URL.encode(&state_json);
86    webbrowser::open(&format!(
87        "https://mermaid.live/edit#base64:{}",
88        state_base64
89    ))
90}
91
92fn open_dot_browser(dot_src: &str) -> Result<()> {
93    let mut url = "https://dreampuf.github.io/GraphvizOnline/#".to_owned();
94    for byte in dot_src.bytes() {
95        // Lazy percent encoding: https://en.wikipedia.org/wiki/Percent-encoding
96        write!(url, "%{:02x}", byte).unwrap();
97    }
98    webbrowser::open(&url)
99}
100
101/// Helper function to create a complete HTML file with ReactFlow.js visualization and open it in browser.
102/// Creates files in temporary directory to avoid cluttering the workspace.
103pub fn save_and_open_reactflow_browser(reactflow_json: &str, filename: &str) -> Result<()> {
104    let template = get_template();
105    let html_content = template.replace("{{GRAPH_DATA}}", reactflow_json);
106
107    // Create file in temporary directory
108    let temp_file = save_to_file(html_content, None, filename, "HTML/Reactflow JS file").unwrap();
109    println!("Got path {}", temp_file.display());
110
111    // Open the HTML file in browser
112    let file_url = format!("file://{}", temp_file.display());
113    webbrowser::open(&file_url)?;
114
115    println!("Opened Enhanced ReactFlow.js visualization in browser.");
116    Ok(())
117}
118
119/// Helper function to render multiple Hydro IR roots as ReactFlow.js JSON.
120fn render_hydro_ir_reactflow(roots: &[HydroRoot], config: &HydroWriteConfig) -> String {
121    super::render::render_hydro_ir_reactflow(roots, config)
122}
123
124/// Helper function to save content to a file with consistent path handling.
125/// If no filename is provided, saves to temporary directory with the default name.
126fn save_to_file(
127    content: String,
128    filename: Option<&str>,
129    default_name: &str,
130    content_type: &str,
131) -> Result<std::path::PathBuf> {
132    let file_path = if let Some(filename) = filename {
133        std::path::PathBuf::from(filename)
134    } else {
135        std::env::temp_dir().join(default_name)
136    };
137
138    std::fs::write(&file_path, content)?;
139    println!("Saved {} to {}", content_type, file_path.display());
140    Ok(file_path)
141}
142
143/// Helper function to handle config unwrapping and rendering.
144fn render_with_config<F>(
145    roots: &[HydroRoot],
146    config: Option<HydroWriteConfig>,
147    renderer: F,
148) -> String
149where
150    F: Fn(&[HydroRoot], &HydroWriteConfig) -> String,
151{
152    let config = config.unwrap_or_default();
153    renderer(roots, &config)
154}