package cmd import ( "fmt" "io" "sort" "sync" "github.com/warkanum/unitdore/config" ) // sortUnits returns a copy of units sorted by order then name; reverse flips both. func sortUnits(units []config.Unit, reverse bool) []config.Unit { out := make([]config.Unit, len(units)) copy(out, units) sort.SliceStable(out, func(i, j int) bool { if out[i].Order != out[j].Order { return (out[i].Order < out[j].Order) != reverse } return (out[i].Name < out[j].Name) != reverse }) return out } // groupByOrder splits order-sorted units into groups sharing the same order. func groupByOrder(sorted []config.Unit) [][]config.Unit { var groups [][]config.Unit for _, u := range sorted { n := len(groups) if n > 0 && groups[n-1][0].Order == u.Order { groups[n-1] = append(groups[n-1], u) continue } groups = append(groups, []config.Unit{u}) } return groups } // runOrdered runs fn for every unit: groups of equal order one after another, // units within a group in parallel. Start delays live in the generated service // files, so a blocking start covers them. Output is printed per group in name // order. Returns the number of units fn succeeded and failed for. func runOrdered(units []config.Unit, w io.Writer, fn func(u config.Unit) (string, error)) (ok, failed int) { for _, g := range groupByOrder(sortUnits(units, false)) { msgs := make([]string, len(g)) errs := make([]error, len(g)) var wg sync.WaitGroup for j, u := range g { wg.Add(1) go func(j int, u config.Unit) { defer wg.Done() msgs[j], errs[j] = fn(u) }(j, u) } wg.Wait() for j, u := range g { if errs[j] != nil { fmt.Fprintf(w, " ✗ failed: %s: %v\n", u.Name, errs[j]) failed++ continue } fmt.Fprintf(w, " ✓ %s\n", msgs[j]) ok++ } } return ok, failed }