packer/internal/dag/graph.go
Lucas Bajolet 9076c7b24a internal/dag: remove unused code
Since the DAG package was lifted from Terraform, its contents are more
than what we need for now, so this commit cleans-up the package to keep
only the currently needed parts of code.
If we need to support more in the future, we can revert this commit, or
pickup the changes again from Terraform.
2024-10-29 16:10:29 -04:00

227 lines
5.2 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package dag
import (
"bytes"
"fmt"
"sort"
)
// Graph is used to represent a dependency graph.
type Graph struct {
vertices Set
edges Set
downEdges map[interface{}]Set
upEdges map[interface{}]Set
}
// Subgrapher allows a Vertex to be a Graph itself, by returning a Grapher.
type Subgrapher interface {
Subgraph() Grapher
}
// A Grapher is any type that returns a Grapher, mainly used to identify
// dag.Graph and dag.AcyclicGraph. In the case of Graph and AcyclicGraph, they
// return themselves.
type Grapher interface {
DirectedGraph() Grapher
}
// Vertex of the graph.
type Vertex interface{}
// NamedVertex is an optional interface that can be implemented by Vertex
// to give it a human-friendly name that is used for outputting the graph.
type NamedVertex interface {
Vertex
Name() string
}
func (g *Graph) DirectedGraph() Grapher {
return g
}
// Vertices returns the list of all the vertices in the graph.
func (g *Graph) Vertices() []Vertex {
result := make([]Vertex, 0, len(g.vertices))
for _, v := range g.vertices {
result = append(result, v.(Vertex))
}
return result
}
// Edges returns the list of all the edges in the graph.
func (g *Graph) Edges() []Edge {
result := make([]Edge, 0, len(g.edges))
for _, v := range g.edges {
result = append(result, v.(Edge))
}
return result
}
// EdgesFrom returns the list of edges from the given source.
func (g *Graph) EdgesFrom(v Vertex) []Edge {
var result []Edge
from := hashcode(v)
for _, e := range g.Edges() {
if hashcode(e.Source()) == from {
result = append(result, e)
}
}
return result
}
// EdgesTo returns the list of edges to the given target.
func (g *Graph) EdgesTo(v Vertex) []Edge {
var result []Edge
search := hashcode(v)
for _, e := range g.Edges() {
if hashcode(e.Target()) == search {
result = append(result, e)
}
}
return result
}
// HasVertex checks if the given Vertex is present in the graph.
func (g *Graph) HasVertex(v Vertex) bool {
return g.vertices.Include(v)
}
// HasEdge checks if the given Edge is present in the graph.
func (g *Graph) HasEdge(e Edge) bool {
return g.edges.Include(e)
}
// Add adds a vertex to the graph. This is safe to call multiple time with
// the same Vertex.
func (g *Graph) Add(v Vertex) Vertex {
g.init()
g.vertices.Add(v)
return v
}
// downEdgesNoCopy returns the vertices targeted by edges from the source Vertex
// v as a Set. This Set is the same as used internally by the Graph to prevent a
// copy, and must not be modified by the caller.
func (g *Graph) downEdgesNoCopy(v Vertex) Set {
g.init()
return g.downEdges[hashcode(v)]
}
// upEdgesNoCopy returns the vertices that are sources of edges targeting the
// destination Vertex v as a Set. This Set is the same as used internally by the
// Graph to prevent a copy, and must not be modified by the caller.
func (g *Graph) upEdgesNoCopy(v Vertex) Set {
g.init()
return g.upEdges[hashcode(v)]
}
// Connect adds an edge with the given source and target. This is safe to
// call multiple times with the same value. Note that the same value is
// verified through pointer equality of the vertices, not through the
// value of the edge itself.
func (g *Graph) Connect(edge Edge) {
g.init()
source := edge.Source()
target := edge.Target()
sourceCode := hashcode(source)
targetCode := hashcode(target)
// Do we have this already? If so, don't add it again.
if s, ok := g.downEdges[sourceCode]; ok && s.Include(target) {
return
}
// Add the edge to the set
g.edges.Add(edge)
// Add the down edge
s, ok := g.downEdges[sourceCode]
if !ok {
s = make(Set)
g.downEdges[sourceCode] = s
}
s.Add(target)
// Add the up edge
s, ok = g.upEdges[targetCode]
if !ok {
s = make(Set)
g.upEdges[targetCode] = s
}
s.Add(source)
}
// String outputs some human-friendly output for the graph structure.
func (g *Graph) String() string {
var buf bytes.Buffer
// Build the list of node names and a mapping so that we can more
// easily alphabetize the output to remain deterministic.
vertices := g.Vertices()
names := make([]string, 0, len(vertices))
mapping := make(map[string]Vertex, len(vertices))
for _, v := range vertices {
name := VertexName(v)
names = append(names, name)
mapping[name] = v
}
sort.Strings(names)
// Write each node in order...
for _, name := range names {
v := mapping[name]
targets := g.downEdges[hashcode(v)]
buf.WriteString(fmt.Sprintf("%s\n", name))
// Alphabetize dependencies
deps := make([]string, 0, targets.Len())
for _, target := range targets {
deps = append(deps, VertexName(target))
}
sort.Strings(deps)
// Write dependencies
for _, d := range deps {
buf.WriteString(fmt.Sprintf(" %s\n", d))
}
}
return buf.String()
}
func (g *Graph) init() {
if g.vertices == nil {
g.vertices = make(Set)
}
if g.edges == nil {
g.edges = make(Set)
}
if g.downEdges == nil {
g.downEdges = make(map[interface{}]Set)
}
if g.upEdges == nil {
g.upEdges = make(map[interface{}]Set)
}
}
// VertexName returns the name of a vertex.
func VertexName(raw Vertex) string {
switch v := raw.(type) {
case NamedVertex:
return v.Name()
case fmt.Stringer:
return v.String()
default:
return fmt.Sprintf("%v", v)
}
}