Codebase list golang-github-vbauerster-mpb / 8068e5a
check if total <= 0, and display simple spinner if so Vladimir Bauer 9 years ago
3 changed file(s) with 144 addition(s) and 93 deletion(s). Raw diff Collapse all Expand all
+105
-73
bar.go less more
99
1010 // Bar represents a progress Bar
1111 type Bar struct {
12 width int
13 termWidth int
14 etaAlpha float64
15
16 fill byte
17 empty byte
18 tip byte
19 leftEnd byte
20 rightEnd byte
12 fillCh, emptyCh, tipCh, leftEndCh, rightEndCh chan byte
13
14 widthCh chan int
15 etaAlphaCh chan float64
2116
2217 incrCh chan int64
2318 trimLeftCh chan bool
5146 till int64
5247 }
5348 state struct {
54 total, current int64
55 timeElapsed, timePerItem time.Duration
56 trimLeftSpace, trimRightSpace bool
57 appendFuncs, prependFuncs []DecoratorFunc
58 fill byte
59 empty byte
60 tip byte
61 leftEnd byte
62 rightEnd byte
63 etaAlpha float64
64 width int
65 refill *refill
49 fill byte
50 empty byte
51 tip byte
52 leftEnd byte
53 rightEnd byte
54 etaAlpha float64
55 barWidth int
56 total int64
57 current int64
58 trimLeftSpace bool
59 trimRightSpace bool
60 timeElapsed time.Duration
61 timePerItem time.Duration
62 appendFuncs []DecoratorFunc
63 prependFuncs []DecoratorFunc
64 simpleSpinner func() byte
65 refill *refill
6666 }
6767 )
6868
69 func newBar(ctx context.Context, wg *sync.WaitGroup, total int64, width int) *Bar {
69 func newBar(ctx context.Context, wg *sync.WaitGroup, total int64, barWidth int) *Bar {
7070 b := &Bar{
71 fill: '=',
72 empty: '-',
73 tip: '>',
74 leftEnd: '[',
75 rightEnd: ']',
76 etaAlpha: 0.25,
77 width: width,
78
71 fillCh: make(chan byte),
72 emptyCh: make(chan byte),
73 tipCh: make(chan byte),
74 leftEndCh: make(chan byte),
75 rightEndCh: make(chan byte),
76 etaAlphaCh: make(chan float64),
7977 incrCh: make(chan int64, 1),
78 widthCh: make(chan int),
8079 trimLeftCh: make(chan bool),
8180 trimRightCh: make(chan bool),
8281 refillCh: make(chan *refill),
8786 completeReqCh: make(chan struct{}),
8887 done: make(chan struct{}),
8988 }
90 go b.server(ctx, wg, total)
89 go b.server(ctx, wg, total, barWidth)
9190 return b
9291 }
9392
9493 // SetWidth sets width of the bar
9594 func (b *Bar) SetWidth(n int) *Bar {
96 if n < 2 {
97 return b
98 }
99 b.width = n
95 if n < 2 || IsClosed(b.done) {
96 return b
97 }
98 b.widthCh <- n
10099 return b
101100 }
102101
121120 // SetFill sets character representing completed progress.
122121 // Defaults to '='
123122 func (b *Bar) SetFill(c byte) *Bar {
124 b.fill = c
123 if IsClosed(b.done) {
124 return b
125 }
126 b.fillCh <- c
125127 return b
126128 }
127129
128130 // SetTip sets character representing tip of progress.
129131 // Defaults to '>'
130132 func (b *Bar) SetTip(c byte) *Bar {
131 b.tip = c
133 if IsClosed(b.done) {
134 return b
135 }
136 b.tipCh <- c
132137 return b
133138 }
134139
135140 // SetEmpty sets character representing the empty progress
136141 // Defaults to '-'
137142 func (b *Bar) SetEmpty(c byte) *Bar {
138 b.empty = c
143 if IsClosed(b.done) {
144 return b
145 }
146 b.emptyCh <- c
139147 return b
140148 }
141149
142150 // SetLeftEnd sets character representing the left most border
143151 // Defaults to '['
144152 func (b *Bar) SetLeftEnd(c byte) *Bar {
145 b.leftEnd = c
153 if IsClosed(b.done) {
154 return b
155 }
156 b.leftEndCh <- c
146157 return b
147158 }
148159
149160 // SetRightEnd sets character representing the right most border
150161 // Defaults to ']'
151162 func (b *Bar) SetRightEnd(c byte) *Bar {
152 b.rightEnd = c
163 if IsClosed(b.done) {
164 return b
165 }
166 b.rightEndCh <- c
153167 return b
154168 }
155169
157171 // Defaults to 0.25
158172 // Normally you shouldn't touch this
159173 func (b *Bar) SetEtaAlpha(a float64) *Bar {
160 b.etaAlpha = a
174 if IsClosed(b.done) {
175 return b
176 }
177 b.etaAlphaCh <- a
161178 return b
162179 }
163180
235252 }
236253
237254 // Completed signals to the bar, that process has been completed.
238 // You should call this method when total is unknown and you reached the point
255 // You should call this method when total is unknown and you've reached the point
239256 // of process completion.
240257 func (b *Bar) Completed() {
241258 if IsClosed(b.done) {
244261 b.completeReqCh <- struct{}{}
245262 }
246263
247 func (b *Bar) bytes(width int) []byte {
248 if width <= 0 {
249 width = b.width
250 }
251 if IsClosed(b.done) {
252 return b.lastState.draw(width)
264 func (b *Bar) bytes(termWidth int) []byte {
265 if IsClosed(b.done) {
266 return b.lastState.draw(termWidth)
253267 }
254268 ch := make(chan state, 1)
255269 b.stateReqCh <- ch
256270 s := <-ch
257 return s.draw(width)
258 }
259
260 func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, total int64) {
271 return s.draw(termWidth)
272 }
273
274 func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, total int64, barWidth int) {
261275 var completed bool
262276 timeStarted := time.Now()
263277 blockStartTime := timeStarted
264 state := state{total: total}
278 state := state{
279 fill: '=',
280 empty: '-',
281 tip: '>',
282 leftEnd: '[',
283 rightEnd: ']',
284 etaAlpha: 0.25,
285 barWidth: barWidth,
286 total: total,
287 }
288 if total <= 0 {
289 state.simpleSpinner = getSpinner()
290 }
265291 defer func() {
266292 b.stop(&state)
267293 wg.Done()
277303 break // break out of select
278304 }
279305 state.timeElapsed = time.Since(timeStarted)
280 state.timePerItem = calcTimePerItemEstimate(state.timePerItem, blockStartTime, b.etaAlpha, i)
306 state.timePerItem = calcTimePerItemEstimate(state.timePerItem, blockStartTime, state.etaAlpha, i)
281307 if n == total {
282308 completed = true
283309 }
295321 state.prependFuncs = nil
296322 }
297323 case ch := <-b.stateReqCh:
298 state.fill = b.fill
299 state.empty = b.empty
300 state.tip = b.tip
301 state.leftEnd = b.leftEnd
302 state.rightEnd = b.rightEnd
303 state.etaAlpha = b.etaAlpha
304 state.width = b.width
305324 ch <- state
325 case state.fill = <-b.fillCh:
326 case state.empty = <-b.emptyCh:
327 case state.tip = <-b.tipCh:
328 case state.leftEnd = <-b.leftEndCh:
329 case state.rightEnd = <-b.rightEndCh:
330 case state.barWidth = <-b.widthCh:
306331 case state.refill = <-b.refillCh:
307332 case state.trimLeftSpace = <-b.trimLeftCh:
308333 case state.trimRightSpace = <-b.trimRightCh:
339364 b.removeReqCh <- struct{}{}
340365 }
341366
342 func (s *state) draw(termWidth int) []byte {
367 func (s state) draw(termWidth int) []byte {
368 if termWidth <= 0 {
369 termWidth = s.barWidth
370 }
343371 stat := &Statistics{
344372 Total: s.total,
345373 Current: s.current,
360388 prependBlock = append(prependBlock, []byte(f(stat))...)
361389 }
362390
363 barBlock := s.fillBar()
391 barBlock := s.fillBar(s.barWidth)
364392 prependCount := utf8.RuneCount(prependBlock)
365393 barCount := utf8.RuneCount(barBlock)
366394 appendCount := utf8.RuneCount(appendBlock)
379407
380408 totalCount := prependCount + barCount + appendCount
381409 if totalCount >= termWidth {
382 s.width = termWidth - prependCount - appendCount - 1
383 barBlock = s.fillBar()
410 newWidth := termWidth - prependCount - appendCount - 1
411 barBlock = s.fillBar(newWidth)
384412 }
385413
386414 buf := make([]byte, 0, termWidth)
391419 return buf
392420 }
393421
394 func (s *state) fillBar() []byte {
395 if s.width < 2 {
422 func (s state) fillBar(width int) []byte {
423 if width < 2 {
396424 return []byte{}
397425 }
398426
399 buf := make([]byte, s.width)
400 completedWidth := percentage(s.total, s.current, s.width)
427 if s.simpleSpinner != nil {
428 return []byte{s.leftEnd, s.simpleSpinner(), s.rightEnd}
429 }
430
431 buf := make([]byte, width)
432 completedWidth := percentage(s.total, s.current, width)
401433
402434 if s.refill != nil {
403 till := percentage(s.total, s.refill.till, s.width)
435 till := percentage(s.total, s.refill.till, width)
404436 for i := 1; i < till; i++ {
405437 buf[i] = s.refill.char
406438 }
413445 }
414446 }
415447
416 for i := completedWidth; i < s.width-1; i++ {
448 for i := completedWidth; i < width-1; i++ {
417449 buf[i] = s.empty
418450 }
419451 // set tip bit
420 if completedWidth > 0 && completedWidth < s.width {
452 if completedWidth > 0 && completedWidth < s.barWidth {
421453 buf[completedWidth-1] = s.tip
422454 }
423455 // set left and right ends bits
424 buf[0], buf[s.width-1] = s.leftEnd, s.rightEnd
456 buf[0], buf[width-1] = s.leftEnd, s.rightEnd
425457
426458 return buf
427459 }
55 )
66
77 func TestFillBar(t *testing.T) {
8 b := newTestBar(80).SetEmpty('-').SetFill('=').SetTip('>').SetLeftEnd('[').SetRightEnd(']')
8 s := newTestState(80, 60)
99 tests := []struct {
10 width int
11 want []byte
10 termWidth int
11 barWidth int
12 want []byte
1213 }{
1314 {
14 width: 1,
15 want: []byte{},
15 termWidth: 2,
16 barWidth: 60,
17 want: []byte{},
1618 },
1719 {
18 width: 2,
19 want: []byte{'[', ']'},
20 termWidth: 3,
21 barWidth: 60,
22 want: []byte{'[', ']'},
2023 },
2124 {
22 width: 3,
23 want: []byte{'[', '>', ']'},
25 termWidth: 4,
26 barWidth: 60,
27 want: []byte{'[', '>', ']'},
2428 },
2529 {
26 width: 4,
27 want: []byte{'[', '=', '>', ']'},
30 termWidth: 5,
31 barWidth: 60,
32 want: []byte{'[', '=', '>', ']'},
33 },
34 {
35 termWidth: 6,
36 barWidth: 60,
37 want: []byte{'[', '=', '>', '-', ']'},
2838 },
2939 }
3040
3141 for _, test := range tests {
32 got := b.fillBar(80, 60, test.width)
42 s.barWidth = test.barWidth
43 got := s.draw(test.termWidth)
3344 if !reflect.DeepEqual(test.want, got) {
3445 t.Errorf("Want: %q, Got: %q\n", test.want, got)
3546 }
3647 }
3748 }
3849
39 func newTestBar(width int) *Bar {
40 b := &Bar{
41 width: width,
50 func newTestState(total, current int64) *state {
51 return &state{
52 fill: '=',
53 empty: '-',
54 tip: '>',
55 leftEnd: '[',
56 rightEnd: ']',
57 etaAlpha: 0.25,
58 total: total,
59 current: current,
60 trimLeftSpace: true,
61 trimRightSpace: true,
4262 }
43 return b
4463 }
3232 }
3333
3434 indexedBar struct {
35 index int
36 width int
37 bar *Bar
35 index int
36 termWidth int
37 bar *Bar
3838 }
3939 )
4040
258258
259259 func drawer(ibars <-chan indexedBar, c chan<- indexedBarBuffer) {
260260 for b := range ibars {
261 buf := b.bar.bytes(b.width)
261 buf := b.bar.bytes(b.termWidth)
262262 buf = append(buf, '\n')
263263 c <- indexedBarBuffer{b.index, buf}
264264 }