Skip to content

Commit 6c88aed

Browse files
committed
Auto merge of #27044 - nrc:graphviz-style, r=@pnkfelix
r? @pnkfelix
2 parents 5e3b756 + 75f8f96 commit 6c88aed

File tree

1 file changed

+157
-32
lines changed

1 file changed

+157
-32
lines changed

src/libgraphviz/lib.rs

+157-32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -312,6 +312,40 @@ pub enum LabelText<'a> {
312312
EscStr(Cow<'a, str>),
313313
}
314314

315+
/// The style for a node or edge.
316+
/// See http://www.graphviz.org/doc/info/attrs.html#k:style for descriptions.
317+
/// Note that some of these are not valid for edges.
318+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
319+
pub enum Style {
320+
None,
321+
Solid,
322+
Dashed,
323+
Dotted,
324+
Bold,
325+
Rounded,
326+
Diagonals,
327+
Filled,
328+
Striped,
329+
Wedged,
330+
}
331+
332+
impl Style {
333+
pub fn as_slice(self) -> &'static str {
334+
match self {
335+
Style::None => "",
336+
Style::Solid => "solid",
337+
Style::Dashed => "dashed",
338+
Style::Dotted => "dotted",
339+
Style::Bold => "bold",
340+
Style::Rounded => "rounded",
341+
Style::Diagonals => "diagonals",
342+
Style::Filled => "filled",
343+
Style::Striped => "striped",
344+
Style::Wedged => "wedged",
345+
}
346+
}
347+
}
348+
315349
// There is a tension in the design of the labelling API.
316350
//
317351
// For example, I considered making a `Labeller<T>` trait that
@@ -430,6 +464,16 @@ pub trait Labeller<'a,N,E> {
430464
let _ignored = e;
431465
LabelStr("".into_cow())
432466
}
467+
468+
/// Maps `n` to a style that will be used in the rendered output.
469+
fn node_style(&'a self, _n: &N) -> Style {
470+
Style::None
471+
}
472+
473+
/// Maps `e` to a style that will be used in the rendered output.
474+
fn edge_style(&'a self, _e: &E) -> Style {
475+
Style::None
476+
}
433477
}
434478

