1 | use crate::util::escape_html; |
2 | use std::io::{self, Write}; |
3 | use serde::{Deserialize, Serialize}; |
4 | |
5 | /// NodeStyle defines some style of [Node](struct.Node.html) |
6 | #[derive (Clone, Serialize, Deserialize)] |
7 | pub struct NodeStyle { |
8 | /// Override the title color of the title |
9 | /// To color the title of the node differently in graphviz |
10 | pub title_bg: Option<String>, |
11 | |
12 | /// Print a seperator b/w the rest of the statements and the last one |
13 | pub last_stmt_sep: bool, |
14 | } |
15 | |
16 | impl Default for NodeStyle { |
17 | fn default() -> NodeStyle { |
18 | NodeStyle { |
19 | title_bg: None, |
20 | last_stmt_sep: false, |
21 | } |
22 | } |
23 | } |
24 | |
25 | /// A graph node |
26 | #[derive (Clone, Serialize, Deserialize)] |
27 | pub struct Node { |
28 | /// A list of statements. |
29 | pub stmts: Vec<String>, |
30 | |
31 | /// A unique identifier for the given node. |
32 | pub label: String, |
33 | |
34 | /// The title is printed on the top of BB, the index of the basic block |
35 | pub(crate) title: String, |
36 | |
37 | /// Can be used to override the default styles |
38 | pub(crate) style: NodeStyle, |
39 | } |
40 | |
41 | impl Node { |
42 | pub fn new(stmts: Vec<String>, label: String, title: String, style: NodeStyle) -> Node { |
43 | Node { |
44 | stmts, |
45 | label, |
46 | title, |
47 | style, |
48 | } |
49 | } |
50 | |
51 | pub fn to_dot<W: Write>(&self, w: &mut W) -> io::Result<()> { |
52 | write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"# )?; |
53 | |
54 | let bg_attr = match &self.style.title_bg { |
55 | Some(color) => format!(r#"bgcolor=" {}""# , color), |
56 | None => "" .into(), |
57 | }; |
58 | write!( |
59 | w, |
60 | r#"<tr><td {bg_attr} {attrs} colspan=" {colspan}"> {blk}</td></tr>"# , |
61 | attrs = r#"align="center""# , |
62 | // TODO: Not sure what this is for |
63 | colspan = 1, |
64 | blk = self.title, |
65 | bg_attr = bg_attr |
66 | )?; |
67 | |
68 | let stmts_len = self.stmts.len(); |
69 | if !self.stmts.is_empty() { |
70 | if self.stmts.len() > 1 { |
71 | write!(w, r#"<tr><td align="left" balign="left">"# )?; |
72 | for statement in &self.stmts[..stmts_len - 1] { |
73 | write!(w, " {}<br/>" , escape_html(statement))?; |
74 | } |
75 | write!(w, "</td></tr>" )?; |
76 | } |
77 | |
78 | if !self.style.last_stmt_sep { |
79 | write!(w, r#"<tr><td align="left">"# )?; |
80 | write!(w, " {}" , escape_html(&self.stmts[stmts_len - 1]))?; |
81 | } else { |
82 | write!(w, r#"<tr><td align="left" balign="left">"# )?; |
83 | write!(w, " {}" , escape_html(&self.stmts[stmts_len - 1]))?; |
84 | } |
85 | write!(w, "</td></tr>" )?; |
86 | } |
87 | |
88 | write!(w, "</table>" ) |
89 | } |
90 | } |
91 | |
92 | /// A directed graph edge |
93 | #[derive (Clone, Serialize, Deserialize, Debug)] |
94 | pub struct Edge { |
95 | /// The label of the source node of the edge. |
96 | pub from: String, |
97 | |
98 | /// The label of the target node of the edge. |
99 | pub to: String, |
100 | |
101 | /// The label (title) of the edge. This doesn't have to unique. |
102 | // TODO: Rename this to title? |
103 | pub label: String, |
104 | } |
105 | |
106 | impl Edge { |
107 | pub fn new(from: String, to: String, label: String) -> Edge { |
108 | Edge { from, to, label } |
109 | } |
110 | |
111 | pub fn to_dot<W: Write>(&self, w: &mut W) -> io::Result<()> { |
112 | writeln!( |
113 | w, |
114 | r#" {} -> {} [label=" {}"];"# , |
115 | self.from, self.to, self.label |
116 | ) |
117 | } |
118 | } |
119 | |