1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::util::escape_html;
use std::io::{self, Write};
use serde::{Deserialize, Serialize};

/// NodeStyle defines some style of [Node](struct.Node.html)
#[derive(Clone, Serialize, Deserialize)]
pub struct NodeStyle {
    /// Override the title color of the title
    /// To color the title of the node differently in graphviz
    pub title_bg: Option<String>,

    /// Print a seperator b/w the rest of the statements and the last one
    pub last_stmt_sep: bool,
}

impl Default for NodeStyle {
    fn default() -> NodeStyle {
        NodeStyle {
            title_bg: None,
            last_stmt_sep: false,
        }
    }
}

/// A graph node
#[derive(Clone, Serialize, Deserialize)]
pub struct Node {
    /// A list of statements.
    pub stmts: Vec<String>,

    /// A unique identifier for the given node.
    pub label: String,

    /// The title is printed on the top of BB, the index of the basic block
    pub(crate) title: String,

    /// Can be used to override the default styles
    pub(crate) style: NodeStyle,
}

impl Node {
    pub fn new(stmts: Vec<String>, label: String, title: String, style: NodeStyle) -> Node {
        Node {
            stmts,
            label,
            title,
            style,
        }
    }

    pub fn to_dot<W: Write>(&self, w: &mut W) -> io::Result<()> {
        write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;

        let bg_attr = match &self.style.title_bg {
            Some(color) => format!(r#"bgcolor="{}""#, color),
            None => "".into(),
        };
        write!(
            w,
            r#"<tr><td {bg_attr} {attrs} colspan="{colspan}">{blk}</td></tr>"#,
            attrs = r#"align="center""#,
            // TODO: Not sure what this is for
            colspan = 1,
            blk = self.title,
            bg_attr = bg_attr
        )?;

        let stmts_len = self.stmts.len();
        if !self.stmts.is_empty() {
            if self.stmts.len() > 1 {
                write!(w, r#"<tr><td align="left" balign="left">"#)?;
                for statement in &self.stmts[..stmts_len - 1] {
                    write!(w, "{}<br/>", escape_html(statement))?;
                }
                write!(w, "</td></tr>")?;
            }

            if !self.style.last_stmt_sep {
                write!(w, r#"<tr><td align="left">"#)?;
                write!(w, "{}", escape_html(&self.stmts[stmts_len - 1]))?;
            } else {
                write!(w, r#"<tr><td align="left" balign="left">"#)?;
                write!(w, "{}", escape_html(&self.stmts[stmts_len - 1]))?;
            }
            write!(w, "</td></tr>")?;
        }

        write!(w, "</table>")
    }
}

/// A directed graph edge
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Edge {
    /// The label of the source node of the edge.
    pub from: String,

    /// The label of the target node of the edge.
    pub to: String,

    /// The label (title) of the edge. This doesn't have to unique.
    // TODO: Rename this to title?
    pub label: String,
}

impl Edge {
    pub fn new(from: String, to: String, label: String) -> Edge {
        Edge { from, to, label }
    }

    pub fn to_dot<W: Write>(&self, w: &mut W) -> io::Result<()> {
        writeln!(
            w,
            r#"    {} -> {} [label="{}"];"#,
            self.from, self.to, self.label
        )
    }
}