]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/dag/dot.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / dag / dot.go
1 package dag
2
3 import (
4 "bytes"
5 "fmt"
6 "sort"
7 "strings"
8 )
9
10 // DotOpts are the options for generating a dot formatted Graph.
11 type DotOpts struct {
12 // Allows some nodes to decide to only show themselves when the user has
13 // requested the "verbose" graph.
14 Verbose bool
15
16 // Highlight Cycles
17 DrawCycles bool
18
19 // How many levels to expand modules as we draw
20 MaxDepth int
21
22 // use this to keep the cluster_ naming convention from the previous dot writer
23 cluster bool
24 }
25
26 // GraphNodeDotter can be implemented by a node to cause it to be included
27 // in the dot graph. The Dot method will be called which is expected to
28 // return a representation of this node.
29 type GraphNodeDotter interface {
30 // Dot is called to return the dot formatting for the node.
31 // The first parameter is the title of the node.
32 // The second parameter includes user-specified options that affect the dot
33 // graph. See GraphDotOpts below for details.
34 DotNode(string, *DotOpts) *DotNode
35 }
36
37 // DotNode provides a structure for Vertices to return in order to specify their
38 // dot format.
39 type DotNode struct {
40 Name string
41 Attrs map[string]string
42 }
43
44 // Returns the DOT representation of this Graph.
45 func (g *marshalGraph) Dot(opts *DotOpts) []byte {
46 if opts == nil {
47 opts = &DotOpts{
48 DrawCycles: true,
49 MaxDepth: -1,
50 Verbose: true,
51 }
52 }
53
54 var w indentWriter
55 w.WriteString("digraph {\n")
56 w.Indent()
57
58 // some dot defaults
59 w.WriteString(`compound = "true"` + "\n")
60 w.WriteString(`newrank = "true"` + "\n")
61
62 // the top level graph is written as the first subgraph
63 w.WriteString(`subgraph "root" {` + "\n")
64 g.writeBody(opts, &w)
65
66 // cluster isn't really used other than for naming purposes in some graphs
67 opts.cluster = opts.MaxDepth != 0
68 maxDepth := opts.MaxDepth
69 if maxDepth == 0 {
70 maxDepth = -1
71 }
72
73 for _, s := range g.Subgraphs {
74 g.writeSubgraph(s, opts, maxDepth, &w)
75 }
76
77 w.Unindent()
78 w.WriteString("}\n")
79 return w.Bytes()
80 }
81
82 func (v *marshalVertex) dot(g *marshalGraph, opts *DotOpts) []byte {
83 var buf bytes.Buffer
84 graphName := g.Name
85 if graphName == "" {
86 graphName = "root"
87 }
88
89 name := v.Name
90 attrs := v.Attrs
91 if v.graphNodeDotter != nil {
92 node := v.graphNodeDotter.DotNode(name, opts)
93 if node == nil {
94 return []byte{}
95 }
96
97 newAttrs := make(map[string]string)
98 for k, v := range attrs {
99 newAttrs[k] = v
100 }
101 for k, v := range node.Attrs {
102 newAttrs[k] = v
103 }
104
105 name = node.Name
106 attrs = newAttrs
107 }
108
109 buf.WriteString(fmt.Sprintf(`"[%s] %s"`, graphName, name))
110 writeAttrs(&buf, attrs)
111 buf.WriteByte('\n')
112
113 return buf.Bytes()
114 }
115
116 func (e *marshalEdge) dot(g *marshalGraph) string {
117 var buf bytes.Buffer
118 graphName := g.Name
119 if graphName == "" {
120 graphName = "root"
121 }
122
123 sourceName := g.vertexByID(e.Source).Name
124 targetName := g.vertexByID(e.Target).Name
125 s := fmt.Sprintf(`"[%s] %s" -> "[%s] %s"`, graphName, sourceName, graphName, targetName)
126 buf.WriteString(s)
127 writeAttrs(&buf, e.Attrs)
128
129 return buf.String()
130 }
131
132 func cycleDot(e *marshalEdge, g *marshalGraph) string {
133 return e.dot(g) + ` [color = "red", penwidth = "2.0"]`
134 }
135
136 // Write the subgraph body. The is recursive, and the depth argument is used to
137 // record the current depth of iteration.
138 func (g *marshalGraph) writeSubgraph(sg *marshalGraph, opts *DotOpts, depth int, w *indentWriter) {
139 if depth == 0 {
140 return
141 }
142 depth--
143
144 name := sg.Name
145 if opts.cluster {
146 // we prefix with cluster_ to match the old dot output
147 name = "cluster_" + name
148 sg.Attrs["label"] = sg.Name
149 }
150 w.WriteString(fmt.Sprintf("subgraph %q {\n", name))
151 sg.writeBody(opts, w)
152
153 for _, sg := range sg.Subgraphs {
154 g.writeSubgraph(sg, opts, depth, w)
155 }
156 }
157
158 func (g *marshalGraph) writeBody(opts *DotOpts, w *indentWriter) {
159 w.Indent()
160
161 for _, as := range attrStrings(g.Attrs) {
162 w.WriteString(as + "\n")
163 }
164
165 // list of Vertices that aren't to be included in the dot output
166 skip := map[string]bool{}
167
168 for _, v := range g.Vertices {
169 if v.graphNodeDotter == nil {
170 skip[v.ID] = true
171 continue
172 }
173
174 w.Write(v.dot(g, opts))
175 }
176
177 var dotEdges []string
178
179 if opts.DrawCycles {
180 for _, c := range g.Cycles {
181 if len(c) < 2 {
182 continue
183 }
184
185 for i, j := 0, 1; i < len(c); i, j = i+1, j+1 {
186 if j >= len(c) {
187 j = 0
188 }
189 src := c[i]
190 tgt := c[j]
191
192 if skip[src.ID] || skip[tgt.ID] {
193 continue
194 }
195
196 e := &marshalEdge{
197 Name: fmt.Sprintf("%s|%s", src.Name, tgt.Name),
198 Source: src.ID,
199 Target: tgt.ID,
200 Attrs: make(map[string]string),
201 }
202
203 dotEdges = append(dotEdges, cycleDot(e, g))
204 src = tgt
205 }
206 }
207 }
208
209 for _, e := range g.Edges {
210 dotEdges = append(dotEdges, e.dot(g))
211 }
212
213 // srot these again to match the old output
214 sort.Strings(dotEdges)
215
216 for _, e := range dotEdges {
217 w.WriteString(e + "\n")
218 }
219
220 w.Unindent()
221 w.WriteString("}\n")
222 }
223
224 func writeAttrs(buf *bytes.Buffer, attrs map[string]string) {
225 if len(attrs) > 0 {
226 buf.WriteString(" [")
227 buf.WriteString(strings.Join(attrStrings(attrs), ", "))
228 buf.WriteString("]")
229 }
230 }
231
232 func attrStrings(attrs map[string]string) []string {
233 strings := make([]string, 0, len(attrs))
234 for k, v := range attrs {
235 strings = append(strings, fmt.Sprintf("%s = %q", k, v))
236 }
237 sort.Strings(strings)
238 return strings
239 }
240
241 // Provide a bytes.Buffer like structure, which will indent when starting a
242 // newline.
243 type indentWriter struct {
244 bytes.Buffer
245 level int
246 }
247
248 func (w *indentWriter) indent() {
249 newline := []byte("\n")
250 if !bytes.HasSuffix(w.Bytes(), newline) {
251 return
252 }
253 for i := 0; i < w.level; i++ {
254 w.Buffer.WriteString("\t")
255 }
256 }
257
258 // Indent increases indentation by 1
259 func (w *indentWriter) Indent() { w.level++ }
260
261 // Unindent decreases indentation by 1
262 func (w *indentWriter) Unindent() { w.level-- }
263
264 // the following methods intercecpt the byte.Buffer writes and insert the
265 // indentation when starting a new line.
266 func (w *indentWriter) Write(b []byte) (int, error) {
267 w.indent()
268 return w.Buffer.Write(b)
269 }
270
271 func (w *indentWriter) WriteString(s string) (int, error) {
272 w.indent()
273 return w.Buffer.WriteString(s)
274 }
275 func (w *indentWriter) WriteByte(b byte) error {
276 w.indent()
277 return w.Buffer.WriteByte(b)
278 }
279 func (w *indentWriter) WriteRune(r rune) (int, error) {
280 w.indent()
281 return w.Buffer.WriteRune(r)
282 }