2 use super::{Id, Labeller, Nodes, Edges, GraphWalk, render, Style};
3 use super::LabelText::{self, LabelStr, EscStr, HtmlStr};
5 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 {
25 struct LabelledGraph {
26 /// The name for this graph. Used for labeling generated `digraph`.
29 /// Each node is an index into `node_labels`; these labels are
30 /// used as the label text for each node. (The node *names*,
31 /// which are unique identifiers, are derived from their index
34 /// If a node maps to None here, then just use its name as its
36 node_labels: Vec<Option<&'static str>>,
38 node_styles: Vec<Style>,
40 /// Each edge relates a from-index to a to-index along with a
41 /// label; `edges` collects them.
45 // A simple wrapper around LabelledGraph that forces the labels to
46 // be emitted as EscStr.
47 struct LabelledGraphWithEscStrs {
52 AllNodesLabelled(Vec<L>),
53 UnlabelledNodes(usize),
54 SomeNodesLabelled(Vec<Option<L>>),
57 type Trivial = NodeLabels<&'static str>;
59 impl NodeLabels<&'static str> {
60 fn to_opt_strs(self) -> Vec<Option<&'static str>> {
62 UnlabelledNodes(len) => vec![None; len],
63 AllNodesLabelled(lbls) => lbls.into_iter().map(|l| Some(l)).collect(),
64 SomeNodesLabelled(lbls) => lbls.into_iter().collect(),
68 fn len(&self) -> usize {
70 &UnlabelledNodes(len) => len,
71 &AllNodesLabelled(ref lbls) => lbls.len(),
72 &SomeNodesLabelled(ref lbls) => lbls.len(),
78 fn new(name: &'static str,
81 node_styles: Option<Vec<Style>>)
83 let count = node_labels.len();
86 node_labels: node_labels.to_opt_strs(),
88 node_styles: match node_styles {
90 None => vec![Style::None; count],
96 impl LabelledGraphWithEscStrs {
97 fn new(name: &'static str,
100 -> LabelledGraphWithEscStrs {
101 LabelledGraphWithEscStrs { graph: LabelledGraph::new(name, node_labels, edges, None) }
105 fn id_name<'a>(n: &Node) -> Id<'a> {
106 Id::new(format!("N{}", *n)).unwrap()
109 impl<'a> Labeller<'a> for LabelledGraph {
111 type Edge = &'a Edge;
112 fn graph_id(&'a self) -> Id<'a> {
113 Id::new(self.name).unwrap()
115 fn node_id(&'a self, n: &Node) -> Id<'a> {
118 fn node_label(&'a self, n: &Node) -> LabelText<'a> {
119 match self.node_labels[*n] {
120 Some(l) => LabelStr(l.into()),
121 None => LabelStr(id_name(n).name()),
124 fn edge_label(&'a self, e: &&'a Edge) -> LabelText<'a> {
125 LabelStr(e.label.into())
127 fn node_style(&'a self, n: &Node) -> Style {
130 fn edge_style(&'a self, e: &&'a Edge) -> Style {
135 impl<'a> Labeller<'a> for LabelledGraphWithEscStrs {
137 type Edge = &'a Edge;
138 fn graph_id(&'a self) -> Id<'a> {
139 self.graph.graph_id()
141 fn node_id(&'a self, n: &Node) -> Id<'a> {
142 self.graph.node_id(n)
144 fn node_label(&'a self, n: &Node) -> LabelText<'a> {
145 match self.graph.node_label(n) {
146 LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
149 fn edge_label(&'a self, e: &&'a Edge) -> LabelText<'a> {
150 match self.graph.edge_label(e) {
151 LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
156 impl<'a> GraphWalk<'a> for LabelledGraph {
158 type Edge = &'a Edge;
159 fn nodes(&'a self) -> Nodes<'a, Node> {
160 (0..self.node_labels.len()).collect()
162 fn edges(&'a self) -> Edges<'a, &'a Edge> {
163 self.edges.iter().collect()
165 fn source(&'a self, edge: &&'a Edge) -> Node {
168 fn target(&'a self, edge: &&'a Edge) -> Node {
173 impl<'a> GraphWalk<'a> for LabelledGraphWithEscStrs {
175 type Edge = &'a Edge;
176 fn nodes(&'a self) -> Nodes<'a, Node> {
179 fn edges(&'a self) -> Edges<'a, &'a Edge> {
182 fn source(&'a self, edge: &&'a Edge) -> Node {
185 fn target(&'a self, edge: &&'a Edge) -> Node {
190 fn test_input(g: LabelledGraph) -> io::Result<String> {
191 let mut writer = Vec::new();
192 render(&g, &mut writer).unwrap();
193 let mut s = String::new();
194 Read::read_to_string(&mut &*writer, &mut s)?;
198 // All of the tests use raw-strings as the format for the expected outputs,
199 // so that you can cut-and-paste the content into a .dot file yourself to
200 // see what the graphviz visualizer would produce.
204 let labels: Trivial = UnlabelledNodes(0);
205 let r = test_input(LabelledGraph::new("empty_graph", labels, vec![], None));
206 assert_eq!(r.unwrap(),
207 r#"digraph empty_graph {
214 let labels: Trivial = UnlabelledNodes(1);
215 let r = test_input(LabelledGraph::new("single_node", labels, vec![], None));
216 assert_eq!(r.unwrap(),
217 r#"digraph single_node {
224 fn single_node_with_style() {
225 let labels: Trivial = UnlabelledNodes(1);
226 let styles = Some(vec![Style::Dashed]);
227 let r = test_input(LabelledGraph::new("single_node", labels, vec![], styles));
228 assert_eq!(r.unwrap(),
229 r#"digraph single_node {
230 N0[label="N0"][style="dashed"];
237 let labels: Trivial = UnlabelledNodes(2);
238 let result = test_input(LabelledGraph::new("single_edge",
240 vec![edge(0, 1, "E", Style::None)],
242 assert_eq!(result.unwrap(),
243 r#"digraph single_edge {
252 fn single_edge_with_style() {
253 let labels: Trivial = UnlabelledNodes(2);
254 let result = test_input(LabelledGraph::new("single_edge",
256 vec![edge(0, 1, "E", Style::Bold)],
258 assert_eq!(result.unwrap(),
259 r#"digraph single_edge {
262 N0 -> N1[label="E"][style="bold"];
268 fn test_some_labelled() {
269 let labels: Trivial = SomeNodesLabelled(vec![Some("A"), None]);
270 let styles = Some(vec![Style::None, Style::Dotted]);
271 let result = test_input(LabelledGraph::new("test_some_labelled",
273 vec![edge(0, 1, "A-1", Style::None)],
275 assert_eq!(result.unwrap(),
276 r#"digraph test_some_labelled {
278 N1[label="N1"][style="dotted"];
279 N0 -> N1[label="A-1"];
285 fn single_cyclic_node() {
286 let labels: Trivial = UnlabelledNodes(1);
287 let r = test_input(LabelledGraph::new("single_cyclic_node",
289 vec![edge(0, 0, "E", Style::None)],
291 assert_eq!(r.unwrap(),
292 r#"digraph single_cyclic_node {
301 let labels = AllNodesLabelled(vec!["{x,y}", "{x}", "{y}", "{}"]);
302 let r = test_input(LabelledGraph::new("hasse_diagram",
304 vec![edge(0, 1, "", Style::None),
305 edge(0, 2, "", Style::None),
306 edge(1, 3, "", Style::None),
307 edge(2, 3, "", Style::None)],
309 assert_eq!(r.unwrap(),
310 r#"digraph hasse_diagram {
324 fn left_aligned_text() {
325 let labels = AllNodesLabelled(vec![
337 let mut writer = Vec::new();
339 let g = LabelledGraphWithEscStrs::new("syntax_tree",
341 vec![edge(0, 1, "then", Style::None),
342 edge(0, 2, "else", Style::None),
343 edge(1, 3, ";", Style::None),
344 edge(2, 3, ";", Style::None)]);
346 render(&g, &mut writer).unwrap();
347 let mut r = String::new();
348 Read::read_to_string(&mut &*writer, &mut r).unwrap();
351 r#"digraph syntax_tree {
352 N0[label="if test {\l branch1\l} else {\l branch2\l}\lafterward\l"];
355 N3[label="afterward"];
356 N0 -> N1[label="then"];
357 N0 -> N2[label="else"];
365 fn simple_id_construction() {
366 let id1 = Id::new("hello");
369 Err(..) => panic!("'hello' is not a valid value for id anymore"),
374 fn badly_formatted_id() {
375 let id2 = Id::new("Weird { struct : ure } !!!");
377 Ok(_) => panic!("graphviz id suddenly allows spaces, brackets and stuff"),