Skip to content

cleanup the root.go file #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 15, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions internal/cmd/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package cmd

import (
"encoding/json"
"fmt"
"strings"
"time"
)

// displayTableStats displays stats in a table format
func displayTableStats(stats *StatsCollector) {
// ANSI color helpers
green := "\033[32m"
yellow := "\033[33m"
reset := "\033[0m"
colorize := func(s, color string) string {
if noColor {
return s
}
return color + s + reset
}

// Find max repo name length
maxRepoLen := len("Repository")
for _, repoStat := range stats.PerRepoStats {
if l := len(repoStat.RepoName); l > maxRepoLen {
maxRepoLen = l
}
}
if maxRepoLen > 40 {
maxRepoLen = 40 // hard cap for very long repo names
}

repoCol := maxRepoLen
colWidths := []int{repoCol, 14, 20, 12}

// Table border helpers
top := "╭"
sep := "├"
bot := "╰"
for i, w := range colWidths {
top += pad("─", w+2) // +2 for padding spaces
sep += pad("─", w+2)
bot += pad("─", w+2)
if i < len(colWidths)-1 {
top += "┬"
sep += "┼"
bot += "┴"
} else {
top += "╮"
sep += "┤"
bot += "╯"
}
}

headRepo := fmt.Sprintf("%-*s", repoCol, "Repository")
headCombined := fmt.Sprintf("%*s", colWidths[1], "PRs Combined")
headSkipped := fmt.Sprintf("%-*s", colWidths[2], "Skipped")
headStatus := fmt.Sprintf("%-*s", colWidths[3], "Status")
head := fmt.Sprintf(
"│ %-*s │ %s │ %s │ %s │",
repoCol, headRepo,
headCombined,
headSkipped,
headStatus,
)

fmt.Println(top)
fmt.Println(head)
fmt.Println(sep)

for _, repoStat := range stats.PerRepoStats {
status := "OK"
statusColor := green
if repoStat.TotalPRs == 0 {
status = "NO OPEN PRs"
statusColor = green
} else if repoStat.NotEnoughPRs {
status = "NOT ENOUGH"
statusColor = yellow
}

mcColor := green
dnmColor := green
if repoStat.SkippedMergeConf > 0 {
mcColor = yellow
}
if repoStat.SkippedCriteria > 0 {
dnmColor = yellow
}
mcRaw := fmt.Sprintf("%d", repoStat.SkippedMergeConf)
dnmRaw := fmt.Sprintf("%d", repoStat.SkippedCriteria)
skippedRaw := fmt.Sprintf("%s (MC), %s (DNM)", mcRaw, dnmRaw)
skippedPadded := fmt.Sprintf("%-*s", colWidths[2], skippedRaw)
mcIdx := strings.Index(skippedPadded, mcRaw)
dnmIdx := strings.Index(skippedPadded, dnmRaw)
skippedColored := skippedPadded
if mcIdx != -1 {
skippedColored = skippedColored[:mcIdx] + colorize(mcRaw, mcColor) + skippedColored[mcIdx+len(mcRaw):]
}
if dnmIdx != -1 {
dnmIdx = strings.Index(skippedColored, dnmRaw)
skippedColored = skippedColored[:dnmIdx] + colorize(dnmRaw, dnmColor) + skippedColored[dnmIdx+len(dnmRaw):]
}
statusColored := colorize(status, statusColor)
statusColored = fmt.Sprintf("%-*s", colWidths[3]+len(statusColored)-len(status), statusColored)

fmt.Printf(
"│ %-*s │ %s │ %s │ %s │\n",
repoCol, repoStat.RepoName,
fmt.Sprintf("%*d", colWidths[1], repoStat.CombinedCount),
skippedColored,
statusColored,
)
}
fmt.Println(bot)

// Print summary mini-table with proper padding
summaryTop := "╭───────────────┬───────────────┬───────────────────────┬───────────────╮"
summaryHead := "│ Repos │ Combined PRs │ Skipped │ Total PRs │"
summarySep := "├───────────────┼───────────────┼───────────────────────┼───────────────┤"
skippedRaw := fmt.Sprintf("%d (MC), %d (DNM)", stats.PRsSkippedMergeConflict, stats.PRsSkippedCriteria)
summaryRow := fmt.Sprintf(
"│ %-13d │ %-13d │ %-21s │ %-13d │",
stats.ReposProcessed,
stats.PRsCombined,
skippedRaw,
len(stats.CombinedPRLinks),
)
summaryBot := "╰───────────────┴───────────────┴───────────────────────┴───────────────╯"
fmt.Println()
fmt.Println(summaryTop)
fmt.Println(summaryHead)
fmt.Println(summarySep)
fmt.Println(summaryRow)
fmt.Println(summaryBot)

// Print PR links block (blue color)
if len(stats.CombinedPRLinks) > 0 {
blue := "\033[34m"
fmt.Println("\nLinks to Combined PRs:")
for _, link := range stats.CombinedPRLinks {
if noColor {
fmt.Println("-", link)
} else {
fmt.Printf("- %s%s%s\n", blue, link, reset)
}
}
}
fmt.Println()
}

