package issue import "sort" // Graph is the edge list read off the `depends:` metadata — the authoritative // one. Body prose is never walked. func Graph(issues map[string]*Issue) map[string][]string { out := make(map[string][]string, len(issues)) for id, i := range issues { out[id] = append([]string{}, i.Depends...) } return out } // Dependents lists who depends on id — the upward direction. func Dependents(issues map[string]*Issue, id string) []string { var out []string for other, i := range issues { if contains(i.Depends, id) { out = append(out, other) } } sort.Strings(out) return out } // TopoOrder puts dependencies first. // // Cycles are broken deterministically rather than raising: a cycle is a data // problem for the caller to report, not a reason to refuse to order the rest. func TopoOrder(ids []string, edges map[string][]string) []string { const ( open = 1 done = 2 ) state := map[string]int{} var order []string var visit func(string) visit = func(n string) { switch state[n] { case done, open: // open = a back edge; leave it unresolved return } state[n] = open for _, d := range edges[n] { if _, ok := edges[d]; ok { visit(d) } } state[n] = done order = append(order, n) } for _, n := range ids { visit(n) } return order } // FindCycles returns one id list per cycle. Empty when the graph is a DAG. func FindCycles(edges map[string][]string) [][]string { const ( open = 1 done = 2 ) state := map[string]int{} var stack []string var cycles [][]string var visit func(string) visit = func(n string) { state[n] = open stack = append(stack, n) for _, d := range edges[n] { if _, ok := edges[d]; !ok { continue } if state[d] == open { for i, s := range stack { if s == d { cycles = append(cycles, append(append([]string{}, stack[i:]...), d)) break } } } else if state[d] == 0 { visit(d) } } stack = stack[:len(stack)-1] state[n] = done } // Sorted so the report is the same on every run; Go map order is not. ids := make([]string, 0, len(edges)) for n := range edges { ids = append(ids, n) } sort.Strings(ids) for _, n := range ids { if state[n] == 0 { visit(n) } } return cycles }