Skip to content

Commit 88b5dd2

Browse files
authored
Merge pull request #19 from github/root-cleanup
cleanup the root.go file
2 parents 8139648 + f557ba8 commit 88b5dd2

File tree

3 files changed

+319
-217
lines changed

3 files changed

+319
-217
lines changed

internal/cmd/output.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package cmd
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"strings"
7+
"time"
8+
)
9+
10+
// displayTableStats displays stats in a table format
11+
func displayTableStats(stats *StatsCollector) {
12+
// ANSI color helpers
13+
green := "\033[32m"
14+
yellow := "\033[33m"
15+
reset := "\033[0m"
16+
colorize := func(s, color string) string {
17+
if noColor {
18+
return s
19+
}
20+
return color + s + reset
21+
}
22+
23+
// Find max repo name length
24+
maxRepoLen := len("Repository")
25+
for _, repoStat := range stats.PerRepoStats {
26+
if l := len(repoStat.RepoName); l > maxRepoLen {
27+
maxRepoLen = l
28+
}
29+
}
30+
if maxRepoLen > 40 {
31+
maxRepoLen = 40 // hard cap for very long repo names
32+
}
33+
34+
repoCol := maxRepoLen
35+
colWidths := []int{repoCol, 14, 20, 12}
36+
37+
// Table border helpers
38+
top := "╭"
39+
sep := "├"
40+
bot := "╰"
41+
for i, w := range colWidths {
42+
top += pad("─", w+2) // +2 for padding spaces
43+
sep += pad("─", w+2)
44+
bot += pad("─", w+2)
45+
if i < len(colWidths)-1 {
46+
top += "┬"
47+
sep += "┼"
48+
bot += "┴"
49+
} else {
50+
top += "╮"
51+
sep += "┤"
52+
bot += "╯"
53+
}
54+
}
55+
56+
headRepo := fmt.Sprintf("%-*s", repoCol, "Repository")
57+
headCombined := fmt.Sprintf("%*s", colWidths[1], "PRs Combined")
58+
headSkipped := fmt.Sprintf("%-*s", colWidths[2], "Skipped")
59+
headStatus := fmt.Sprintf("%-*s", colWidths[3], "Status")
60+
head := fmt.Sprintf(
61+
"│ %-*s │ %s │ %s │ %s │",
62+
repoCol, headRepo,
63+
headCombined,
64+
headSkipped,
65+
headStatus,
66+
)
67+
68+
fmt.Println(top)
69+
fmt.Println(head)
70+
fmt.Println(sep)
71+
72+
for _, repoStat := range stats.PerRepoStats {
73+
status := "OK"
74+
statusColor := green
75+
if repoStat.TotalPRs == 0 {
76+
status = "NO OPEN PRs"
77+
statusColor = green
78+
} else if repoStat.NotEnoughPRs {
79+
status = "NOT ENOUGH"
80+
statusColor = yellow
81+
}
82+
83+
mcColor := green
84+
dnmColor := green
85+
if repoStat.SkippedMergeConf > 0 {
86+
mcColor = yellow
87+
}
88+
if repoStat.SkippedCriteria > 0 {
89+
dnmColor = yellow
90+
}
91+
mcRaw := fmt.Sprintf("%d", repoStat.SkippedMergeConf)
92+
dnmRaw := fmt.Sprintf("%d", repoStat.SkippedCriteria)
93+
skippedRaw := fmt.Sprintf("%s (MC), %s (DNM)", mcRaw, dnmRaw)
94+
skippedPadded := fmt.Sprintf("%-*s", colWidths[2], skippedRaw)
95+
mcIdx := strings.Index(skippedPadded, mcRaw)
96+
dnmIdx := strings.Index(skippedPadded, dnmRaw)
97+
skippedColored := skippedPadded
98+
if mcIdx != -1 {
99+
skippedColored = skippedColored[:mcIdx] + colorize(mcRaw, mcColor) + skippedColored[mcIdx+len(mcRaw):]
100+
}
101+
if dnmIdx != -1 {
102+
dnmIdx = strings.Index(skippedColored, dnmRaw)
103+
skippedColored = skippedColored[:dnmIdx] + colorize(dnmRaw, dnmColor) + skippedColored[dnmIdx+len(dnmRaw):]
104+
}
105+
statusColored := colorize(status, statusColor)
106+
statusColored = fmt.Sprintf("%-*s", colWidths[3]+len(statusColored)-len(status), statusColored)
107+
108+
fmt.Printf(
109+
"│ %-*s │ %s │ %s │ %s │\n",
110+
repoCol, repoStat.RepoName,
111+
fmt.Sprintf("%*d", colWidths[1], repoStat.CombinedCount),
112+
skippedColored,
113+
statusColored,
114+
)
115+
}
116+
fmt.Println(bot)
117+
118+
// Print summary mini-table with proper padding
119+
summaryTop := "╭───────────────┬───────────────┬───────────────────────┬───────────────╮"
120+
summaryHead := "│ Repos │ Combined PRs │ Skipped │ Total PRs │"
121+
summarySep := "├───────────────┼───────────────┼───────────────────────┼───────────────┤"
122+
skippedRaw := fmt.Sprintf("%d (MC), %d (DNM)", stats.PRsSkippedMergeConflict, stats.PRsSkippedCriteria)
123+
summaryRow := fmt.Sprintf(
124+
"│ %-13d │ %-13d │ %-21s │ %-13d │",
125+
stats.ReposProcessed,
126+
stats.PRsCombined,
127+
skippedRaw,
128+
len(stats.CombinedPRLinks),
129+
)
130+
summaryBot := "╰───────────────┴───────────────┴───────────────────────┴───────────────╯"
131+
fmt.Println()
132+
fmt.Println(summaryTop)
133+
fmt.Println(summaryHead)
134+
fmt.Println(summarySep)
135+
fmt.Println(summaryRow)
136+
fmt.Println(summaryBot)
137+
138+
// Print PR links block (blue color)
139+
if len(stats.CombinedPRLinks) > 0 {
140+
blue := "\033[34m"
141+
fmt.Println("\nLinks to Combined PRs:")
142+
for _, link := range stats.CombinedPRLinks {
143+
if noColor {
144+
fmt.Println("-", link)
145+
} else {
146+
fmt.Printf("- %s%s%s\n", blue, link, reset)
147+
}
148+
}
149+
}
150+
fmt.Println()
151+
}
152+
153+
// displayJSONStats displays stats in JSON format
154+
func displayJSONStats(stats *StatsCollector) {
155+
output := map[string]interface{}{
156+
"reposProcessed": stats.ReposProcessed,
157+
"prsCombined": stats.PRsCombined,
158+
"prsSkippedMergeConflict": stats.PRsSkippedMergeConflict,
159+
"prsSkippedCriteria": stats.PRsSkippedCriteria,
160+
"executionTime": stats.EndTime.Sub(stats.StartTime).String(),
161+
"combinedPRLinks": stats.CombinedPRLinks,
162+
"perRepoStats": stats.PerRepoStats,
163+
}
164+
jsonData, _ := json.MarshalIndent(output, "", " ")
165+
fmt.Println(string(jsonData))
166+
}
167+
168+
// displayPlainStats displays stats in plain text format
169+
func displayPlainStats(stats *StatsCollector) {
170+
elapsed := stats.EndTime.Sub(stats.StartTime)
171+
fmt.Printf("Repositories Processed: %d\n", stats.ReposProcessed)
172+
fmt.Printf("PRs Combined: %d\n", stats.PRsCombined)
173+
fmt.Printf("PRs Skipped (Merge Conflicts): %d\n", stats.PRsSkippedMergeConflict)
174+
fmt.Printf("PRs Skipped (Did Not Match): %d\n", stats.PRsSkippedCriteria)
175+
fmt.Printf("Execution Time: %s\n", elapsed.Round(time.Second))
176+
177+
fmt.Println("Links to Combined PRs:")
178+
for _, link := range stats.CombinedPRLinks {
179+
fmt.Println("-", link)
180+
}
181+
182+
fmt.Println("\nPer-Repository Details:")
183+
for _, repoStat := range stats.PerRepoStats {
184+
fmt.Printf(" %s\n", repoStat.RepoName)
185+
if repoStat.NotEnoughPRs {
186+
fmt.Println(" Not enough PRs to combine.")
187+
continue
188+
}
189+
fmt.Printf(" Combined: %d\n", repoStat.CombinedCount)
190+
fmt.Printf(" Skipped (Merge Conflicts): %d\n", repoStat.SkippedMergeConf)
191+
fmt.Printf(" Skipped (Did Not Match): %d\n", repoStat.SkippedCriteria)
192+
if repoStat.CombinedPRLink != "" {
193+
fmt.Printf(" Combined PR: %s\n", repoStat.CombinedPRLink)
194+
}
195+
}
196+
}
197+
198+
// pad returns a string of n runes of s (usually "─")
199+
func pad(s string, n int) string {
200+
if n <= 0 {
201+
return ""
202+
}
203+
out := ""
204+
for i := 0; i < n; i++ {
205+
out += s
206+
}
207+
return out
208+
}

