| 1 | use serde::{Deserialize, Serialize}; |
| 2 | use std::collections::HashMap; |
| 3 | use std::io::{self, Write}; |
| 4 | |
| 5 | use crate::node::*; |
| 6 | |
| 7 | #[derive (Clone, Serialize, Deserialize)] |
| 8 | pub enum GraphKind { |
| 9 | Digraph, |
| 10 | Subgraph, |
| 11 | } |
| 12 | |
| 13 | pub type AdjList<'a> = HashMap<&'a str, Vec<&'a str>>; |
| 14 | |
| 15 | /// Graph represents a directed graph as a list of nodes and list of edges. |
| 16 | #[derive (Serialize, Deserialize)] |
| 17 | pub struct Graph { |
| 18 | /// Identifier for the graph |
| 19 | pub name: String, |
| 20 | |
| 21 | /// The Vector containing the Nodes |
| 22 | pub nodes: Vec<Node>, |
| 23 | |
| 24 | /// The Vector containing the Edges |
| 25 | pub edges: Vec<Edge>, |
| 26 | } |
| 27 | |
| 28 | #[derive (Clone)] |
| 29 | pub struct GraphvizSettings { |
| 30 | /// The attributes of the graph in graphviz. |
| 31 | pub graph_attrs: Option<String>, |
| 32 | |
| 33 | /// The attributes of the nodes in graphviz. |
| 34 | pub node_attrs: Option<String>, |
| 35 | |
| 36 | /// The attributes of the edges in graphviz. |
| 37 | pub edge_attrs: Option<String>, |
| 38 | |
| 39 | /// Label of the graph |
| 40 | pub graph_label: Option<String>, |
| 41 | } |
| 42 | |
| 43 | impl Default for GraphvizSettings { |
| 44 | fn default() -> GraphvizSettings { |
| 45 | GraphvizSettings { |
| 46 | graph_attrs: None, |
| 47 | node_attrs: None, |
| 48 | edge_attrs: None, |
| 49 | graph_label: None, |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | impl Graph { |
| 55 | pub fn new(name: String, nodes: Vec<Node>, edges: Vec<Edge>) -> Graph { |
| 56 | Graph { name, nodes, edges } |
| 57 | } |
| 58 | |
| 59 | /// Returns the adjacency list representation of the graph. |
| 60 | /// Adjacency list can be used to easily find the childern of a given node. |
| 61 | /// If the a node does not have any childern, then the list correspoding to that node |
| 62 | /// will be empty. |
| 63 | pub fn adj_list(&self) -> AdjList<'_> { |
| 64 | let mut m: AdjList<'_> = HashMap::new(); |
| 65 | for node in &self.nodes { |
| 66 | m.insert(&node.label, Vec::new()); |
| 67 | } |
| 68 | for edge in &self.edges { |
| 69 | m.entry(&edge.from).or_insert_with(Vec::new).push(&edge.to); |
| 70 | } |
| 71 | m |
| 72 | } |
| 73 | |
| 74 | /// Returns the reverse adjacency list representation of the graph. |
| 75 | /// Reverse adjacency list represents the adjacency list of a directed graph where |
| 76 | /// the edges have been reversed. |
| 77 | /// Reverse adjacency list can be used to easily find the parents of a given node. |
| 78 | /// If the a node does not have any childern, then the list correspoding to that node |
| 79 | /// will be empty. |
| 80 | pub fn rev_adj_list(&self) -> AdjList<'_> { |
| 81 | let mut m: AdjList<'_> = HashMap::new(); |
| 82 | for node in &self.nodes { |
| 83 | m.insert(&node.label, Vec::new()); |
| 84 | } |
| 85 | for edge in &self.edges { |
| 86 | m.entry(&edge.to).or_insert_with(Vec::new).push(&edge.from); |
| 87 | } |
| 88 | m |
| 89 | } |
| 90 | |
| 91 | /// Returns the node with the given label, if found. |
| 92 | pub fn get_node_by_label(&self, label: &str) -> Option<&Node> { |
| 93 | self.nodes.iter().find(|node| node.label == *label) |
| 94 | } |
| 95 | |
| 96 | /// Returns the dot representation of the given graph. |
| 97 | /// This can rendered using the graphviz program. |
| 98 | pub fn to_dot<W: Write>( |
| 99 | &self, |
| 100 | w: &mut W, |
| 101 | settings: &GraphvizSettings, |
| 102 | as_subgraph: bool, |
| 103 | ) -> io::Result<()> { |
| 104 | if as_subgraph { |
| 105 | write!(w, "subgraph cluster_ {}" , self.name)?; |
| 106 | } else { |
| 107 | write!(w, "digraph {}" , self.name)?; |
| 108 | } |
| 109 | |
| 110 | writeln!(w, " {{" )?; |
| 111 | |
| 112 | if let Some(graph_attrs) = &settings.graph_attrs { |
| 113 | writeln!(w, r#" graph [ {}];"# , graph_attrs)?; |
| 114 | } |
| 115 | if let Some(node_attrs) = &settings.node_attrs { |
| 116 | writeln!(w, r#" node [ {}];"# , node_attrs)?; |
| 117 | } |
| 118 | if let Some(edge_attrs) = &settings.edge_attrs { |
| 119 | writeln!(w, r#" edge [ {}];"# , edge_attrs)?; |
| 120 | } |
| 121 | if let Some(label) = &settings.graph_label { |
| 122 | writeln!(w, r#" label=< {}>;"# , label)?; |
| 123 | } |
| 124 | |
| 125 | for node in self.nodes.iter() { |
| 126 | write!(w, r#" {} [shape="none", label=<"# , node.label)?; |
| 127 | node.to_dot(w)?; |
| 128 | writeln!(w, ">];" )?; |
| 129 | } |
| 130 | |
| 131 | for edge in self.edges.iter() { |
| 132 | edge.to_dot(w)?; |
| 133 | } |
| 134 | |
| 135 | writeln!(w, " }}" ) |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | #[cfg (test)] |
| 140 | mod tests { |
| 141 | use crate::*; |
| 142 | fn get_test_graph() -> Graph { |
| 143 | let stmts: Vec<String> = vec!["hi" .into(), "hell" .into()]; |
| 144 | let label1: String = "bb0__0_3" .into(); |
| 145 | let style: NodeStyle = Default::default(); |
| 146 | let node1 = Node::new(stmts, label1.clone(), "0" .into(), style.clone()); |
| 147 | |
| 148 | let stmts: Vec<String> = vec!["_1 = const 1_i32" .into(), "_2 = const 2_i32" .into()]; |
| 149 | let label2: String = "bb0__1_3" .into(); |
| 150 | let node2 = Node::new(stmts, label2.clone(), "1" .into(), style.clone()); |
| 151 | |
| 152 | Graph::new( |
| 153 | "Mir_0_3" .into(), |
| 154 | vec![node1, node2], |
| 155 | vec![Edge::new(label1, label2, "return" .into())], |
| 156 | ) |
| 157 | } |
| 158 | |
| 159 | #[test ] |
| 160 | fn test_adj_list() { |
| 161 | let g = get_test_graph(); |
| 162 | let adj_list = g.adj_list(); |
| 163 | let expected: AdjList = [("bb0__0_3" , vec!["bb0__1_3" ]), (&"bb0__1_3" , vec![])] |
| 164 | .iter() |
| 165 | .cloned() |
| 166 | .collect(); |
| 167 | assert_eq!(adj_list, expected); |
| 168 | } |
| 169 | |
| 170 | #[test ] |
| 171 | fn test_json_ser() { |
| 172 | let g = get_test_graph(); |
| 173 | let json = serde_json::to_string(&g).unwrap(); |
| 174 | let expected_json: String = "\ |
| 175 | {\ |
| 176 | \"name \": \"Mir_0_3 \",\ |
| 177 | \"nodes \":[\ |
| 178 | {\ |
| 179 | \"stmts \":[\ |
| 180 | \"hi \",\ |
| 181 | \"hell \"\ |
| 182 | ],\ |
| 183 | \"label \": \"bb0__0_3 \",\ |
| 184 | \"title \": \"0 \",\ |
| 185 | \"style \":{\ |
| 186 | \"title_bg \":null,\ |
| 187 | \"last_stmt_sep \":false\ |
| 188 | }\ |
| 189 | },\ |
| 190 | {\ |
| 191 | \"stmts \":[\ |
| 192 | \"_1 = const 1_i32 \",\ |
| 193 | \"_2 = const 2_i32 \"\ |
| 194 | ],\ |
| 195 | \"label \": \"bb0__1_3 \",\ |
| 196 | \"title \": \"1 \",\ |
| 197 | \"style \":{\ |
| 198 | \"title_bg \":null,\ |
| 199 | \"last_stmt_sep \":false\ |
| 200 | }\ |
| 201 | }\ |
| 202 | ],\ |
| 203 | \"edges \":[\ |
| 204 | {\ |
| 205 | \"from \": \"bb0__0_3 \",\ |
| 206 | \"to \": \"bb0__1_3 \",\ |
| 207 | \"label \": \"return \"\ |
| 208 | }\ |
| 209 | ]\ |
| 210 | }" |
| 211 | .into(); |
| 212 | assert_eq!(json, expected_json) |
| 213 | } |
| 214 | |
| 215 | #[test ] |
| 216 | fn test_json_deser() { |
| 217 | let expected = get_test_graph(); |
| 218 | let struct_json: String = "\ |
| 219 | {\ |
| 220 | \"name \": \"Mir_0_3 \",\ |
| 221 | \"nodes \":[\ |
| 222 | {\ |
| 223 | \"stmts \":[\ |
| 224 | \"hi \",\ |
| 225 | \"hell \"\ |
| 226 | ],\ |
| 227 | \"label \": \"bb0__0_3 \",\ |
| 228 | \"title \": \"0 \",\ |
| 229 | \"style \":{\ |
| 230 | \"title_bg \":null,\ |
| 231 | \"last_stmt_sep \":false\ |
| 232 | }\ |
| 233 | },\ |
| 234 | {\ |
| 235 | \"stmts \":[\ |
| 236 | \"_1 = const 1_i32 \",\ |
| 237 | \"_2 = const 2_i32 \"\ |
| 238 | ],\ |
| 239 | \"label \": \"bb0__1_3 \",\ |
| 240 | \"title \": \"1 \",\ |
| 241 | \"style \":{\ |
| 242 | \"title_bg \":null,\ |
| 243 | \"last_stmt_sep \":false\ |
| 244 | }\ |
| 245 | }\ |
| 246 | ],\ |
| 247 | \"edges \":[\ |
| 248 | {\ |
| 249 | \"from \": \"bb0__0_3 \",\ |
| 250 | \"to \": \"bb0__1_3 \",\ |
| 251 | \"label \": \"return \"\ |
| 252 | }\ |
| 253 | ]\ |
| 254 | }" |
| 255 | .into(); |
| 256 | let got: Graph = serde_json::from_str(&struct_json).unwrap(); |
| 257 | |
| 258 | assert_eq!(expected.nodes.len(), got.nodes.len()); |
| 259 | assert_eq!(expected.edges.len(), got.edges.len()); |
| 260 | |
| 261 | for (n1, n2) in expected.nodes.iter().zip(got.nodes.iter()) { |
| 262 | assert_eq!(n1.stmts, n2.stmts); |
| 263 | assert_eq!(n1.label, n2.label); |
| 264 | assert_eq!(n1.title, n2.title); |
| 265 | } |
| 266 | |
| 267 | for (e1, e2) in expected.edges.iter().zip(got.edges.iter()) { |
| 268 | assert_eq!(e1.from, e2.from); |
| 269 | assert_eq!(e1.to, e2.to); |
| 270 | assert_eq!(e1.label, e2.label); |
| 271 | } |
| 272 | } |
| 273 | } |
| 274 | |