1 use super::LabelText::{self, EscStr, HtmlStr, LabelStr};
2 use super::{render, Edges, GraphWalk, Id, Labeller, Nodes, Style};
4 use std::io::prelude::*;
7 /// each node is an index in a vector in the graph.
16 fn edge(from: usize, to: usize, label: &'static str, style: Style) -> Edge {
17 Edge { from, to, label, style }
20 struct LabelledGraph {
21 /// The name for this graph. Used for labeling generated `digraph`.
24 /// Each node is an index into `node_labels`; these labels are
25 /// used as the label text for each node. (The node *names*,
26 /// which are unique identifiers, are derived from their index
29 /// If a node maps to None here, then just use its name as its
31 node_labels: Vec<Option<&'static str>>,
33 node_styles: Vec<Style>,
35 /// Each edge relates a from-index to a to-index along with a
36 /// label; `edges` collects them.
40 // A simple wrapper around LabelledGraph that forces the labels to
41 // be emitted as EscStr.
42 struct LabelledGraphWithEscStrs {
47 AllNodesLabelled(Vec<L>),
48 UnlabelledNodes(usize),
49 SomeNodesLabelled(Vec<Option<L>>),
52 type Trivial = NodeLabels<&'static str>;
54 impl NodeLabels<&'static str> {
55 fn to_opt_strs(self) -> Vec<Option<&'static str>> {
57 UnlabelledNodes(len) => vec![None; len],
58 AllNodesLabelled(lbls) => lbls.into_iter().map(Some).collect(),
59 SomeNodesLabelled(lbls) => lbls,
63 fn len(&self) -> usize {
65 &UnlabelledNodes(len) => len,
66 &AllNodesLabelled(ref lbls) => lbls.len(),
67 &SomeNodesLabelled(ref lbls) => lbls.len(),
77 node_styles: Option<Vec<Style>>,
79 let count = node_labels.len();
82 node_labels: node_labels.to_opt_strs(),
84 node_styles: match node_styles {
86 None => vec![Style::None; count],
92 impl LabelledGraphWithEscStrs {
93 fn new(name: &'static str, node_labels: Trivial, edges: Vec<Edge>) -> LabelledGraphWithEscStrs {
94 LabelledGraphWithEscStrs { graph: LabelledGraph::new(name, node_labels, edges, None) }
98 fn id_name<'a>(n: &Node) -> Id<'a> {
99 Id::new(format!("N{}", *n)).unwrap()
102 impl<'a> Labeller<'a> for LabelledGraph {
104 type Edge = &'a Edge;
105 fn graph_id(&'a self) -> Id<'a> {
106 Id::new(self.name).unwrap()
108 fn node_id(&'a self, n: &Node) -> Id<'a> {
111 fn node_label(&'a self, n: &Node) -> LabelText<'a> {
112 match self.node_labels[*n] {
113 Some(l) => LabelStr(l.into()),
114 None => LabelStr(id_name(n).name),
117 fn edge_label(&'a self, e: &&'a Edge) -> LabelText<'a> {
118 LabelStr(e.label.into())
120 fn node_style(&'a self, n: &Node) -> Style {
123 fn edge_style(&'a self, e: &&'a Edge) -> Style {
128 impl<'a> Labeller<'a> for LabelledGraphWithEscStrs {
130 type Edge = &'a Edge;
131 fn graph_id(&'a self) -> Id<'a> {
132 self.graph.graph_id()
134 fn node_id(&'a self, n: &Node) -> Id<'a> {
135 self.graph.node_id(n)
137 fn node_label(&'a self, n: &Node) -> LabelText<'a> {
138 match self.graph.node_label(n) {
139 LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
142 fn edge_label(&'a self, e: &&'a Edge) -> LabelText<'a> {
143 match self.graph.edge_label(e) {
144 LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
149 impl<'a> GraphWalk<'a> for LabelledGraph {
151 type Edge = &'a Edge;
152 fn nodes(&'a self) -> Nodes<'a, Node> {
153 (0..self.node_labels.len()).collect()
155 fn edges(&'a self) -> Edges<'a, &'a Edge> {
156 self.edges.iter().collect()
158 fn source(&'a self, edge: &&'a Edge) -> Node {
161 fn target(&'a self, edge: &&'a Edge) -> Node {
166 impl<'a> GraphWalk<'a> for LabelledGraphWithEscStrs {
168 type Edge = &'a Edge;
169 fn nodes(&'a self) -> Nodes<'a, Node> {
172 fn edges(&'a self) -> Edges<'a, &'a Edge> {
175 fn source(&'a self, edge: &&'a Edge) -> Node {
178 fn target(&'a self, edge: &&'a Edge) -> Node {
183 fn test_input(g: LabelledGraph) -> io::Result<String> {
184 let mut writer = Vec::new();
185 render(&g, &mut writer).unwrap();
186 let mut s = String::new();
187 Read::read_to_string(&mut &*writer, &mut s)?;
191 // All of the tests use raw-strings as the format for the expected outputs,
192 // so that you can cut-and-paste the content into a .dot file yourself to
193 // see what the graphviz visualizer would produce.
197 let labels: Trivial = UnlabelledNodes(0);
198 let r = test_input(LabelledGraph::new("empty_graph", labels, vec![], None));
201 r#"digraph empty_graph {
209 let labels: Trivial = UnlabelledNodes(1);
210 let r = test_input(LabelledGraph::new("single_node", labels, vec![], None));
213 r#"digraph single_node {
221 fn single_node_with_style() {
222 let labels: Trivial = UnlabelledNodes(1);
223 let styles = Some(vec![Style::Dashed]);
224 let r = test_input(LabelledGraph::new("single_node", labels, vec![], styles));
227 r#"digraph single_node {
228 N0[label="N0"][style="dashed"];
236 let labels: Trivial = UnlabelledNodes(2);
237 let result = test_input(LabelledGraph::new(
240 vec![edge(0, 1, "E", Style::None)],
245 r#"digraph single_edge {
255 fn single_edge_with_style() {
256 let labels: Trivial = UnlabelledNodes(2);
257 let result = test_input(LabelledGraph::new(
260 vec![edge(0, 1, "E", Style::Bold)],
265 r#"digraph single_edge {
268 N0 -> N1[label="E"][style="bold"];
275 fn test_some_labelled() {
276 let labels: Trivial = SomeNodesLabelled(vec![Some("A"), None]);
277 let styles = Some(vec![Style::None, Style::Dotted]);
278 let result = test_input(LabelledGraph::new(
279 "test_some_labelled",
281 vec![edge(0, 1, "A-1", Style::None)],
286 r#"digraph test_some_labelled {
288 N1[label="N1"][style="dotted"];
289 N0 -> N1[label="A-1"];
296 fn single_cyclic_node() {
297 let labels: Trivial = UnlabelledNodes(1);
298 let r = test_input(LabelledGraph::new(
299 "single_cyclic_node",
301 vec![edge(0, 0, "E", Style::None)],
306 r#"digraph single_cyclic_node {
316 let labels = AllNodesLabelled(vec!["{x,y}", "{x}", "{y}", "{}"]);
317 let r = test_input(LabelledGraph::new(
321 edge(0, 1, "", Style::None),
322 edge(0, 2, "", Style::None),
323 edge(1, 3, "", Style::None),
324 edge(2, 3, "", Style::None),
330 r#"digraph hasse_diagram {
345 fn left_aligned_text() {
346 let labels = AllNodesLabelled(vec![
359 let mut writer = Vec::new();
361 let g = LabelledGraphWithEscStrs::new(
365 edge(0, 1, "then", Style::None),
366 edge(0, 2, "else", Style::None),
367 edge(1, 3, ";", Style::None),
368 edge(2, 3, ";", Style::None),
372 render(&g, &mut writer).unwrap();
373 let mut r = String::new();
374 Read::read_to_string(&mut &*writer, &mut r).unwrap();
378 r#"digraph syntax_tree {
379 N0[label="if test {\l branch1\l} else {\l branch2\l}\lafterward\l"];
382 N3[label="afterward"];
383 N0 -> N1[label="then"];
384 N0 -> N2[label="else"];
393 fn simple_id_construction() {
394 let id1 = Id::new("hello");
397 Err(..) => panic!("'hello' is not a valid value for id anymore"),
402 fn badly_formatted_id() {
403 let id2 = Id::new("Weird { struct : ure } !!!");
405 Ok(_) => panic!("graphviz id suddenly allows spaces, brackets and stuff"),