[DEPRECATED] Go implementation of plcbundle
1package ui
2
3import (
4 "fmt"
5 "os"
6 "strings"
7 "sync"
8 "time"
9)
10
11// ProgressBar shows progress of an operation
12type ProgressBar struct {
13 total int
14 current int
15 totalBytes int64
16 currentBytes int64
17 startTime time.Time
18 mu sync.Mutex
19 width int
20 lastPrint time.Time
21 showBytes bool
22 autoBytes bool // Auto-calculate bytes from items
23 bytesPerItem int64
24}
25
26// NewProgressBar creates a simple progress bar
27func NewProgressBar(total int) *ProgressBar {
28 return &ProgressBar{
29 total: total,
30 startTime: time.Now(),
31 width: 40,
32 lastPrint: time.Now(),
33 showBytes: false,
34 }
35}
36
37// NewProgressBarWithBytes creates a progress bar that tracks bytes
38func NewProgressBarWithBytes(total int, totalBytes int64) *ProgressBar {
39 return &ProgressBar{
40 total: total,
41 totalBytes: totalBytes,
42 startTime: time.Now(),
43 width: 40,
44 lastPrint: time.Now(),
45 showBytes: true,
46 }
47}
48
49// NewProgressBarWithBytesAuto creates a progress bar that auto-estimates bytes
50// avgBytesPerItem is the estimated bytes per item (e.g., avg bundle size)
51func NewProgressBarWithBytesAuto(total int, avgBytesPerItem int64) *ProgressBar {
52 return &ProgressBar{
53 total: total,
54 totalBytes: int64(total) * avgBytesPerItem,
55 startTime: time.Now(),
56 width: 40,
57 lastPrint: time.Now(),
58 showBytes: true,
59 autoBytes: true,
60 bytesPerItem: avgBytesPerItem,
61 }
62}
63
64// Set sets the current progress (auto-estimates bytes if enabled)
65func (pb *ProgressBar) Set(current int) {
66 pb.mu.Lock()
67 defer pb.mu.Unlock()
68 pb.current = current
69
70 // Auto-calculate bytes if enabled
71 if pb.autoBytes && pb.bytesPerItem > 0 {
72 pb.currentBytes = int64(current) * pb.bytesPerItem
73 }
74
75 pb.print()
76}
77
78// SetWithBytes sets progress with exact byte tracking
79func (pb *ProgressBar) SetWithBytes(current int, bytesProcessed int64) {
80 pb.mu.Lock()
81 defer pb.mu.Unlock()
82 pb.current = current
83 pb.currentBytes = bytesProcessed
84 pb.showBytes = true
85 pb.print()
86}
87
88// AddBytes increments current progress and adds bytes
89func (pb *ProgressBar) AddBytes(increment int, bytes int64) {
90 pb.mu.Lock()
91 defer pb.mu.Unlock()
92 pb.current += increment
93 pb.currentBytes += bytes
94 pb.showBytes = true
95 pb.print()
96}
97
98// Finish completes the progress bar
99func (pb *ProgressBar) Finish() {
100 pb.mu.Lock()
101 defer pb.mu.Unlock()
102 pb.current = pb.total
103 pb.currentBytes = pb.totalBytes
104 pb.print()
105 fmt.Fprintf(os.Stderr, "\n")
106}
107
108// print renders the progress bar
109func (pb *ProgressBar) print() {
110 if time.Since(pb.lastPrint) < 100*time.Millisecond && pb.current < pb.total {
111 return
112 }
113 pb.lastPrint = time.Now()
114
115 percent := 0.0
116 if pb.total > 0 {
117 percent = float64(pb.current) / float64(pb.total) * 100
118 }
119
120 filled := 0
121 if pb.total > 0 {
122 filled = int(float64(pb.width) * float64(pb.current) / float64(pb.total))
123 if filled > pb.width {
124 filled = pb.width
125 }
126 }
127
128 bar := strings.Repeat("█", filled) + strings.Repeat("░", pb.width-filled)
129
130 elapsed := time.Since(pb.startTime)
131 speed := 0.0
132 if elapsed.Seconds() > 0 {
133 speed = float64(pb.current) / elapsed.Seconds()
134 }
135
136 remaining := pb.total - pb.current
137 var eta time.Duration
138 if speed > 0 && remaining > 0 {
139 eta = time.Duration(float64(remaining)/speed) * time.Second
140 }
141
142 isComplete := pb.current >= pb.total
143
144 if pb.showBytes && pb.currentBytes > 0 {
145 mbProcessed := float64(pb.currentBytes) / (1000 * 1000)
146 mbPerSec := 0.0
147 if elapsed.Seconds() > 0 {
148 mbPerSec = mbProcessed / elapsed.Seconds()
149 }
150
151 if isComplete {
152 fmt.Fprintf(os.Stderr, "\r [%s] %6.2f%% | %d/%d | %.1f/s | %.1f MB/s | Done ",
153 bar, percent, pb.current, pb.total, speed, mbPerSec)
154 } else {
155 fmt.Fprintf(os.Stderr, "\r [%s] %6.2f%% | %d/%d | %.1f/s | %.1f MB/s | ETA: %s ",
156 bar, percent, pb.current, pb.total, speed, mbPerSec, formatETA(eta))
157 }
158 } else {
159 if isComplete {
160 fmt.Fprintf(os.Stderr, "\r [%s] %6.2f%% | %d/%d | %.1f/s | Done ",
161 bar, percent, pb.current, pb.total, speed)
162 } else {
163 fmt.Fprintf(os.Stderr, "\r [%s] %6.2f%% | %d/%d | %.1f/s | ETA: %s ",
164 bar, percent, pb.current, pb.total, speed, formatETA(eta))
165 }
166 }
167}
168
169func formatETA(d time.Duration) string {
170 if d == 0 {
171 return "0s"
172 }
173 if d < time.Minute {
174 return fmt.Sprintf("%ds", int(d.Seconds()))
175 }
176 if d < time.Hour {
177 return fmt.Sprintf("%dm %ds", int(d.Minutes()), int(d.Seconds())%60)
178 }
179 return fmt.Sprintf("%dh %dm", int(d.Hours()), int(d.Minutes())%60)
180}