use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::io::{self, Write}; use crate::node::*; #[derive(Clone, Serialize, Deserialize)] pub enum GraphKind { Digraph, Subgraph, } pub type AdjList<'a> = HashMap<&'a str, Vec<&'a str>>; /// Graph represents a directed graph as a list of nodes and list of edges. #[derive(Serialize, Deserialize)] pub struct Graph { /// Identifier for the graph pub name: String, /// The Vector containing the Nodes pub nodes: Vec, /// The Vector containing the Edges pub edges: Vec, } #[derive(Clone)] pub struct GraphvizSettings { /// The attributes of the graph in graphviz. pub graph_attrs: Option, /// The attributes of the nodes in graphviz. pub node_attrs: Option, /// The attributes of the edges in graphviz. pub edge_attrs: Option, /// Label of the graph pub graph_label: Option, } impl Default for GraphvizSettings { fn default() -> GraphvizSettings { GraphvizSettings { graph_attrs: None, node_attrs: None, edge_attrs: None, graph_label: None, } } } impl Graph { pub fn new(name: String, nodes: Vec, edges: Vec) -> Graph { Graph { name, nodes, edges } } /// Returns the adjacency list representation of the graph. /// Adjacency list can be used to easily find the childern of a given node. /// If the a node does not have any childern, then the list correspoding to that node /// will be empty. pub fn adj_list(&self) -> AdjList<'_> { let mut m: AdjList<'_> = HashMap::new(); for node in &self.nodes { m.insert(&node.label, Vec::new()); } for edge in &self.edges { m.entry(&edge.from).or_insert_with(Vec::new).push(&edge.to); } m } /// Returns the reverse adjacency list representation of the graph. /// Reverse adjacency list represents the adjacency list of a directed graph where /// the edges have been reversed. /// Reverse adjacency list can be used to easily find the parents of a given node. /// If the a node does not have any childern, then the list correspoding to that node /// will be empty. pub fn rev_adj_list(&self) -> AdjList<'_> { let mut m: AdjList<'_> = HashMap::new(); for node in &self.nodes { m.insert(&node.label, Vec::new()); } for edge in &self.edges { m.entry(&edge.to).or_insert_with(Vec::new).push(&edge.from); } m } /// Returns the node with the given label, if found. pub fn get_node_by_label(&self, label: &str) -> Option<&Node> { self.nodes.iter().find(|node| node.label == *label) } /// Returns the dot representation of the given graph. /// This can rendered using the graphviz program. pub fn to_dot( &self, w: &mut W, settings: &GraphvizSettings, as_subgraph: bool, ) -> io::Result<()> { if as_subgraph { write!(w, "subgraph cluster_{}", self.name)?; } else { write!(w, "digraph {}", self.name)?; } writeln!(w, " {{")?; if let Some(graph_attrs) = &settings.graph_attrs { writeln!(w, r#" graph [{}];"#, graph_attrs)?; } if let Some(node_attrs) = &settings.node_attrs { writeln!(w, r#" node [{}];"#, node_attrs)?; } if let Some(edge_attrs) = &settings.edge_attrs { writeln!(w, r#" edge [{}];"#, edge_attrs)?; } if let Some(label) = &settings.graph_label { writeln!(w, r#" label=<{}>;"#, label)?; } for node in self.nodes.iter() { write!(w, r#" {} [shape="none", label=<"#, node.label)?; node.to_dot(w)?; writeln!(w, ">];")?; } for edge in self.edges.iter() { edge.to_dot(w)?; } writeln!(w, "}}") } } #[cfg(test)] mod tests { use crate::*; fn get_test_graph() -> Graph { let stmts: Vec = vec!["hi".into(), "hell".into()]; let label1: String = "bb0__0_3".into(); let style: NodeStyle = Default::default(); let node1 = Node::new(stmts, label1.clone(), "0".into(), style.clone()); let stmts: Vec = vec!["_1 = const 1_i32".into(), "_2 = const 2_i32".into()]; let label2: String = "bb0__1_3".into(); let node2 = Node::new(stmts, label2.clone(), "1".into(), style.clone()); Graph::new( "Mir_0_3".into(), vec![node1, node2], vec![Edge::new(label1, label2, "return".into())], ) } #[test] fn test_adj_list() { let g = get_test_graph(); let adj_list = g.adj_list(); let expected: AdjList = [("bb0__0_3", vec!["bb0__1_3"]), (&"bb0__1_3", vec![])] .iter() .cloned() .collect(); assert_eq!(adj_list, expected); } #[test] fn test_json_ser() { let g = get_test_graph(); let json = serde_json::to_string(&g).unwrap(); let expected_json: String = "\ {\ \"name\":\"Mir_0_3\",\ \"nodes\":[\ {\ \"stmts\":[\ \"hi\",\ \"hell\"\ ],\ \"label\":\"bb0__0_3\",\ \"title\":\"0\",\ \"style\":{\ \"title_bg\":null,\ \"last_stmt_sep\":false\ }\ },\ {\ \"stmts\":[\ \"_1 = const 1_i32\",\ \"_2 = const 2_i32\"\ ],\ \"label\":\"bb0__1_3\",\ \"title\":\"1\",\ \"style\":{\ \"title_bg\":null,\ \"last_stmt_sep\":false\ }\ }\ ],\ \"edges\":[\ {\ \"from\":\"bb0__0_3\",\ \"to\":\"bb0__1_3\",\ \"label\":\"return\"\ }\ ]\ }" .into(); assert_eq!(json, expected_json) } #[test] fn test_json_deser() { let expected = get_test_graph(); let struct_json: String = "\ {\ \"name\":\"Mir_0_3\",\ \"nodes\":[\ {\ \"stmts\":[\ \"hi\",\ \"hell\"\ ],\ \"label\":\"bb0__0_3\",\ \"title\":\"0\",\ \"style\":{\ \"title_bg\":null,\ \"last_stmt_sep\":false\ }\ },\ {\ \"stmts\":[\ \"_1 = const 1_i32\",\ \"_2 = const 2_i32\"\ ],\ \"label\":\"bb0__1_3\",\ \"title\":\"1\",\ \"style\":{\ \"title_bg\":null,\ \"last_stmt_sep\":false\ }\ }\ ],\ \"edges\":[\ {\ \"from\":\"bb0__0_3\",\ \"to\":\"bb0__1_3\",\ \"label\":\"return\"\ }\ ]\ }" .into(); let got: Graph = serde_json::from_str(&struct_json).unwrap(); assert_eq!(expected.nodes.len(), got.nodes.len()); assert_eq!(expected.edges.len(), got.edges.len()); for (n1, n2) in expected.nodes.iter().zip(got.nodes.iter()) { assert_eq!(n1.stmts, n2.stmts); assert_eq!(n1.label, n2.label); assert_eq!(n1.title, n2.title); } for (e1, e2) in expected.edges.iter().zip(got.edges.iter()) { assert_eq!(e1.from, e2.from); assert_eq!(e1.to, e2.to); assert_eq!(e1.label, e2.label); } } }