1use std::error::Error;
2
3use slotmap::SecondaryMap;
4
5use crate::compile::ir::HydroRoot;
6use crate::location::LocationKey;
7use crate::viz::render::{
8 HydroWriteConfig, render_hydro_ir_dot, render_hydro_ir_json, render_hydro_ir_mermaid,
9};
10
11pub struct GraphApi<'a> {
13 ir: &'a [HydroRoot],
14 location_names: &'a SecondaryMap<LocationKey, String>,
15}
16
17impl<'a> GraphApi<'a> {
18 pub fn new(ir: &'a [HydroRoot], location_names: &'a SecondaryMap<LocationKey, String>) -> Self {
19 Self { ir, location_names }
20 }
21
22 fn config(&self, use_short_labels: bool, show_metadata: bool) -> HydroWriteConfig<'a> {
23 HydroWriteConfig {
24 show_metadata,
25 show_location_groups: true,
26 use_short_labels,
27 location_names: self.location_names,
28 }
29 }
30
31 pub fn render(
33 &self,
34 format: crate::viz::config::GraphType,
35 use_short_labels: bool,
36 show_metadata: bool,
37 ) -> String {
38 let config = self.config(use_short_labels, show_metadata);
39 match format {
40 crate::viz::config::GraphType::Mermaid => render_hydro_ir_mermaid(self.ir, config),
41 crate::viz::config::GraphType::Dot => render_hydro_ir_dot(self.ir, config),
42 crate::viz::config::GraphType::Json => render_hydro_ir_json(self.ir, config),
43 crate::viz::config::GraphType::Ir => {
44 crate::compile::ir::serialize_dedup_shared(|| {
45 serde_json::to_string_pretty(self.ir).expect("failed to serialize IR")
46 })
47 }
48 }
49 }
50
51 pub fn write_to_file(
53 &self,
54 format: crate::viz::config::GraphType,
55 filename: &str,
56 use_short_labels: bool,
57 show_metadata: bool,
58 ) -> Result<(), Box<dyn Error>> {
59 let content = self.render(format, use_short_labels, show_metadata);
60 std::fs::write(filename, content)?;
61 Ok(())
62 }
63
64 #[cfg(feature = "build")]
66 pub fn generate_graph(
67 &self,
68 config: &crate::viz::config::GraphConfig,
69 ) -> Result<Option<String>, Box<dyn Error>> {
70 let Some(graph_type) = config.graph else {
71 return Ok(None);
72 };
73 let filename = config
74 .output
75 .clone()
76 .unwrap_or_else(|| format!("hydro_graph.{}", graph_type.file_extension()));
77 self.write_to_file(
78 graph_type,
79 &filename,
80 !config.long_labels,
81 !config.no_metadata,
82 )?;
83 Ok(Some(filename))
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn test_string_generation() {
93 let ir = vec![];
94 let mut location_names = SecondaryMap::new();
95 let loc_key_1 = LocationKey::TEST_KEY_1;
96 location_names.insert(loc_key_1, "test_process".to_owned());
97
98 let api = GraphApi::new(&ir, &location_names);
99
100 let mermaid = api.render(crate::viz::config::GraphType::Mermaid, true, true);
101 let dot = api.render(crate::viz::config::GraphType::Dot, true, true);
102 let json = api.render(crate::viz::config::GraphType::Json, true, true);
103
104 assert!(!mermaid.is_empty());
105 assert!(!dot.is_empty());
106 assert!(!json.is_empty());
107 }
108}