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
129
|
package tui
import (
"context"
"fmt"
"strings"
"sync/atomic"
"time"
)
type ProgressBar struct {
title string
width int
total atomic.Int64
completed atomic.Int64
start time.Time
}
func NewProgressBar(title string, width, total int, start time.Time) *ProgressBar {
pb := &ProgressBar{
title: title,
width: width,
start: start,
}
pb.total.Store(int64(total))
pb.Print()
return pb
}
func (pb *ProgressBar) SetTotal(steps int) {
pb.total.Store(int64(steps))
}
func (pb *ProgressBar) Total() int {
return int(pb.total.Load())
}
func (pb *ProgressBar) SetCompleted(steps int) {
pb.completed.Store(int64(steps))
}
func (pb *ProgressBar) Completed() int {
return int(pb.completed.Load())
}
func (pb *ProgressBar) SetProgress(completed, total int) {
pb.completed.Store(int64(completed))
pb.total.Store(int64(total))
}
func (pb *ProgressBar) Progress() (int, int) {
return pb.Completed(), pb.Total()
}
func (pb *ProgressBar) Add(steps int) {
pb.completed.Add(int64(steps))
}
func (pb *ProgressBar) Print() {
fmt.Print(pb.String())
}
func (pb *ProgressBar) Finish() {
fmt.Println(pb.String())
}
func (pb *ProgressBar) String() string {
title := pb.title
width := pb.width
total := pb.Total()
completed := pb.Completed()
start := pb.start
var progress int
var percentage int
if total > 0 {
progress = int(float64(completed) / float64(total) * float64(width))
percentage = int(float64(completed) / float64(total) * 100)
}
lap := time.Since(start)
duration := fmt.Sprintf("%02d:%02d:%02d", int(lap.Hours()), int(lap.Minutes())%60, int(lap.Seconds())%60)
return fmt.Sprintf(
"\r[%s] %-20s %3d%% [%s%s] %d/%d",
duration,
title,
percentage,
strings.Repeat("=", min(progress, width)),
strings.Repeat("-", max(0, width-progress)),
completed,
total,
)
}
func (pb *ProgressBar) Watch(
ctx context.Context,
interval time.Duration,
callback func() int,
) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
n := callback()
pb.SetCompleted(n)
pb.Finish()
return
case <-ticker.C:
n := callback()
pb.SetCompleted(n)
pb.Print()
}
}
}
|