1
- // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
1
+ // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2
2
// file at the top-level directory of this distribution and at
3
3
// http://rust-lang.org/COPYRIGHT.
4
4
//
@@ -312,6 +312,40 @@ pub enum LabelText<'a> {
312
312
EscStr ( Cow < ' a , str > ) ,
313
313
}
314
314
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
+
315
349
// There is a tension in the design of the labelling API.
316
350
//
317
351
// For example, I considered making a `Labeller<T>` trait that
@@ -430,6 +464,16 @@ pub trait Labeller<'a,N,E> {
430
464
let _ignored = e;
431
465
LabelStr ( "" . into_cow ( ) )
432
466
}
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
+ }
433
477
}
434
478
435
479
impl < ' a > LabelText < ' a > {
@@ -529,6 +573,8 @@ pub trait GraphWalk<'a, N, E> {
529
573
pub enum RenderOption {
530
574
NoEdgeLabels ,
531
575
NoNodeLabels ,
576
+ NoEdgeStyles ,
577
+ NoNodeStyles ,
532
578
}
533
579
534
580
/// 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
562
608
for n in g. nodes ( ) . iter ( ) {
563
609
try!( indent ( w) ) ;
564
610
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 ( "\" ]" ) ;
571
627
}
628
+
629
+ text. push ( ";" ) ;
630
+ try!( writeln ( w, & text) ) ;
572
631
}
573
632
574
633
for e in g. edges ( ) . iter ( ) {
575
- let escaped_label = g. edge_label ( e) . escape ( ) ;
634
+ let escaped_label = & g. edge_label ( e) . escape ( ) ;
576
635
try!( indent ( w) ) ;
577
636
let source = g. source ( e) ;
578
637
let target = g. target ( e) ;
579
638
let source_id = g. node_id ( & source) ;
580
639
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 ( "\" ]" ) ;
588
654
}
655
+
656
+ text. push ( ";" ) ;
657
+ try!( writeln ( w, & text) ) ;
589
658
}
590
659
591
660
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
594
663
#[ cfg( test) ]
595
664
mod tests {
596
665
use self :: NodeLabels :: * ;
597
- use super :: { Id , Labeller , Nodes , Edges , GraphWalk , render} ;
666
+ use super :: { Id , Labeller , Nodes , Edges , GraphWalk , render, Style } ;
598
667
use super :: LabelText :: { self , LabelStr , EscStr } ;
599
668
use std:: io;
600
669
use std:: io:: prelude:: * ;
@@ -603,11 +672,14 @@ mod tests {
603
672
/// each node is an index in a vector in the graph.
604
673
type Node = usize ;
605
674
struct Edge {
606
- from : usize , to : usize , label : & ' static str
675
+ from : usize ,
676
+ to : usize ,
677
+ label : & ' static str ,
678
+ style : Style ,
607
679
}
608
680
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 }
611
683
}
612
684
613
685
struct LabelledGraph {
@@ -623,6 +695,8 @@ mod tests {
623
695
/// text.
624
696
node_labels : Vec < Option < & ' static str > > ,
625
697
698
+ node_styles : Vec < Style > ,
699
+
626
700
/// Each edge relates a from-index to a to-index along with a
627
701
/// label; `edges` collects them.
628
702
edges : Vec < Edge > ,
@@ -654,16 +728,30 @@ mod tests {
654
728
=> lbls. into_iter ( ) . collect ( ) ,
655
729
}
656
730
}
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
+ }
657
739
}
658
740
659
741
impl LabelledGraph {
660
742
fn new ( name : & ' static str ,
661
743
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 ( ) ;
663
747
LabelledGraph {
664
748
name : name,
665
749
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
+ }
667
755
}
668
756
}
669
757
}
@@ -673,7 +761,10 @@ mod tests {
673
761
node_labels : Trivial ,
674
762
edges : Vec < Edge > ) -> LabelledGraphWithEscStrs {
675
763
LabelledGraphWithEscStrs {
676
- graph : LabelledGraph :: new ( name, node_labels, edges)
764
+ graph : LabelledGraph :: new ( name,
765
+ node_labels,
766
+ edges,
767
+ None )
677
768
}
678
769
}
679
770
}
@@ -698,6 +789,12 @@ mod tests {
698
789
fn edge_label ( & ' a self , e : & & ' a Edge ) -> LabelText < ' a > {
699
790
LabelStr ( e. label . into_cow ( ) )
700
791
}
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
+ }
701
798
}
702
799
703
800
impl < ' a > Labeller < ' a , Node , & ' a Edge > for LabelledGraphWithEscStrs {
@@ -760,7 +857,7 @@ mod tests {
760
857
#[ test]
761
858
fn empty_graph ( ) {
762
859
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 ) ) ;
764
861
assert_eq ! ( r. unwrap( ) ,
765
862
r#"digraph empty_graph {
766
863
}
@@ -770,19 +867,31 @@ r#"digraph empty_graph {
770
867
#[ test]
771
868
fn single_node ( ) {
772
869
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 ) ) ;
774
871
assert_eq ! ( r. unwrap( ) ,
775
872
r#"digraph single_node {
776
873
N0[label="N0"];
777
874
}
778
875
"# ) ;
779
876
}
780
877
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
+
781
890
#[ test]
782
891
fn single_edge ( ) {
783
892
let labels : Trivial = UnlabelledNodes ( 2 ) ;
784
893
let result = test_input ( LabelledGraph :: new ( "single_edge" , labels,
785
- vec ! ( edge( 0 , 1 , "E" ) ) ) ) ;
894
+ vec ! [ edge( 0 , 1 , "E" , Style :: None ) ] , None ) ) ;
786
895
assert_eq ! ( result. unwrap( ) ,
787
896
r#"digraph single_edge {
788
897
N0[label="N0"];
@@ -792,15 +901,30 @@ r#"digraph single_edge {
792
901
"# ) ;
793
902
}
794
903
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
+
795
918
#[ test]
796
919
fn test_some_labelled ( ) {
797
920
let labels : Trivial = SomeNodesLabelled ( vec ! [ Some ( "A" ) , None ] ) ;
921
+ let styles = Some ( vec ! [ Style :: None , Style :: Dotted ] ) ;
798
922
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 ) ) ;
800
924
assert_eq ! ( result. unwrap( ) ,
801
925
r#"digraph test_some_labelled {
802
926
N0[label="A"];
803
- N1[label="N1"];
927
+ N1[label="N1"][style="dotted"] ;
804
928
N0 -> N1[label="A-1"];
805
929
}
806
930
"# ) ;
@@ -810,7 +934,7 @@ r#"digraph test_some_labelled {
810
934
fn single_cyclic_node ( ) {
811
935
let labels : Trivial = UnlabelledNodes ( 1 ) ;
812
936
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 ) ) ;
814
938
assert_eq ! ( r. unwrap( ) ,
815
939
r#"digraph single_cyclic_node {
816
940
N0[label="N0"];
@@ -824,8 +948,9 @@ r#"digraph single_cyclic_node {
824
948
let labels = AllNodesLabelled ( vec ! ( "{x,y}" , "{x}" , "{y}" , "{}" ) ) ;
825
949
let r = test_input ( LabelledGraph :: new (
826
950
"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 ) ) ;
829
954
assert_eq ! ( r. unwrap( ) ,
830
955
r#"digraph hasse_diagram {
831
956
N0[label="{x,y}"];
@@ -858,8 +983,8 @@ r#"digraph hasse_diagram {
858
983
859
984
let g = LabelledGraphWithEscStrs :: new (
860
985
"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 ) ] ) ;
863
988
864
989
render ( & g, & mut writer) . unwrap ( ) ;
865
990
let mut r = String :: new ( ) ;
0 commit comments