@@ -19,27 +19,33 @@ package cmd
1919import (
2020 "fmt"
2121 "io/ioutil"
22+ "strings"
2223
2324 "github.com/spf13/cobra"
2425)
2526
27+ var dep string
28+
2629var graphCmd = & cobra.Command {
2730 Use : "graph" ,
2831 Short : "Generate a .dot file to be used with Graphviz's dot command." ,
2932 Long : `A graph.dot file will be generated which can be used with Graphviz's dot command.
3033 For example to generate a svg image use:
3134 twopi -Tsvg -o dag.svg graph.dot` ,
3235 RunE : func (cmd * cobra.Command , args []string ) error {
33- depGraph , deps , _ := getDepInfo ()
34- fileContents := "digraph {\n graph [rankdir=TB, overlap=false];\n "
35- for _ , dep := range deps {
36- _ , ok := depGraph [dep ]
37- if ! ok {
38- continue
39- }
40- for _ , neighbour := range depGraph [dep ] {
41- fileContents += fmt .Sprintf ("\" %s\" -> \" %s\" \n " , dep , neighbour )
42- }
36+ depGraph , deps , mainModule := getDepInfo ()
37+ // strict ensures that there is only one edge between two vertices
38+ // overlap = false ensures the vertices don't overlap
39+ fileContents := "strict digraph {\n graph [overlap=false];\n "
40+
41+ // graph to be generated is based around input dep
42+ if dep != "" {
43+ var chains []Chain
44+ var temp Chain
45+ getAllChains (mainModule , depGraph , temp , & chains )
46+ fileContents += getFileContentsForSingleDep (chains , dep )
47+ } else {
48+ fileContents += getFileContentsForAllDeps (deps , depGraph , mainModule )
4349 }
4450 fileContents += "}"
4551 fileContentsByte := []byte (fileContents )
@@ -52,6 +58,86 @@ var graphCmd = &cobra.Command{
5258 },
5359}
5460
61+ // find all possible chains starting from currentDep
62+ func getAllChains (currentDep string , graph map [string ][]string , currentChain Chain , chains * []Chain ) {
63+ currentChain = append (currentChain , currentDep )
64+ _ , ok := graph [currentDep ]
65+ if ok {
66+ for _ , dep := range graph [currentDep ] {
67+ if ! contains (currentChain , dep ) {
68+ cpy := make (Chain , len (currentChain ))
69+ copy (cpy , currentChain )
70+ getAllChains (dep , graph , cpy , chains )
71+ } else {
72+ * chains = append (* chains , currentChain )
73+ }
74+ }
75+ } else {
76+ * chains = append (* chains , currentChain )
77+ }
78+ }
79+
80+ // get the contents of the .dot file for the graph
81+ // when the -d flag is set
82+ func getFileContentsForSingleDep (chains []Chain , dep string ) string {
83+ // to color the entered node as yellow
84+ data := colorMainNode (dep )
85+
86+ // add all chains which have the input dep to the .dot file
87+ for _ , chain := range chains {
88+ if chainContains (chain , dep ) {
89+ for i := range chain {
90+ if chain [i ] == dep {
91+ chain [i ] = "MainNode"
92+ } else {
93+ chain [i ] = "\" " + chain [i ] + "\" "
94+ }
95+ }
96+ data += strings .Join (chain , " -> " )
97+ data += "\n "
98+ }
99+ }
100+ return data
101+ }
102+
103+ // get the contents of the .dot file for the graph
104+ // of all dependencies (when -d is not set)
105+ func getFileContentsForAllDeps (deps []string , depGraph map [string ][]string , mainModule string ) string {
106+
107+ // color the main module as yellow
108+ data := colorMainNode (mainModule )
109+ for _ , dep := range deps {
110+ _ , ok := depGraph [dep ]
111+ if ! ok {
112+ continue
113+ }
114+ // main module can never be a neighbour
115+ for _ , neighbour := range depGraph [dep ] {
116+ if dep == mainModule {
117+ // for the main module use a colored node
118+ data += fmt .Sprintf ("\" MainNode\" -> \" %s\" \n " , neighbour )
119+ } else {
120+ data += fmt .Sprintf ("\" %s\" -> \" %s\" \n " , dep , neighbour )
121+ }
122+ }
123+ }
124+ return data
125+ }
126+
127+ func chainContains (chain Chain , dep string ) bool {
128+ for _ , d := range chain {
129+ if d == dep {
130+ return true
131+ }
132+ }
133+ return false
134+ }
135+
136+ func colorMainNode (mainNode string ) string {
137+ return fmt .Sprintf ("MainNode [label=\" %s\" , style=\" filled\" color=\" yellow\" ]\n " , mainNode )
138+ }
139+
55140func init () {
56141 rootCmd .AddCommand (graphCmd )
142+ graphCmd .Flags ().StringVarP (& dep , "dep" , "d" , "" , "Specify dependency to create a graph around" )
57143}
0 commit comments