github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/utils/sort.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package utils provides generic utility functions. 5 package utils 6 7 import "fmt" 8 9 // Dependency is an interface that represents a node in a list of dependencies. 10 type Dependency interface { 11 Name() string 12 Dependencies() []string 13 } 14 15 // SortDependencies performs a topological sort on a dependency graph and 16 // returns a slice of the nodes in order of their precedence. 17 // The input data is a map of nodes to a slice of its dependencies. 18 // 19 // E.g: 20 // A depends on B & C, B depends on C and C has no dependencies: 21 // {"A": ["B", "C"], "B": ["C"], "C": []string{}} 22 // 23 // Note sort order is dependent on the slice order of the input data for 24 // nodes with the same in-degree (i.e. the same number of dependencies). 25 func SortDependencies(data []Dependency) ([]string, error) { 26 // Initialize the in-degree and out-degree maps. 27 inDegree := make(map[string]int) 28 outDegree := make(map[string][]string) 29 30 // Populate the in-degree and out-degree maps. 31 for _, d := range data { 32 outDegree[d.Name()] = d.Dependencies() 33 inDegree[d.Name()] = 0 34 } 35 for _, deps := range data { 36 for _, d := range deps.Dependencies() { 37 inDegree[d]++ 38 } 39 } 40 41 // Initialize the queue and the result list. 42 queue := make([]string, 0) 43 result := make([]string, 0) 44 45 // Enqueue all nodes with zero in-degree. 46 for _, d := range data { 47 if inDegree[d.Name()] == 0 { 48 queue = append(queue, d.Name()) 49 } 50 } 51 52 // Process the queue. 53 for len(queue) > 0 { 54 // Dequeue a node from the queue. 55 node := queue[0] 56 queue = queue[1:] 57 58 // Add the node to the result list. 59 result = append([]string{node}, result...) 60 61 // Decrement the in-degree of all outgoing neighbors. 62 for _, neighbor := range outDegree[node] { 63 inDegree[neighbor]-- 64 // If the neighbor has zero in-degree, enqueue it. 65 if inDegree[neighbor] == 0 { 66 queue = append(queue, neighbor) 67 } 68 } 69 } 70 71 // If there are still nodes with non-zero in-degree, there is a cycle in the graph. 72 // Return an empty result list to indicate this. 73 for _, degree := range inDegree { 74 if degree > 0 { 75 return result, fmt.Errorf("dependency cycle detected") 76 } 77 } 78 79 // Return the result list. 80 return result, nil 81 }