| 0 | 0 |
package mpb
|
| 1 | 1 |
|
| 2 | 2 |
import (
|
| 3 | |
"fmt"
|
| 4 | 3 |
"io"
|
| 5 | 4 |
"math"
|
| 6 | 5 |
"sync"
|
|
| 21 | 20 |
|
| 22 | 21 |
// Bar represents a progress Bar
|
| 23 | 22 |
type Bar struct {
|
| 24 | |
stateReqCh chan state
|
| 25 | |
incrCh chan int64
|
| 26 | |
dCommandCh chan *dCommandData
|
| 27 | |
flushedCh chan struct{}
|
| 28 | |
removeReqCh chan struct{}
|
| 29 | |
completeReqCh chan struct{}
|
| 30 | |
done chan struct{}
|
| 31 | |
cancel <-chan struct{}
|
|
23 |
stateReqCh chan state
|
|
24 |
incrCh chan incrReq
|
|
25 |
dCommandCh chan *dCommandData
|
|
26 |
flushedCh chan struct{}
|
|
27 |
removeReqCh chan struct{}
|
|
28 |
completedCh chan struct{}
|
|
29 |
done chan struct{}
|
|
30 |
cancel <-chan struct{}
|
| 32 | 31 |
|
| 33 | 32 |
// follawing are used after (*Bar.done) is closed
|
| 34 | 33 |
width int
|
|
| 46 | 45 |
TimePerItemEstimate time.Duration
|
| 47 | 46 |
}
|
| 48 | 47 |
|
|
48 |
// Refil is a struct for b.IncrWithReFill
|
|
49 |
type Refill struct {
|
|
50 |
Char rune
|
|
51 |
Till int64
|
|
52 |
}
|
|
53 |
|
| 49 | 54 |
// Eta returns exponential-weighted-moving-average ETA estimator
|
| 50 | 55 |
func (s *Statistics) Eta() time.Duration {
|
| 51 | 56 |
return time.Duration(s.Total-s.Current) * s.TimePerItemEstimate
|
| 52 | 57 |
}
|
| 53 | 58 |
|
| 54 | 59 |
type (
|
| 55 | |
refill struct {
|
| 56 | |
char rune
|
| 57 | |
till int64
|
|
60 |
incrReq struct {
|
|
61 |
amount int64
|
|
62 |
refill *Refill
|
| 58 | 63 |
}
|
| 59 | 64 |
state struct {
|
| 60 | 65 |
id int
|
|
| 72 | 77 |
appendFuncs []DecoratorFunc
|
| 73 | 78 |
prependFuncs []DecoratorFunc
|
| 74 | 79 |
simpleSpinner func() byte
|
| 75 | |
refill *refill
|
|
80 |
refill *Refill
|
| 76 | 81 |
}
|
| 77 | 82 |
)
|
| 78 | 83 |
|
| 79 | 84 |
func newBar(id int, total int64, wg *sync.WaitGroup, conf *userConf) *Bar {
|
| 80 | 85 |
b := &Bar{
|
| 81 | |
width: conf.width,
|
| 82 | |
stateReqCh: make(chan state),
|
| 83 | |
incrCh: make(chan int64, 1),
|
| 84 | |
dCommandCh: make(chan *dCommandData),
|
| 85 | |
flushedCh: make(chan struct{}, 1),
|
| 86 | |
removeReqCh: make(chan struct{}),
|
| 87 | |
completeReqCh: make(chan struct{}),
|
| 88 | |
done: make(chan struct{}),
|
| 89 | |
cancel: conf.cancel,
|
|
86 |
width: conf.width,
|
|
87 |
stateReqCh: make(chan state),
|
|
88 |
incrCh: make(chan incrReq),
|
|
89 |
dCommandCh: make(chan *dCommandData),
|
|
90 |
flushedCh: make(chan struct{}),
|
|
91 |
removeReqCh: make(chan struct{}),
|
|
92 |
completedCh: make(chan struct{}),
|
|
93 |
done: make(chan struct{}),
|
|
94 |
cancel: conf.cancel,
|
| 90 | 95 |
}
|
| 91 | 96 |
|
| 92 | 97 |
s := state{
|
|
| 172 | 177 |
|
| 173 | 178 |
// Incr increments progress bar
|
| 174 | 179 |
func (b *Bar) Incr(n int) {
|
| 175 | |
if n < 1 || isClosed(b.done) {
|
| 176 | |
return
|
| 177 | |
}
|
| 178 | |
b.incrCh <- int64(n)
|
|
180 |
b.IncrWithReFill(n, nil)
|
| 179 | 181 |
}
|
| 180 | 182 |
|
| 181 | 183 |
// IncrWithReFill increments pb with different fill character
|
| 182 | |
func (b *Bar) IncrWithReFill(n int, r rune) {
|
| 183 | |
if isClosed(b.done) {
|
| 184 | |
return
|
| 185 | |
}
|
| 186 | |
b.Incr(n)
|
| 187 | |
// b.refillCh <- &refill{r, int64(n)}
|
| 188 | |
s := <-b.stateReqCh
|
| 189 | |
s.refill = &refill{r, int64(n)}
|
| 190 | |
b.stateReqCh <- s
|
|
184 |
func (b *Bar) IncrWithReFill(n int, refill *Refill) {
|
|
185 |
if n < 1 {
|
|
186 |
return
|
|
187 |
}
|
|
188 |
select {
|
|
189 |
case b.incrCh <- incrReq{int64(n), refill}:
|
|
190 |
case <-b.done:
|
|
191 |
return
|
|
192 |
}
|
| 191 | 193 |
}
|
| 192 | 194 |
|
| 193 | 195 |
// GetAppenders returns slice of appender DecoratorFunc
|
|
| 266 | 268 |
// You should call this method when total is unknown and you've reached the point
|
| 267 | 269 |
// of process completion.
|
| 268 | 270 |
func (b *Bar) Completed() {
|
| 269 | |
if isClosed(b.done) {
|
| 270 | |
return
|
| 271 | |
}
|
| 272 | |
b.completeReqCh <- struct{}{}
|
|
271 |
select {
|
|
272 |
case b.completedCh <- struct{}{}:
|
|
273 |
case <-b.done:
|
|
274 |
return
|
|
275 |
}
|
| 273 | 276 |
}
|
| 274 | 277 |
|
| 275 | 278 |
func (b *Bar) getState() state {
|
| 276 | |
if isClosed(b.done) {
|
|
279 |
select {
|
|
280 |
case s := <-b.stateReqCh:
|
|
281 |
return s
|
|
282 |
case <-b.done:
|
| 277 | 283 |
return b.state
|
| 278 | 284 |
}
|
| 279 | |
return <-b.stateReqCh
|
| 280 | 285 |
}
|
| 281 | 286 |
|
| 282 | 287 |
func (b *Bar) server(wg *sync.WaitGroup, s state) {
|
| 283 | 288 |
var incrStartTime time.Time
|
| 284 | 289 |
|
| 285 | 290 |
defer func() {
|
| 286 | |
// b.stop(&barState, conf.width)
|
| 287 | |
b.stop(&s)
|
|
291 |
b.state = s
|
| 288 | 292 |
wg.Done()
|
| 289 | |
fmt.Printf("Exited bar %d\n", s.id)
|
|
293 |
// fmt.Printf("Exited bar %d\n", s.id)
|
|
294 |
close(b.done)
|
| 290 | 295 |
}()
|
| 291 | 296 |
|
| 292 | 297 |
for {
|
| 293 | 298 |
select {
|
| 294 | 299 |
case b.stateReqCh <- s:
|
| 295 | 300 |
case s = <-b.stateReqCh:
|
| 296 | |
case amount := <-b.incrCh:
|
|
301 |
case r := <-b.incrCh:
|
| 297 | 302 |
if s.current == 0 {
|
| 298 | 303 |
incrStartTime = time.Now()
|
| 299 | 304 |
s.startTime = incrStartTime
|
| 300 | 305 |
}
|
| 301 | |
n := s.current + amount
|
|
306 |
n := s.current + r.amount
|
| 302 | 307 |
if s.total > 0 && n > s.total {
|
| 303 | 308 |
s.current = s.total
|
| 304 | 309 |
s.completed = true
|
| 305 | 310 |
break // break out of select
|
| 306 | 311 |
}
|
| 307 | 312 |
s.timeElapsed = time.Since(s.startTime)
|
| 308 | |
s.updateTimePerItemEstimate(incrStartTime, amount)
|
|
313 |
s.updateTimePerItemEstimate(incrStartTime, r.amount)
|
| 309 | 314 |
if n == s.total {
|
| 310 | 315 |
s.completed = true
|
| 311 | 316 |
}
|
| 312 | 317 |
s.current = n
|
|
318 |
s.refill = r.refill
|
| 313 | 319 |
incrStartTime = time.Now()
|
| 314 | 320 |
case data := <-b.dCommandCh:
|
| 315 | 321 |
switch data.action {
|
|
| 326 | 332 |
if s.completed {
|
| 327 | 333 |
return
|
| 328 | 334 |
}
|
| 329 | |
case <-b.completeReqCh:
|
|
335 |
case <-b.completedCh:
|
|
336 |
s.completed = true
|
| 330 | 337 |
return
|
| 331 | 338 |
case <-b.removeReqCh:
|
| 332 | 339 |
return
|
|
| 336 | 343 |
}
|
| 337 | 344 |
}
|
| 338 | 345 |
|
| 339 | |
func (b *Bar) stop(s *state) {
|
| 340 | |
b.state = *s
|
| 341 | |
close(b.done)
|
| 342 | |
}
|
| 343 | |
|
| 344 | 346 |
func (b *Bar) flushed() {
|
| 345 | |
if isClosed(b.done) {
|
| 346 | |
return
|
| 347 | |
}
|
| 348 | |
b.flushedCh <- struct{}{}
|
|
347 |
select {
|
|
348 |
case b.flushedCh <- struct{}{}:
|
|
349 |
case <-b.done:
|
|
350 |
return
|
|
351 |
}
|
| 349 | 352 |
}
|
| 350 | 353 |
|
| 351 | 354 |
func (b *Bar) remove() {
|
| 352 | |
if isClosed(b.done) {
|
| 353 | |
return
|
| 354 | |
}
|
| 355 | |
b.removeReqCh <- struct{}{}
|
|
355 |
select {
|
|
356 |
case b.removeReqCh <- struct{}{}:
|
|
357 |
case <-b.done:
|
|
358 |
return
|
|
359 |
}
|
| 356 | 360 |
}
|
| 357 | 361 |
|
| 358 | 362 |
func (b *Bar) render(rFn func(chan []byte), termWidth int, prependWs, appendWs *widthSync) <-chan []byte {
|
|
| 450 | 454 |
return buf
|
| 451 | 455 |
}
|
| 452 | 456 |
|
| 453 | |
func fillBar(total, current int64, width int, fmtBytes barFmtBytes, rf *refill) []byte {
|
|
457 |
func fillBar(total, current int64, width int, fmtBytes barFmtBytes, rf *Refill) []byte {
|
| 454 | 458 |
if width < 2 || total <= 0 {
|
| 455 | 459 |
return []byte{}
|
| 456 | 460 |
}
|
|
| 464 | 468 |
buf = append(buf, fmtBytes[rLeft]...)
|
| 465 | 469 |
|
| 466 | 470 |
if rf != nil {
|
| 467 | |
till := percentage(total, rf.till, barWidth)
|
| 468 | |
rbytes := make([]byte, utf8.RuneLen(rf.char))
|
| 469 | |
utf8.EncodeRune(rbytes, rf.char)
|
|
471 |
till := percentage(total, rf.Till, barWidth)
|
|
472 |
rbytes := make([]byte, utf8.RuneLen(rf.Char))
|
|
473 |
utf8.EncodeRune(rbytes, rf.Char)
|
| 470 | 474 |
// append refill rune
|
| 471 | 475 |
for i := 0; i < till; i++ {
|
| 472 | 476 |
buf = append(buf, rbytes...)
|