internal/cmd/output_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestDisplayTableStats(t *testing.T) {
9+
stats := &StatsCollector{
10+
ReposProcessed: 2,
11+
PRsCombined: 5,
12+
PRsSkippedMergeConflict: 1,
13+
PRsSkippedCriteria: 2,
14+
CombinedPRLinks: []string{"http://example.com/pr1", "http://example.com/pr2"},
15+
PerRepoStats: map[string]*RepoStats{
16+
"repo1": {
17+
RepoName: "repo1",
18+
CombinedCount: 3,
19+
SkippedMergeConf: 1,
20+
SkippedCriteria: 0,
21+
CombinedPRLink: "http://example.com/pr1",
22+
NotEnoughPRs: false,
23+
TotalPRs: 5,
24+
},
25+
"repo2": {
26+
RepoName: "repo2",
27+
CombinedCount: 2,
28+
SkippedMergeConf: 0,
29+
SkippedCriteria: 2,
30+
CombinedPRLink: "http://example.com/pr2",
31+
NotEnoughPRs: false,
32+
TotalPRs: 4,
33+
},
34+
},
35+
StartTime: time.Now(),
36+
EndTime: time.Now().Add(2 * time.Minute),
37+
}
38+
39+
displayTableStats(stats)
40+
// Add assertions or manual verification as needed
41+
}
42+
43+
func TestDisplayJSONStats(t *testing.T) {
44+
stats := &StatsCollector{
45+
ReposProcessed: 2,
46+
PRsCombined: 5,
47+
PRsSkippedMergeConflict: 1,
48+
PRsSkippedCriteria: 2,
49+
CombinedPRLinks: []string{"http://example.com/pr1", "http://example.com/pr2"},
50+
PerRepoStats: map[string]*RepoStats{
51+
"repo1": {
52+
RepoName: "repo1",
53+
CombinedCount: 3,
54+
SkippedMergeConf: 1,
55+
SkippedCriteria: 0,
56+
CombinedPRLink: "http://example.com/pr1",
57+
NotEnoughPRs: false,
58+
TotalPRs: 5,
59+
},
60+
"repo2": {
61+
RepoName: "repo2",
62+
CombinedCount: 2,
63+
SkippedMergeConf: 0,
64+
SkippedCriteria: 2,
65+
CombinedPRLink: "http://example.com/pr2",
66+
NotEnoughPRs: false,
67+
TotalPRs: 4,
68+
},
69+
},
70+
StartTime: time.Now(),
71+
EndTime: time.Now().Add(2 * time.Minute),
72+
}
73+
74+
displayJSONStats(stats)
75+
// Add assertions or manual verification as needed
76+
}
77+
78+
func TestDisplayPlainStats(t *testing.T) {
79+
stats := &StatsCollector{
80+
ReposProcessed: 2,
81+
PRsCombined: 5,
82+
PRsSkippedMergeConflict: 1,
83+
PRsSkippedCriteria: 2,
84+
CombinedPRLinks: []string{"http://example.com/pr1", "http://example.com/pr2"},
85+
PerRepoStats: map[string]*RepoStats{
86+
"repo1": {
87+
RepoName: "repo1",
88+
CombinedCount: 3,
89+
SkippedMergeConf: 1,
90+
SkippedCriteria: 0,
91+
CombinedPRLink: "http://example.com/pr1",
92+
NotEnoughPRs: false,
93+
TotalPRs: 5,
94+
},
95+
"repo2": {
96+
RepoName: "repo2",
97+
CombinedCount: 2,
98+
SkippedMergeConf: 0,
99+
SkippedCriteria: 2,
100+
CombinedPRLink: "http://example.com/pr2",
101+
NotEnoughPRs: false,
102+
TotalPRs: 4,
103+
},
104+
},
105+
StartTime: time.Now(),
106+
EndTime: time.Now().Add(2 * time.Minute),
107+
}
108+
109+
displayPlainStats(stats)
110+
// Add assertions or manual verification as needed
111+
}

0 commit comments

Comments
 (0)