...
1 package progress
2
3 import (
4 "math"
5 "sync"
6 "time"
7 )
8
9
10
11
12
13
14 type Tracker struct {
15
16
17
18
19 Message string
20
21
22 ExpectedDuration time.Duration
23
24 Total int64
25
26 Units Units
27
28 done bool
29 err bool
30 mutex sync.RWMutex
31 timeStart time.Time
32 timeStop time.Time
33 value int64
34 }
35
36
37
38 func (t *Tracker) ETA() time.Duration {
39 t.mutex.RLock()
40 defer t.mutex.RUnlock()
41
42 timeTaken := time.Since(t.timeStart)
43 if t.ExpectedDuration > time.Duration(0) && t.ExpectedDuration > timeTaken {
44 return t.ExpectedDuration - timeTaken
45 }
46
47 pDone := int64(t.percentDoneWithoutLock())
48 if pDone == 0 {
49 return time.Duration(0)
50 }
51 return time.Duration((int64(timeTaken) / pDone) * (100 - pDone))
52 }
53
54
55 func (t *Tracker) Increment(value int64) {
56 t.mutex.Lock()
57 t.incrementWithoutLock(value)
58 t.mutex.Unlock()
59 }
60
61
62
63 func (t *Tracker) IncrementWithError(value int64) {
64 t.mutex.Lock()
65 t.incrementWithoutLock(value)
66 t.err = true
67 t.mutex.Unlock()
68 }
69
70
71
72 func (t *Tracker) IsDone() bool {
73 t.mutex.RLock()
74 defer t.mutex.RUnlock()
75
76 return t.done
77 }
78
79
80 func (t *Tracker) IsErrored() bool {
81 t.mutex.RLock()
82 defer t.mutex.RUnlock()
83
84 return t.err
85 }
86
87
88
89 func (t *Tracker) IsIndeterminate() bool {
90 t.mutex.RLock()
91 defer t.mutex.RUnlock()
92
93 return t.Total == 0
94 }
95
96
97
98 func (t *Tracker) MarkAsDone() {
99 t.mutex.Lock()
100 t.Total = t.value
101 t.stop()
102 t.mutex.Unlock()
103 }
104
105
106
107 func (t *Tracker) MarkAsErrored() {
108 t.mutex.Lock()
109
110 if !t.done {
111 t.Total = t.value
112 t.err = true
113 t.stop()
114 }
115 t.mutex.Unlock()
116 }
117
118
119 func (t *Tracker) PercentDone() float64 {
120 t.mutex.RLock()
121 defer t.mutex.RUnlock()
122 return t.percentDoneWithoutLock()
123 }
124
125 func (t *Tracker) percentDoneWithoutLock() float64 {
126 if t.Total == 0 {
127 return 0
128 }
129 return float64(t.value) * 100.0 / float64(t.Total)
130 }
131
132
133 func (t *Tracker) Reset() {
134 t.mutex.Lock()
135 t.done = false
136 t.err = false
137 t.timeStart = time.Time{}
138 t.timeStop = time.Time{}
139 t.value = 0
140 t.mutex.Unlock()
141 }
142
143
144
145 func (t *Tracker) SetValue(value int64) {
146 t.mutex.Lock()
147 t.done = false
148 t.timeStop = time.Time{}
149 t.value = 0
150 t.incrementWithoutLock(value)
151 t.mutex.Unlock()
152 }
153
154
155 func (t *Tracker) UpdateMessage(msg string) {
156 t.mutex.Lock()
157 t.Message = msg
158 t.mutex.Unlock()
159 }
160
161
162 func (t *Tracker) Value() int64 {
163 t.mutex.RLock()
164 defer t.mutex.RUnlock()
165 return t.value
166 }
167
168 func (t *Tracker) message() string {
169 t.mutex.RLock()
170 defer t.mutex.RUnlock()
171 return t.Message
172 }
173
174 func (t *Tracker) valueAndTotal() (int64, int64) {
175 t.mutex.RLock()
176 value := t.value
177 total := t.Total
178 t.mutex.RUnlock()
179 return value, total
180 }
181
182 func (t *Tracker) incrementWithoutLock(value int64) {
183 if !t.done {
184 t.value += value
185 if t.Total > 0 && t.value >= t.Total {
186 t.stop()
187 }
188 }
189 }
190
191 func (t *Tracker) start() {
192 t.mutex.Lock()
193 if t.Total < 0 {
194 t.Total = math.MaxInt64
195 }
196 t.done = false
197 t.err = false
198 t.timeStart = time.Now()
199 t.mutex.Unlock()
200 }
201
202
203 func (t *Tracker) stop() {
204 t.done = true
205 t.timeStop = time.Now()
206 if t.value > t.Total {
207 t.Total = t.value
208 }
209 }
210
View as plain text