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
435479impl < ' a > LabelText < ' a > {
@@ -529,6 +573,8 @@ pub trait GraphWalk<'a, N, E> {
529573pub 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) ]
595664mod 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( ) ,
765862r#"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( ) ,
775872r#"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( ) ,
787896r#"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( ) ,
801925r#"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( ) ,
815939r#"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( ) ,
830955r#"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