[DEPRECATED] Go implementation of plcbundle
at main 180 lines 4.4 kB view raw
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}