435479
impl<'a> LabelText<'a> {
@@ -529,6 +573,8 @@ pub trait GraphWalk<'a, N, E> {
529573
pub enum RenderOption {
530574
NoEdgeLabels,
531575
NoNodeLabels,
576+
NoEdgeStyles,
577+
NoNodeStyles,
532578
}
533579

534580
/// Returns vec holding all the default render options.
@@ -562,30 +608,53 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N
562608
for n in g.nodes().iter() {
563609
try!(indent(w));
564610
let id = g.node_id(n);
565-
if options.contains(&RenderOption::NoNodeLabels) {
566-
try!(writeln(w, &[id.as_slice(), ";"]));
567-
} else {
568-
let escaped = g.node_label(n).escape();
569-
try!(writeln(w, &[id.as_slice(),
570-
"[label=\"", &escaped, "\"];"]));
611+
612+
let escaped = &g.node_label(n).escape();
613+
614+
let mut text = vec![id.as_slice()];
615+
616+
if !options.contains(&RenderOption::NoNodeLabels) {
617+
text.push("[label=\"");
618+
text.push(escaped);
619+
text.push("\"]");
620+
}
621+
622+
let style = g.node_style(n);
623+
if !options.contains(&RenderOption::NoNodeStyles) && style != Style::None {
624+
text.push("[style=\"");
625+
text.push(style.as_slice());
626+
text.push("\"]");
571627
}
628+
629+
text.push(";");
630+
try!(writeln(w, &text));
572631
}
573632

574633
for e in g.edges().iter() {
575-
let escaped_label = g.edge_label(e).escape();
634+
let escaped_label = &g.edge_label(e).escape();
576635
try!(indent(w));
577636
let source = g.source(e);
578637
let target = g.target(e);
579638
let source_id = g.node_id(&source);
580639
let target_id = g.node_id(&target);
581-
if options.contains(&RenderOption::NoEdgeLabels) {
582-
try!(writeln(w, &[source_id.as_slice(),
583-
" -> ", target_id.as_slice(), ";"]));
584-
} else {
585-
try!(writeln(w, &[source_id.as_slice(),
586-
" -> ", target_id.as_slice(),
587-
"[label=\"", &escaped_label, "\"];"]));
640+
641+
let mut text = vec![source_id.as_slice(), " -> ", target_id.as_slice()];
642+
643+
if !options.contains(&RenderOption::NoEdgeLabels) {
644+
text.push("[label=\"");
645+
text.push(escaped_label);
646+
text.push("\"]");
647+
}
648+
649+
let style = g.edge_style(e);
650+
if !options.contains(&RenderOption::NoEdgeStyles) && style != Style::None {
651+
text.push("[style=\"");
652+
text.push(style.as_slice());
653+
text.push("\"]");
588654
}
655+
656+
text.push(";");
657+
try!(writeln(w, &text));
589658
}
590659

591660
writeln(w, &["}"])
@@ -594,7 +663,7 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N
594663
#[cfg(test)]
595664
mod tests {
596665
use self::NodeLabels::*;
597-
use super::{Id, Labeller, Nodes, Edges, GraphWalk, render};
666+
use super::{Id, Labeller, Nodes, Edges, GraphWalk, render, Style};
598667
use super::LabelText::{self, LabelStr, EscStr};
599668
use std::io;
600669
use std::io::prelude::*;
@@ -603,11 +672,14 @@ mod tests {
603672
/// each node is an index in a vector in the graph.
604673
type Node = usize;
605674
struct Edge {
606-
from: usize, to: usize, label: &'static str
675+
from: usize,
676+
to: usize,
677+
label: &'static str,
678+
style: Style,
607679
}
608680

609-
fn edge(from: usize, to: usize, label: &'static str) -> Edge {
610-
Edge { from: from, to: to, label: label }
681+
fn edge(from: usize, to: usize, label: &'static str, style: Style) -> Edge {
682+
Edge { from: from, to: to, label: label, style: style }
611683
}
612684

613685
struct LabelledGraph {
@@ -623,6 +695,8 @@ mod tests {
623695
/// text.
624696
node_labels: Vec<Option<&'static str>>,
625697

698+
node_styles: Vec<Style>,
699+
626700
/// Each edge relates a from-index to a to-index along with a
627701
/// label; `edges` collects them.
628702
edges: Vec<Edge>,
@@ -654,16 +728,30 @@ mod tests {
654728
=> lbls.into_iter().collect(),
655729
}
656730
}
731+
732+
fn len(&self) -> usize {
733+
match self {
734+
&UnlabelledNodes(len) => len,
735+
&AllNodesLabelled(ref lbls) => lbls.len(),
736+
&SomeNodesLabelled(ref lbls) => lbls.len(),
737+
}
738+
}
657739
}
658740

659741
impl LabelledGraph {
660742
fn new(name: &'static str,
661743
node_labels: Trivial,
662-
edges: Vec<Edge>) -> LabelledGraph {
744+
edges: Vec<Edge>,
745+
node_styles: Option<Vec<Style>>) -> LabelledGraph {
746+
let count = node_labels.len();
663747
LabelledGraph {
664748
name: name,
665749
node_labels: node_labels.to_opt_strs(),
666-
edges: edges
750+
edges: edges,
751+
node_styles: match node_styles {
752+
Some(nodes) => nodes,
753+
None => vec![Style::None; count],
754+
}
667755
}
668756
}
669757
}
@@ -673,7 +761,10 @@ mod tests {
673761
node_labels: Trivial,
674762
edges: Vec<Edge>) -> LabelledGraphWithEscStrs {
675763
LabelledGraphWithEscStrs {
676-
graph: LabelledGraph::new(name, node_labels, edges)
764+
graph: LabelledGraph::new(name,
765+
node_labels,
766+
edges,
767+
None)
677768
}
678769
}
679770
}
@@ -698,6 +789,12 @@ mod tests {
698789
fn edge_label(&'a self, e: & &'a Edge) -> LabelText<'a> {
699790
LabelStr(e.label.into_cow())
700791
}
792+
fn node_style(&'a self, n: &Node) -> Style {
793+
self.node_styles[*n]
794+
}
795+
fn edge_style(&'a self, e: & &'a Edge) -> Style {
796+
e.style
797+
}
701798
}
702799

703800
impl<'a> Labeller<'a, Node, &'a Edge> for LabelledGraphWithEscStrs {
@@ -760,7 +857,7 @@ mod tests {
760857
#[test]
761858
fn empty_graph() {
762859
let labels : Trivial = UnlabelledNodes(0);
763-
let r = test_input(LabelledGraph::new("empty_graph", labels, vec!()));
860+
let r = test_input(LabelledGraph::new("empty_graph", labels, vec![], None));
764861
assert_eq!(r.unwrap(),
765862
r#"digraph empty_graph {
766863
}
@@ -770,19 +867,31 @@ r#"digraph empty_graph {
770867
#[test]
771868
fn single_node() {
772869
let labels : Trivial = UnlabelledNodes(1);
773-
let r = test_input(LabelledGraph::new("single_node", labels, vec!()));
870+
let r = test_input(LabelledGraph::new("single_node", labels, vec![], None));
774871
assert_eq!(r.unwrap(),
775872
r#"digraph single_node {
776873
N0[label="N0"];
777874
}
778875
"#);
779876
}
780877

878+
#[test]
879+
fn single_node_with_style() {
880+
let labels : Trivial = UnlabelledNodes(1);
881+
let styles = Some(vec![Style::Dashed]);
882+
let r = test_input(LabelledGraph::new("single_node", labels, vec![], styles));
883+
assert_eq!(r.unwrap(),
884+
r#"digraph single_node {
885+
N0[label="N0"][style="dashed"];
886+
}
887+
"#);
888+
}
889+
781890
#[test]
782891
fn single_edge() {
783892
let labels : Trivial = UnlabelledNodes(2);
784893
let result = test_input(LabelledGraph::new("single_edge", labels,
785-
vec!(edge(0, 1, "E"))));
894+
vec![edge(0, 1, "E", Style::None)], None));
786895
assert_eq!(result.unwrap(),
787896
r#"digraph single_edge {
788897
N0[label="N0"];
@@ -792,15 +901,30 @@ r#"digraph single_edge {
792901
"#);
793902
}
794903

904+
#[test]
905+
fn single_edge_with_style() {
906+
let labels : Trivial = UnlabelledNodes(2);
907+
let result = test_input(LabelledGraph::new("single_edge", labels,
908+
vec![edge(0, 1, "E", Style::Bold)], None));
909+
assert_eq!(result.unwrap(),
910+
r#"digraph single_edge {
911+
N0[label="N0"];
912+
N1[label="N1"];
913+
N0 -> N1[label="E"][style="bold"];
914+
}
915+
"#);
916+
}
917+
795918
#[test]
796919
fn test_some_labelled() {
797920
let labels : Trivial = SomeNodesLabelled(vec![Some("A"), None]);
921+
let styles = Some(vec![Style::None, Style::Dotted]);
798922
let result = test_input(LabelledGraph::new("test_some_labelled", labels,
799-
vec![edge(0, 1, "A-1")]));
923+
vec![edge(0, 1, "A-1", Style::None)], styles));
800924
assert_eq!(result.unwrap(),
801925
r#"digraph test_some_labelled {
802926
N0[label="A"];
803-
N1[label="N1"];
927+
N1[label="N1"][style="dotted"];
804928
N0 -> N1[label="A-1"];
805929
}
806930
"#);
@@ -810,7 +934,7 @@ r#"digraph test_some_labelled {
810934
fn single_cyclic_node() {
811935
let labels : Trivial = UnlabelledNodes(1);
812936
let r = test_input(LabelledGraph::new("single_cyclic_node", labels,
813-
vec!(edge(0, 0, "E"))));
937+
vec![edge(0, 0, "E", Style::None)], None));
814938
assert_eq!(r.unwrap(),
815939
r#"digraph single_cyclic_node {
816940
N0[label="N0"];
@@ -824,8 +948,9 @@ r#"digraph single_cyclic_node {
824948
let labels = AllNodesLabelled(vec!("{x,y}", "{x}", "{y}", "{}"));
825949
let r = test_input(LabelledGraph::new(
826950
"hasse_diagram", labels,
827-
vec!(edge(0, 1, ""), edge(0, 2, ""),
828-
edge(1, 3, ""), edge(2, 3, ""))));
951+
vec![edge(0, 1, "", Style::None), edge(0, 2, "", Style::None),
952+
edge(1, 3, "", Style::None), edge(2, 3, "", Style::None)],
953+
None));
829954
assert_eq!(r.unwrap(),
830955
r#"digraph hasse_diagram {
831956
N0[label="{x,y}"];
@@ -858,8 +983,8 @@ r#"digraph hasse_diagram {
858983

859984
let g = LabelledGraphWithEscStrs::new(
860985
"syntax_tree", labels,
861-
vec!(edge(0, 1, "then"), edge(0, 2, "else"),
862-
edge(1, 3, ";"), edge(2, 3, ";" )));
986+
vec![edge(0, 1, "then", Style::None), edge(0, 2, "else", Style::None),
987+
edge(1, 3, ";", Style::None), edge(2, 3, ";", Style::None)]);
863988

864989
render(&g, &mut writer).unwrap();
865990
let mut r = String::new();

0 commit comments

Comments
 (0)