vizz/graph.rs
1use std::io::{Result, Write};
2
3use crate::Visualize;
4
5#[derive(Debug, Clone)]
6/// A struct for building a graph
7///
8/// This is the main tool end users will want to use to generate graph visualizations.
9///
10/// # Example
11///
12/// ```
13/// use vizz::Graph;
14/// use std::fs::File;
15///
16/// struct MyStruct<'a> {
17/// my_u8: u8,
18/// my_string: String,
19/// my_ref: &'a String,
20/// }
21///
22/// # use vizz::Visualize;
23/// # use vizz::DataDescription;
24/// #
25/// # impl<'a> Visualize for MyStruct<'a> {
26/// # fn associated_data(&self) -> Option<Vec<DataDescription>> {
27/// # Some(vec![
28/// # DataDescription::from(&self.my_u8).with_label("my_u8"),
29/// # DataDescription::from(&self.my_string).with_label("my_string"),
30/// # DataDescription::from(&self.my_ref).with_label("my_ref"),
31/// # ])
32/// # }
33/// # }
34/// #
35/// // create some data
36/// let unowned_string = String::from("this is referenced!");
37/// let my_struct = MyStruct {
38/// my_u8: 42,
39/// my_string: "this is owned!".into(),
40/// my_ref: &unowned_string,
41/// };
42///
43/// // create file
44/// let mut dot_file = File::create("/tmp/my_struct.dot").unwrap();
45///
46/// // create graph
47/// Graph::new()
48/// .set_id("my_test_graph")
49/// .add_node(&my_struct)
50/// .add_node(&unowned_string)
51/// .write_to(&mut dot_file)
52/// .unwrap();
53/// ```
54pub struct Graph {
55 /// The ID of the graph in the DOT language grammar
56 id: String,
57 /// The string containing the dot file contents, to eventually be written to a file
58 buffer: String,
59}
60
61impl Graph {
62 /// Create a new graph
63 pub fn new() -> Graph {
64 Graph {
65 id: String::from("visualization"),
66 buffer: String::new(),
67 }
68 }
69
70 /// Set the ID for the graph
71 ///
72 /// See the DOT language grammar ID for more info: <https://graphviz.org/doc/info/lang.html>
73 pub fn set_id(self, new_id: impl Into<String>) -> Graph {
74 Graph {
75 id: new_id.into(),
76 ..self
77 }
78 }
79
80 /// Add a data structure that implements [Visualize] to the [Graph]
81 pub fn add_node<V>(self, node: &V) -> Graph
82 where
83 V: Visualize,
84 {
85 Graph {
86 buffer: self.buffer + &node.render_node(),
87 ..self
88 }
89 }
90
91 /// Create the full DOT graph file contents as a [String]
92 pub fn render(&self) -> String {
93 format!(
94 r#"digraph {} {{
95{}
96}}"#,
97 self.id, self.buffer
98 )
99 }
100
101 /// Write the DOT file to the filesystem
102 pub fn write_to<W: Write>(self, writer: &mut W) -> Result<()> {
103 write!(writer, "{}", self.render())
104 }
105}
106
107impl Default for Graph {
108 fn default() -> Self {
109 Graph::new()
110 }
111}
112
113#[cfg(test)]
114mod test {
115 use super::*;
116 use crate::util;
117
118 #[test]
119 fn test_render_graph() {
120 let target = 8u8;
121 let target_address_string = util::address_of(&target);
122 let graph_id = "test_generate_graph";
123 let graph = Graph::new().set_id(graph_id).add_node(&target);
124 assert_eq!(graph.render(), format!("digraph {1} {{\n node [shape=plaintext]\n \"{0}\" [label=<<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\"><TR><TD PORT=\"{0}-address\"><I>{0}</I></TD><TD PORT=\"{0}-type\"><B>u8</B></TD><TD PORT=\"{0}-value\">8</TD></TR></TABLE>>];\n \n}}", target_address_string, graph_id));
125 }
126}