// displayJSONStats displays stats in JSON format
func displayJSONStats(stats *StatsCollector) {
output := map[string]interface{}{
"reposProcessed": stats.ReposProcessed,
"prsCombined": stats.PRsCombined,
"prsSkippedMergeConflict": stats.PRsSkippedMergeConflict,
"prsSkippedCriteria": stats.PRsSkippedCriteria,
"executionTime": stats.EndTime.Sub(stats.StartTime).String(),
"combinedPRLinks": stats.CombinedPRLinks,
"perRepoStats": stats.PerRepoStats,
}
jsonData, _ := json.MarshalIndent(output, "", " ")
fmt.Println(string(jsonData))
}

// displayPlainStats displays stats in plain text format
func displayPlainStats(stats *StatsCollector) {
elapsed := stats.EndTime.Sub(stats.StartTime)
fmt.Printf("Repositories Processed: %d\n", stats.ReposProcessed)
fmt.Printf("PRs Combined: %d\n", stats.PRsCombined)
fmt.Printf("PRs Skipped (Merge Conflicts): %d\n", stats.PRsSkippedMergeConflict)
fmt.Printf("PRs Skipped (Did Not Match): %d\n", stats.PRsSkippedCriteria)
fmt.Printf("Execution Time: %s\n", elapsed.Round(time.Second))

fmt.Println("Links to Combined PRs:")
for _, link := range stats.CombinedPRLinks {
fmt.Println("-", link)
}

fmt.Println("\nPer-Repository Details:")
for _, repoStat := range stats.PerRepoStats {
fmt.Printf(" %s\n", repoStat.RepoName)
if repoStat.NotEnoughPRs {
fmt.Println(" Not enough PRs to combine.")
continue
}
fmt.Printf(" Combined: %d\n", repoStat.CombinedCount)
fmt.Printf(" Skipped (Merge Conflicts): %d\n", repoStat.SkippedMergeConf)
fmt.Printf(" Skipped (Did Not Match): %d\n", repoStat.SkippedCriteria)
if repoStat.CombinedPRLink != "" {
fmt.Printf(" Combined PR: %s\n", repoStat.CombinedPRLink)
}
}
}

// pad returns a string of n runes of s (usually "─")
func pad(s string, n int) string {
if n <= 0 {
return ""
}
out := ""
for i := 0; i < n; i++ {
out += s
}
return out
}
111 changes: 111 additions & 0 deletions internal/cmd/output_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package cmd

import (
"testing"
"time"
)

func TestDisplayTableStats(t *testing.T) {
stats := &StatsCollector{
ReposProcessed: 2,
PRsCombined: 5,
PRsSkippedMergeConflict: 1,
PRsSkippedCriteria: 2,
CombinedPRLinks: []string{"http://example.com/pr1", "http://example.com/pr2"},
PerRepoStats: map[string]*RepoStats{
"repo1": {
RepoName: "repo1",
CombinedCount: 3,
SkippedMergeConf: 1,
SkippedCriteria: 0,
CombinedPRLink: "http://example.com/pr1",
NotEnoughPRs: false,
TotalPRs: 5,
},
"repo2": {
RepoName: "repo2",
CombinedCount: 2,
SkippedMergeConf: 0,
SkippedCriteria: 2,
CombinedPRLink: "http://example.com/pr2",
NotEnoughPRs: false,
TotalPRs: 4,
},
},
StartTime: time.Now(),
EndTime: time.Now().Add(2 * time.Minute),
}

displayTableStats(stats)
// Add assertions or manual verification as needed
}

func TestDisplayJSONStats(t *testing.T) {
stats := &StatsCollector{
ReposProcessed: 2,
PRsCombined: 5,
PRsSkippedMergeConflict: 1,
PRsSkippedCriteria: 2,
CombinedPRLinks: []string{"http://example.com/pr1", "http://example.com/pr2"},
PerRepoStats: map[string]*RepoStats{
"repo1": {
RepoName: "repo1",
CombinedCount: 3,
SkippedMergeConf: 1,
SkippedCriteria: 0,
CombinedPRLink: "http://example.com/pr1",
NotEnoughPRs: false,
TotalPRs: 5,
},
"repo2": {
RepoName: "repo2",
CombinedCount: 2,
SkippedMergeConf: 0,
SkippedCriteria: 2,
CombinedPRLink: "http://example.com/pr2",
NotEnoughPRs: false,
TotalPRs: 4,
},
},
StartTime: time.Now(),
EndTime: time.Now().Add(2 * time.Minute),
}

displayJSONStats(stats)
// Add assertions or manual verification as needed
}

func TestDisplayPlainStats(t *testing.T) {
stats := &StatsCollector{
ReposProcessed: 2,
PRsCombined: 5,
PRsSkippedMergeConflict: 1,
PRsSkippedCriteria: 2,
CombinedPRLinks: []string{"http://example.com/pr1", "http://example.com/pr2"},
PerRepoStats: map[string]*RepoStats{
"repo1": {
RepoName: "repo1",
CombinedCount: 3,
SkippedMergeConf: 1,
SkippedCriteria: 0,
CombinedPRLink: "http://example.com/pr1",
NotEnoughPRs: false,
TotalPRs: 5,
},
"repo2": {
RepoName: "repo2",
CombinedCount: 2,
SkippedMergeConf: 0,
SkippedCriteria: 2,
CombinedPRLink: "http://example.com/pr2",
NotEnoughPRs: false,
TotalPRs: 4,
},
},
StartTime: time.Now(),
EndTime: time.Now().Add(2 * time.Minute),
}

displayPlainStats(stats)
// Add assertions or manual verification as needed
}
Loading
Oops, something went wrong.