Codebase list golang-github-vbauerster-mpb / 43ef86e
refactoring: bar format constants Vladimir Bauer 9 years ago
2 changed file(s) with 88 addition(s) and 91 deletion(s). Raw diff Collapse all Expand all
77 "unicode/utf8"
88 )
99
10 type formatRunes [numFmtRunes]rune
10 const (
11 rLeft = iota
12 rFill
13 rTip
14 rEmpty
15 rRight
16 )
17
18 type barFmtRunes [numFmtRunes]rune
19 type barFmtBytes [numFmtRunes][]byte
1120
1221 // Bar represents a progress Bar
1322 type Bar struct {
14 widthSetCh chan int
15 widthReqCh chan chan int
1623 stateReqCh chan chan state
24 widthCh chan int
1725 formatCh chan string
1826 etaAlphaCh chan float64
1927 incrCh chan int64
5462 }
5563 state struct {
5664 id int
57 format formatRunes
65 width int
66 format barFmtRunes
5867 etaAlpha float64
5968 total int64
6069 current int64
7180
7281 func newBar(ctx context.Context, wg *sync.WaitGroup, id int, total int64, width int, format string) *Bar {
7382 b := &Bar{
83 stateReqCh: make(chan chan state, 1),
84 widthCh: make(chan int),
7485 formatCh: make(chan string),
7586 etaAlphaCh: make(chan float64),
7687 incrCh: make(chan int64, 1),
77 widthSetCh: make(chan int),
78 widthReqCh: make(chan chan int),
7988 trimLeftCh: make(chan bool),
8089 trimRightCh: make(chan bool),
8190 refillCh: make(chan *refill),
82 stateReqCh: make(chan chan state, 1),
8391 decoratorCh: make(chan *decorator),
8492 flushedCh: make(chan struct{}, 1),
8593 removeReqCh: make(chan struct{}),
95103 if n < 2 || IsClosed(b.done) {
96104 return b
97105 }
98 b.widthSetCh <- n
99 return b
100 }
101
102 func (b *Bar) GetWidth() int {
103 if IsClosed(b.done) {
104 return b.width
105 }
106 ch := make(chan int, 1)
107 b.widthReqCh <- ch
108 return <-ch
106 b.widthCh <- n
107 return b
109108 }
110109
111110 // TrimLeftSpace removes space befor LeftEnd charater
254253
255254 func (b *Bar) bytes(termWidth int) []byte {
256255 s := b.getState()
257 return draw(&s, b.GetWidth(), termWidth)
256 return draw(&s, termWidth)
258257 }
259258
260259 func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, id int, total int64, width int, format string) {
261260 var completed bool
262261 timeStarted := time.Now()
263262 blockStartTime := timeStarted
264 state := state{
263 barState := state{
265264 id: id,
266 format: formatRunes{'[', '=', '>', '-', ']'},
265 width: width,
266 format: barFmtRunes{'[', '=', '>', '-', ']'},
267267 etaAlpha: 0.25,
268268 total: total,
269269 }
270270 if total <= 0 {
271 state.simpleSpinner = getSpinner()
271 barState.simpleSpinner = getSpinner()
272272 } else {
273 state.updateFormat(format)
273 barState.updateFormat(format)
274274 }
275275 defer func() {
276 b.stop(&state, width)
276 b.stop(&barState, width)
277277 wg.Done()
278278 }()
279279 for {
280280 select {
281281 case i := <-b.incrCh:
282 n := state.current + i
282 n := barState.current + i
283283 if total > 0 && n > total {
284 state.current = total
284 barState.current = total
285285 completed = true
286286 blockStartTime = time.Now()
287287 break // break out of select
288288 }
289 state.timeElapsed = time.Since(timeStarted)
290 state.timePerItem = calcTimePerItemEstimate(state.timePerItem, blockStartTime, state.etaAlpha, i)
289 barState.timeElapsed = time.Since(timeStarted)
290 barState.timePerItem = calcTimePerItemEstimate(barState.timePerItem, blockStartTime, barState.etaAlpha, i)
291291 if n == total {
292292 completed = true
293293 }
294 state.current = n
294 barState.current = n
295295 blockStartTime = time.Now()
296296 case d := <-b.decoratorCh:
297297 switch d.kind {
298298 case decAppend:
299 state.appendFuncs = append(state.appendFuncs, d.f)
299 barState.appendFuncs = append(barState.appendFuncs, d.f)
300300 case decAppendZero:
301 state.appendFuncs = nil
301 barState.appendFuncs = nil
302302 case decPrepend:
303 state.prependFuncs = append(state.prependFuncs, d.f)
303 barState.prependFuncs = append(barState.prependFuncs, d.f)
304304 case decPrependZero:
305 state.prependFuncs = nil
305 barState.prependFuncs = nil
306306 }
307307 case ch := <-b.stateReqCh:
308 ch <- state
309 case ch := <-b.widthReqCh:
310 ch <- width
311 case width = <-b.widthSetCh:
308 ch <- barState
312309 case format := <-b.formatCh:
313 state.updateFormat(format)
314 case state.refill = <-b.refillCh:
315 case state.trimLeftSpace = <-b.trimLeftCh:
316 case state.trimRightSpace = <-b.trimRightCh:
310 barState.updateFormat(format)
311 case barState.width = <-b.widthCh:
312 case barState.refill = <-b.refillCh:
313 case barState.trimLeftSpace = <-b.trimLeftCh:
314 case barState.trimRightSpace = <-b.trimRightCh:
317315 case <-b.flushedCh:
318316 if completed {
319317 return
358356 }
359357 }
360358
361 func draw(s *state, barWidth, termWidth int) []byte {
359 func draw(s *state, termWidth int) []byte {
362360 if termWidth <= 0 {
363 termWidth = barWidth
361 termWidth = s.width
364362 }
365363
366364 stat := newStatistics(s)
377375 prependBlock = append(prependBlock, []byte(f(stat))...)
378376 }
379377
380 barBlock := fillBar(s, barWidth)
381378 prependCount := utf8.RuneCount(prependBlock)
382 barCount := utf8.RuneCount(barBlock)
383379 appendCount := utf8.RuneCount(appendBlock)
384380
385381 var leftSpace, rightSpace []byte
394390 rightSpace = space
395391 }
396392
393 var barBlock []byte
394 buf := make([]byte, 0, termWidth)
395 fmtBytes := convertFmtRunesToBytes(s.format)
396
397 if s.simpleSpinner != nil {
398 for _, block := range [...][]byte{fmtBytes[rLeft], []byte{s.simpleSpinner()}, fmtBytes[rRight]} {
399 barBlock = append(barBlock, block...)
400 }
401 return concatenateBlocks(buf, prependBlock, leftSpace, barBlock, rightSpace, appendBlock)
402 }
403
404 barBlock = fillBar(s.total, s.current, s.width, fmtBytes, s.refill)
405 barCount := utf8.RuneCount(barBlock)
397406 totalCount := prependCount + barCount + appendCount
398407 if totalCount > termWidth {
399408 newWidth := termWidth - prependCount - appendCount
400 barBlock = fillBar(s, newWidth)
401 }
402
403 buf := make([]byte, 0, termWidth)
404 for _, block := range [...][]byte{prependBlock, leftSpace, barBlock, rightSpace, appendBlock} {
409 barBlock = fillBar(s.total, s.current, newWidth, fmtBytes, s.refill)
410 }
411
412 return concatenateBlocks(buf, prependBlock, leftSpace, barBlock, rightSpace, appendBlock)
413 }
414
415 func concatenateBlocks(buf []byte, blocks ...[]byte) []byte {
416 for _, block := range blocks {
405417 buf = append(buf, block...)
406418 }
407
408419 return buf
409420 }
410421
411 func fillBar(s *state, width int) []byte {
422 func fillBar(total, current int64, width int, fmtBytes barFmtBytes, rf *refill) []byte {
412423 if width < 2 {
413424 return []byte{}
414425 }
415426
416427 // bar width without leftEnd and rightEnd runes
417428 barWidth := width - 1
418 formatBytes := convertFmtRunesToBytes(s.format)
419
420 if s.simpleSpinner != nil {
421 var buf []byte
422 for _, block := range [...][]byte{formatBytes[0], []byte{s.simpleSpinner()}, formatBytes[4]} {
423 buf = append(buf, block...)
424 }
425 return buf
426 }
427
428 completedWidth := percentage(s.total, s.current, barWidth)
429
430 completedWidth := percentage(total, current, barWidth)
429431
430432 buf := make([]byte, 0, width)
431 // append leftEnd rune
432 buf = append(buf, formatBytes[0]...)
433
434 if s.refill != nil {
435 till := percentage(s.total, s.refill.till, barWidth)
436 rbytes := make([]byte, utf8.RuneLen(s.refill.char))
437 utf8.EncodeRune(rbytes, s.refill.char)
433 buf = append(buf, fmtBytes[rLeft]...)
434
435 if rf != nil {
436 till := percentage(total, rf.till, barWidth)
437 rbytes := make([]byte, utf8.RuneLen(rf.char))
438 utf8.EncodeRune(rbytes, rf.char)
438439 // append refill rune
439440 for i := 0; i < till; i++ {
440441 buf = append(buf, rbytes...)
441442 }
442 // append fill rune
443443 for i := till; i < completedWidth-1; i++ {
444 buf = append(buf, formatBytes[1]...)
444 buf = append(buf, fmtBytes[rFill]...)
445445 }
446446 } else {
447 // append fill rune
448447 for i := 0; i < completedWidth-1; i++ {
449 buf = append(buf, formatBytes[1]...)
448 buf = append(buf, fmtBytes[rFill]...)
450449 }
451450 }
452451
453 // set tip bit
454452 if completedWidth > 0 && completedWidth < barWidth {
455 buf = append(buf, formatBytes[2]...)
456 }
457
458 // append empty rune
453 buf = append(buf, fmtBytes[rTip]...)
454 }
455
459456 for i := completedWidth + 1; i < barWidth; i++ {
460 buf = append(buf, formatBytes[3]...)
461 }
462
463 // append rightEnd rune
464 buf = append(buf, formatBytes[4]...)
457 buf = append(buf, fmtBytes[rEmpty]...)
458 }
459
460 buf = append(buf, fmtBytes[rRight]...)
465461
466462 return buf
467463 }
475471 }
476472 }
477473
478 func convertFmtRunesToBytes(format formatRunes) [numFmtRunes][]byte {
479 var formatBytes [numFmtRunes][]byte
474 func convertFmtRunesToBytes(format barFmtRunes) barFmtBytes {
475 var fmtBytes barFmtBytes
480476 for i, r := range format {
481477 buf := make([]byte, utf8.RuneLen(r))
482478 utf8.EncodeRune(buf, r)
483 formatBytes[i] = buf
484 }
485 return formatBytes
479 fmtBytes[i] = buf
480 }
481 return fmtBytes
486482 }
487483
488484 func calcTimePerItemEstimate(tpie time.Duration, blockStartTime time.Time, alpha float64, items int64) time.Duration {
4949 }
5050
5151 for _, test := range tests {
52 got := draw(s, test.barWidth, test.termWidth)
52 s.width = test.barWidth
53 got := draw(s, test.termWidth)
5354 if !reflect.DeepEqual(test.want, got) {
5455 t.Errorf("Want: %q, Got: %q\n", test.want, got)
5556 }
5859
5960 func newTestState(total, current int64) *state {
6061 return &state{
61 format: formatRunes{'[', '=', '>', '-', ']'},
62 format: barFmtRunes{'[', '=', '>', '-', ']'},
6263 total: total,
6364 current: current,
6465 trimLeftSpace: true,