-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
filter.go
96 lines (79 loc) · 2.64 KB
/
filter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package graph
import (
"fmt"
"strings"
"github.com/gonum/graph/simple"
)
// FilterPackages receives a graph and a set of packagePrefixes contained within the graph.
// Returns a new graph with the sub-tree for each node matching the packagePrefix collapsed
// into just that node. Relationships among packagePrefixes are kept: edges originating from
// packagePrefix subpackages are re-written to originate from the packagePrefix, and edges
// terminating at packagePrefix subpackages are re-written to terminate at the packagePrefix.
func FilterPackages(g *MutableDirectedGraph, packagePrefixes []string) (*MutableDirectedGraph, error) {
collapsedGraph := NewMutableDirectedGraph(g.rootNodeNames)
// copy all nodes to new graph
for _, n := range g.Nodes() {
node, ok := n.(*Node)
if !ok {
continue
}
collapsedNodeName := getFilteredNodeName(packagePrefixes, node.UniqueName)
_, exists := collapsedGraph.NodeByName(collapsedNodeName)
if exists {
continue
}
err := collapsedGraph.AddNode(&Node{
UniqueName: collapsedNodeName,
Id: n.ID(),
})
if err != nil {
return nil, err
}
}
// add edges to collapsed graph
for _, from := range g.Nodes() {
node, ok := from.(*Node)
if !ok {
return nil, fmt.Errorf("expected nodes in graph to be of type *Node")
}
fromNodeName := getFilteredNodeName(packagePrefixes, node.UniqueName)
fromNode, exists := collapsedGraph.NodeByName(fromNodeName)
if !exists {
return nil, fmt.Errorf("expected node with name %q to exist in collapsed graph", fromNodeName)
}
for _, to := range g.From(from) {
node, ok := to.(*Node)
if !ok {
return nil, fmt.Errorf("expected nodes in graph to be of type *Node")
}
toNodeName := getFilteredNodeName(packagePrefixes, node.UniqueName)
if fromNodeName == toNodeName {
continue
}
toNode, exists := collapsedGraph.NodeByName(toNodeName)
if !exists {
return nil, fmt.Errorf("expected node with name %q to exist in collapsed graph", toNodeName)
}
if collapsedGraph.HasEdgeFromTo(fromNode, toNode) {
continue
}
collapsedGraph.SetEdge(simple.Edge{F: fromNode, T: toNode})
}
}
return collapsedGraph, nil
}
func getFilteredNodeName(collapsedPrefixes []string, packageName string) string {
for _, prefix := range collapsedPrefixes {
// ensure that each prefix ends in a slash
// otherwise, we will incorrectly squash packages
// like "api" and "apimachinery" into eachother.
prefixWithSlash := prefix
if string(prefix[len(prefix)-1]) != "/" {
prefixWithSlash = prefixWithSlash + "/"
}
if strings.HasPrefix(packageName+"/", prefixWithSlash) {
return prefix
}
}
return packageName
}