-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprogress.go
More file actions
128 lines (118 loc) · 2.88 KB
/
progress.go
File metadata and controls
128 lines (118 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
import (
"fmt"
"os"
"strings"
"time"
)
var (
isTTY = func() bool {
fi, err := os.Stderr.Stat()
if err != nil {
return false
}
return fi.Mode()&os.ModeCharDevice != 0
}()
quietMode bool
)
const barWidth = 30
// formatSize formats a byte count as a human-readable string.
// If raw is true, returns the raw byte count.
func formatSize(b int64, raw bool) string {
if raw {
return fmt.Sprintf("%d", b)
}
const (
kiB = 1024
miB = 1024 * kiB
giB = 1024 * miB
tiB = 1024 * giB
)
switch {
case b >= tiB:
return fmt.Sprintf("%.1f TiB", float64(b)/float64(tiB))
case b >= giB:
return fmt.Sprintf("%.1f GiB", float64(b)/float64(giB))
case b >= miB:
return fmt.Sprintf("%.1f MiB", float64(b)/float64(miB))
case b >= kiB:
return fmt.Sprintf("%.1f KiB", float64(b)/float64(kiB))
default:
return fmt.Sprintf("%d B", b)
}
}
// formatCount formats an integer with comma separators.
func formatCount(n int64) string {
if n < 0 {
return "-" + formatCount(-n)
}
s := fmt.Sprintf("%d", n)
if len(s) <= 3 {
return s
}
var result []byte
for i, c := range s {
if i > 0 && (len(s)-i)%3 == 0 {
result = append(result, ',')
}
result = append(result, byte(c))
}
return string(result)
}
// printProgressBar renders a progress bar on stderr, overwriting the current line.
func printProgressBar(prefix string, current, total int64, suffix string) {
if quietMode || !isTTY || total <= 0 {
return
}
pct := current * 100 / total
filled := int(barWidth * current / total)
if filled > barWidth {
filled = barWidth
}
bar := strings.Repeat("\u2588", filled) + strings.Repeat("\u2591", barWidth-filled)
fmt.Fprintf(os.Stderr, "\r\033[K%s [%s] %3d%% %s", prefix, bar, pct, suffix)
}
// printCounter renders a counter on stderr, overwriting the current line.
func printCounter(prefix string, count int64) {
if quietMode || !isTTY {
return
}
fmt.Fprintf(os.Stderr, "\r\033[K%s %s", prefix, formatCount(count))
}
// printStatus renders a status message on stderr, overwriting the current line.
func printStatus(msg string) {
if quietMode || !isTTY {
return
}
fmt.Fprintf(os.Stderr, "\r\033[K%s", msg)
}
// formatETA returns a compact time estimate string like "1.5m", "30s", "2.3h".
func formatETA(elapsed time.Duration, current, total int64) string {
if current <= 0 || total <= 0 || current > total {
return ""
}
rate := float64(current) / elapsed.Seconds()
if rate <= 0 {
return ""
}
remaining := float64(total-current) / rate
switch {
case remaining < 60:
return fmt.Sprintf("~%.0fs", remaining)
case remaining < 3600:
return fmt.Sprintf("~%.1fm", remaining/60)
default:
return fmt.Sprintf("~%.1fh", remaining/3600)
}
}
// finishLine completes the current line and moves to the next.
func finishLine(text string) {
if quietMode {
return
}
if isTTY {
fmt.Fprintf(os.Stderr, "\r\033[K%s\n", text)
} else {
fmt.Fprintf(os.Stderr, "%s\n", text)
}
}