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  }