github.com/hashicorp/packer@v1.14.3/internal/dag/graph.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package dag 5 6 import ( 7 "bytes" 8 "fmt" 9 "sort" 10 ) 11 12 // Graph is used to represent a dependency graph. 13 type Graph struct { 14 vertices Set 15 edges Set 16 downEdges map[interface{}]Set 17 upEdges map[interface{}]Set 18 } 19 20 // Subgrapher allows a Vertex to be a Graph itself, by returning a Grapher. 21 type Subgrapher interface { 22 Subgraph() Grapher 23 } 24 25 // A Grapher is any type that returns a Grapher, mainly used to identify 26 // dag.Graph and dag.AcyclicGraph. In the case of Graph and AcyclicGraph, they 27 // return themselves. 28 type Grapher interface { 29 DirectedGraph() Grapher 30 } 31 32 // Vertex of the graph. 33 type Vertex interface{} 34 35 // NamedVertex is an optional interface that can be implemented by Vertex 36 // to give it a human-friendly name that is used for outputting the graph. 37 type NamedVertex interface { 38 Vertex 39 Name() string 40 } 41 42 func (g *Graph) DirectedGraph() Grapher { 43 return g 44 } 45 46 // Vertices returns the list of all the vertices in the graph. 47 func (g *Graph) Vertices() []Vertex { 48 result := make([]Vertex, 0, len(g.vertices)) 49 for _, v := range g.vertices { 50 result = append(result, v.(Vertex)) 51 } 52 53 return result 54 } 55 56 // Edges returns the list of all the edges in the graph. 57 func (g *Graph) Edges() []Edge { 58 result := make([]Edge, 0, len(g.edges)) 59 for _, v := range g.edges { 60 result = append(result, v.(Edge)) 61 } 62 63 return result 64 } 65 66 // EdgesFrom returns the list of edges from the given source. 67 func (g *Graph) EdgesFrom(v Vertex) []Edge { 68 var result []Edge 69 from := hashcode(v) 70 for _, e := range g.Edges() { 71 if hashcode(e.Source()) == from { 72 result = append(result, e) 73 } 74 } 75 76 return result 77 } 78 79 // EdgesTo returns the list of edges to the given target. 80 func (g *Graph) EdgesTo(v Vertex) []Edge { 81 var result []Edge 82 search := hashcode(v) 83 for _, e := range g.Edges() { 84 if hashcode(e.Target()) == search { 85 result = append(result, e) 86 } 87 } 88 89 return result 90 } 91 92 // HasVertex checks if the given Vertex is present in the graph. 93 func (g *Graph) HasVertex(v Vertex) bool { 94 return g.vertices.Include(v) 95 } 96 97 // HasEdge checks if the given Edge is present in the graph. 98 func (g *Graph) HasEdge(e Edge) bool { 99 return g.edges.Include(e) 100 } 101 102 // Add adds a vertex to the graph. This is safe to call multiple time with 103 // the same Vertex. 104 func (g *Graph) Add(v Vertex) Vertex { 105 g.init() 106 g.vertices.Add(v) 107 return v 108 } 109 110 // downEdgesNoCopy returns the vertices targeted by edges from the source Vertex 111 // v as a Set. This Set is the same as used internally by the Graph to prevent a 112 // copy, and must not be modified by the caller. 113 func (g *Graph) downEdgesNoCopy(v Vertex) Set { 114 g.init() 115 return g.downEdges[hashcode(v)] 116 } 117 118 // upEdgesNoCopy returns the vertices that are sources of edges targeting the 119 // destination Vertex v as a Set. This Set is the same as used internally by the 120 // Graph to prevent a copy, and must not be modified by the caller. 121 func (g *Graph) upEdgesNoCopy(v Vertex) Set { 122 g.init() 123 return g.upEdges[hashcode(v)] 124 } 125 126 // Connect adds an edge with the given source and target. This is safe to 127 // call multiple times with the same value. Note that the same value is 128 // verified through pointer equality of the vertices, not through the 129 // value of the edge itself. 130 func (g *Graph) Connect(edge Edge) { 131 g.init() 132 133 source := edge.Source() 134 target := edge.Target() 135 sourceCode := hashcode(source) 136 targetCode := hashcode(target) 137 138 // Do we have this already? If so, don't add it again. 139 if s, ok := g.downEdges[sourceCode]; ok && s.Include(target) { 140 return 141 } 142 143 // Add the edge to the set 144 g.edges.Add(edge) 145 146 // Add the down edge 147 s, ok := g.downEdges[sourceCode] 148 if !ok { 149 s = make(Set) 150 g.downEdges[sourceCode] = s 151 } 152 s.Add(target) 153 154 // Add the up edge 155 s, ok = g.upEdges[targetCode] 156 if !ok { 157 s = make(Set) 158 g.upEdges[targetCode] = s 159 } 160 s.Add(source) 161 } 162 163 // String outputs some human-friendly output for the graph structure. 164 func (g *Graph) String() string { 165 var buf bytes.Buffer 166 167 // Build the list of node names and a mapping so that we can more 168 // easily alphabetize the output to remain deterministic. 169 vertices := g.Vertices() 170 names := make([]string, 0, len(vertices)) 171 mapping := make(map[string]Vertex, len(vertices)) 172 for _, v := range vertices { 173 name := VertexName(v) 174 names = append(names, name) 175 mapping[name] = v 176 } 177 sort.Strings(names) 178 179 // Write each node in order... 180 for _, name := range names { 181 v := mapping[name] 182 targets := g.downEdges[hashcode(v)] 183 184 buf.WriteString(fmt.Sprintf("%s\n", name)) 185 186 // Alphabetize dependencies 187 deps := make([]string, 0, targets.Len()) 188 for _, target := range targets { 189 deps = append(deps, VertexName(target)) 190 } 191 sort.Strings(deps) 192 193 // Write dependencies 194 for _, d := range deps { 195 buf.WriteString(fmt.Sprintf(" %s\n", d)) 196 } 197 } 198 199 return buf.String() 200 } 201 202 func (g *Graph) init() { 203 if g.vertices == nil { 204 g.vertices = make(Set) 205 } 206 if g.edges == nil { 207 g.edges = make(Set) 208 } 209 if g.downEdges == nil { 210 g.downEdges = make(map[interface{}]Set) 211 } 212 if g.upEdges == nil { 213 g.upEdges = make(map[interface{}]Set) 214 } 215 } 216 217 // VertexName returns the name of a vertex. 218 func VertexName(raw Vertex) string { 219 switch v := raw.(type) { 220 case NamedVertex: 221 return v.Name() 222 case fmt.Stringer: 223 return v.String() 224 default: 225 return fmt.Sprintf("%v", v) 226 } 227 }