Breaking change: decor package refactoring
Vladimir Bauer
8 years ago
| 8 | 8 | "time" |
| 9 | 9 | "unicode/utf8" |
| 10 | 10 | |
| 11 | "github.com/VividCortex/ewma" | |
| 11 | 12 | "github.com/vbauerster/mpb/decor" |
| 12 | 13 | ) |
| 13 | 14 | |
| 21 | 22 | |
| 22 | 23 | const ( |
| 23 | 24 | formatLen = 5 |
| 24 | etaAlpha = 0.12 | |
| 25 | 25 | ) |
| 26 | 26 | |
| 27 | 27 | type barRunes [formatLen]rune |
| 35 | 35 | cacheState *bState |
| 36 | 36 | operateState chan func(*bState) |
| 37 | 37 | frameReaderCh chan io.Reader |
| 38 | startBlockCh <-chan time.Time | |
| 38 | 39 | |
| 39 | 40 | // done is closed by Bar's goroutine, after cacheState is written |
| 40 | 41 | done chan struct{} |
| 47 | 48 | id int |
| 48 | 49 | width int |
| 49 | 50 | runes barRunes |
| 50 | etaAlpha float64 | |
| 51 | 51 | total int64 |
| 52 | 52 | current int64 |
| 53 | 53 | totalAutoIncrTrigger int64 |
| 62 | 62 | startTime time.Time |
| 63 | 63 | blockStartTime time.Time |
| 64 | 64 | timeElapsed time.Duration |
| 65 | timePerItemEstimate time.Duration | |
| 66 | timeRemaining time.Duration | |
| 67 | aDecorators []decor.DecoratorFunc | |
| 68 | pDecorators []decor.DecoratorFunc | |
| 65 | aDecorators []decor.Decorator | |
| 66 | pDecorators []decor.Decorator | |
| 69 | 67 | refill *refill |
| 70 | 68 | bufP, bufB, bufA *bytes.Buffer |
| 71 | 69 | panicMsg string |
| 72 | 70 | |
| 71 | ewmAverage ewma.MovingAverage | |
| 72 | ||
| 73 | 73 | // following options are assigned to the *Bar |
| 74 | priority int | |
| 75 | runningBar *Bar | |
| 74 | priority int | |
| 75 | runningBar *Bar | |
| 76 | startBlockCh chan time.Time | |
| 76 | 77 | } |
| 77 | 78 | refill struct { |
| 78 | 79 | char rune |
| 95 | 96 | id: id, |
| 96 | 97 | priority: id, |
| 97 | 98 | total: total, |
| 98 | etaAlpha: etaAlpha, | |
| 99 | 99 | dynamic: dynamic, |
| 100 | 100 | } |
| 101 | 101 | |
| 112 | 112 | b := &Bar{ |
| 113 | 113 | priority: s.priority, |
| 114 | 114 | runningBar: s.runningBar, |
| 115 | startBlockCh: s.startBlockCh, | |
| 115 | 116 | operateState: make(chan func(*bState)), |
| 116 | 117 | frameReaderCh: make(chan io.Reader, 1), |
| 117 | 118 | done: make(chan struct{}), |
| 118 | 119 | shutdown: make(chan struct{}), |
| 119 | 120 | } |
| 120 | 121 | |
| 122 | s.startBlockCh = nil | |
| 123 | ||
| 121 | 124 | if b.runningBar != nil { |
| 122 | 125 | b.priority = b.runningBar.priority |
| 123 | 126 | } |
| 143 | 146 | } |
| 144 | 147 | |
| 145 | 148 | // ProxyReader wrapper for io operations, like io.Copy |
| 146 | func (b *Bar) ProxyReader(r io.Reader) *Reader { | |
| 147 | return &Reader{r, b} | |
| 149 | func (b *Bar) ProxyReader(r io.Reader, startBlock ...chan<- time.Time) *Reader { | |
| 150 | proxyReader := &Reader{ | |
| 151 | Reader: r, | |
| 152 | bar: b, | |
| 153 | } | |
| 154 | if len(startBlock) > 0 { | |
| 155 | proxyReader.startBlockCh = startBlock[0] | |
| 156 | } | |
| 157 | return proxyReader | |
| 148 | 158 | } |
| 149 | 159 | |
| 150 | 160 | // Increment is a shorthand for b.IncrBy(1) |
| 222 | 232 | // SetTotal sets total dynamically. The final param indicates the very last set, |
| 223 | 233 | // in other words you should set it to true when total is determined. |
| 224 | 234 | func (b *Bar) SetTotal(total int64, final bool) { |
| 225 | select { | |
| 226 | case b.operateState <- func(s *bState) { | |
| 235 | b.operateState <- func(s *bState) { | |
| 227 | 236 | if total != 0 { |
| 228 | 237 | s.total = total |
| 229 | 238 | } |
| 230 | 239 | s.dynamic = !final |
| 231 | }: | |
| 232 | case <-b.done: | |
| 233 | } | |
| 234 | } | |
| 235 | ||
| 236 | // StartBlock updates start timestamp of the current increment block. | |
| 237 | // It is optional to call, unless ETA decorator is used. | |
| 238 | // If *bar.ProxyReader is used, it will be called implicitly. | |
| 239 | func (b *Bar) StartBlock() { | |
| 240 | now := time.Now() | |
| 241 | select { | |
| 242 | case b.operateState <- func(s *bState) { | |
| 243 | if s.current == 0 { | |
| 244 | s.startTime = now | |
| 245 | } | |
| 246 | s.blockStartTime = now | |
| 247 | }: | |
| 248 | case <-b.done: | |
| 249 | 240 | } |
| 250 | 241 | } |
| 251 | 242 | |
| 256 | 247 | case b.operateState <- func(s *bState) { |
| 257 | 248 | s.current += int64(n) |
| 258 | 249 | s.timeElapsed = now.Sub(s.startTime) |
| 259 | s.timeRemaining = s.calcETA(n, now.Sub(s.blockStartTime)) | |
| 250 | if s.ewmAverage != nil { | |
| 251 | lastBlockTime := now.Sub(s.blockStartTime) | |
| 252 | lastItemEstimate := float64(lastBlockTime) / float64(n) | |
| 253 | s.ewmAverage.Add(lastItemEstimate) | |
| 254 | } | |
| 260 | 255 | if s.dynamic { |
| 261 | 256 | curp := decor.CalcPercentage(s.total, s.current, 100) |
| 262 | 257 | if 100-curp <= s.totalAutoIncrTrigger { |
| 285 | 280 | func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) { |
| 286 | 281 | defer wg.Done() |
| 287 | 282 | s.startTime = time.Now() |
| 288 | s.blockStartTime = s.startTime | |
| 289 | 283 | for { |
| 290 | 284 | select { |
| 291 | 285 | case op := <-b.operateState: |
| 292 | 286 | op(s) |
| 287 | case now := <-b.startBlockCh: | |
| 288 | s.blockStartTime = now | |
| 293 | 289 | case <-cancel: |
| 294 | 290 | s.toComplete = true |
| 295 | 291 | cancel = nil |
| 348 | 344 | stat := newStatistics(s) |
| 349 | 345 | |
| 350 | 346 | // render prepend functions to the left of the bar |
| 351 | for i, f := range s.pDecorators { | |
| 352 | s.bufP.WriteString(f(stat, pSyncer.Accumulator[i], pSyncer.Distributor[i])) | |
| 353 | } | |
| 354 | ||
| 355 | for i, f := range s.aDecorators { | |
| 356 | s.bufA.WriteString(f(stat, aSyncer.Accumulator[i], aSyncer.Distributor[i])) | |
| 347 | for i, d := range s.pDecorators { | |
| 348 | s.bufP.WriteString(d.Decor(stat, pSyncer.Accumulator[i], pSyncer.Distributor[i])) | |
| 349 | } | |
| 350 | ||
| 351 | for i, d := range s.aDecorators { | |
| 352 | s.bufA.WriteString(d.Decor(stat, aSyncer.Accumulator[i], aSyncer.Distributor[i])) | |
| 357 | 353 | } |
| 358 | 354 | |
| 359 | 355 | prependCount := utf8.RuneCount(s.bufP.Bytes()) |
| 429 | 425 | } |
| 430 | 426 | } |
| 431 | 427 | |
| 432 | func (s *bState) calcETA(n int, lastBlockTime time.Duration) time.Duration { | |
| 433 | lastItemEstimate := float64(lastBlockTime) / float64(n) | |
| 434 | s.timePerItemEstimate = time.Duration((s.etaAlpha * lastItemEstimate) + (1-s.etaAlpha)*float64(s.timePerItemEstimate)) | |
| 435 | return time.Duration(s.total-s.current) * s.timePerItemEstimate | |
| 436 | } | |
| 437 | ||
| 438 | 428 | func newStatistics(s *bState) *decor.Statistics { |
| 439 | 429 | return &decor.Statistics{ |
| 440 | ID: s.id, | |
| 441 | Completed: s.completeFlushed, | |
| 442 | Total: s.total, | |
| 443 | Current: s.current, | |
| 444 | StartTime: s.startTime, | |
| 445 | TimeElapsed: s.timeElapsed, | |
| 446 | TimeRemaining: s.timeRemaining, | |
| 447 | TimePerItemEstimate: s.timePerItemEstimate, | |
| 430 | ID: s.id, | |
| 431 | Completed: s.completeFlushed, | |
| 432 | Total: s.total, | |
| 433 | Current: s.current, | |
| 434 | StartTime: s.startTime, | |
| 435 | TimeElapsed: s.timeElapsed, | |
| 448 | 436 | } |
| 449 | 437 | } |
| 450 | 438 | |
| 8 | 8 | type BarOption func(*bState) |
| 9 | 9 | |
| 10 | 10 | // AppendDecorators let you inject decorators to the bar's right side |
| 11 | func AppendDecorators(appenders ...decor.DecoratorFunc) BarOption { | |
| 11 | func AppendDecorators(appenders ...decor.Decorator) BarOption { | |
| 12 | 12 | return func(s *bState) { |
| 13 | s.aDecorators = append(s.aDecorators, appenders...) | |
| 13 | for _, decorator := range appenders { | |
| 14 | if t, ok := decorator.(*decor.EwmaETA); ok { | |
| 15 | s.ewmAverage = t | |
| 16 | s.startBlockCh = t.StartBlockCh | |
| 17 | } | |
| 18 | s.aDecorators = append(s.aDecorators, decorator) | |
| 19 | } | |
| 14 | 20 | } |
| 15 | 21 | } |
| 16 | 22 | |
| 17 | 23 | // PrependDecorators let you inject decorators to the bar's left side |
| 18 | func PrependDecorators(prependers ...decor.DecoratorFunc) BarOption { | |
| 24 | func PrependDecorators(prependers ...decor.Decorator) BarOption { | |
| 19 | 25 | return func(s *bState) { |
| 20 | s.pDecorators = append(s.pDecorators, prependers...) | |
| 26 | for _, decorator := range prependers { | |
| 27 | if t, ok := decorator.(*decor.EwmaETA); ok { | |
| 28 | s.ewmAverage = t | |
| 29 | s.startBlockCh = t.StartBlockCh | |
| 30 | } | |
| 31 | s.pDecorators = append(s.pDecorators, decorator) | |
| 32 | } | |
| 21 | 33 | } |
| 22 | 34 | } |
| 23 | 35 | |
| 47 | 59 | func BarID(id int) BarOption { |
| 48 | 60 | return func(s *bState) { |
| 49 | 61 | s.id = id |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | // BarEtaAlpha option is a way to adjust ETA behavior. | |
| 54 | // You can play with it, if you're not satisfied with default behavior. | |
| 55 | // Default value is 0.12 | |
| 56 | func BarEtaAlpha(a float64) BarOption { | |
| 57 | return func(s *bState) { | |
| 58 | s.etaAlpha = a | |
| 59 | 62 | } |
| 60 | 63 | } |
| 61 | 64 | |
| 89 | 89 | total := 100 |
| 90 | 90 | |
| 91 | 91 | bar := p.AddBar(int64(total), PrependDecorators( |
| 92 | func(s *decor.Statistics, _ chan<- int, _ <-chan int) string { | |
| 92 | decor.DecoratorFunc(func(s *decor.Statistics, _ chan<- int, _ <-chan int) string { | |
| 93 | 93 | if s.Current >= 42 { |
| 94 | 94 | panic(wantPanic) |
| 95 | 95 | } |
| 96 | 96 | return "test" |
| 97 | }, | |
| 97 | }), | |
| 98 | 98 | )) |
| 99 | 99 | |
| 100 | 100 | go func() { |
| 12 | 12 | } |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | func BenchmarkIncrSingleBarStartBlock(b *testing.B) { | |
| 16 | p := New(WithOutput(ioutil.Discard)) | |
| 17 | bar := p.AddBar(int64(b.N)) | |
| 18 | for i := 0; i < b.N; i++ { | |
| 19 | bar.StartBlock() | |
| 20 | bar.Increment() | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | 15 | func BenchmarkIncrSingleBarWhileIsNotCompleted(b *testing.B) { |
| 25 | 16 | p := New(WithOutput(ioutil.Discard)) |
| 26 | 17 | bar := p.AddBar(int64(b.N)) |
| 4 | 4 | "math" |
| 5 | 5 | "time" |
| 6 | 6 | "unicode/utf8" |
| 7 | ||
| 8 | "github.com/VividCortex/ewma" | |
| 7 | 9 | ) |
| 8 | 10 | |
| 9 | 11 | const ( |
| 12 | 14 | // | foo| b| Without DidentRight |
| 13 | 15 | DidentRight = 1 << iota |
| 14 | 16 | |
| 15 | // DwidthSync bit enables same column width synchronization. | |
| 16 | // Effective on multiple bars only. | |
| 17 | DwidthSync | |
| 18 | ||
| 19 | // DextraSpace bit adds extra space, makes sense with DwidthSync only. | |
| 17 | // DextraSpace bit adds extra space, makes sense with DSyncWidth only. | |
| 20 | 18 | // When DidentRight bit set, the space will be added to the right, |
| 21 | 19 | // otherwise to the left. |
| 22 | 20 | DextraSpace |
| 23 | 21 | |
| 24 | // DSyncSpace is shortcut for DwidthSync|DextraSpace | |
| 25 | DSyncSpace = DwidthSync | DextraSpace | |
| 26 | ||
| 27 | // DSyncSpaceR is shortcut for DwidthSync|DextraSpace|DidentRight | |
| 28 | DSyncSpaceR = DwidthSync | DextraSpace | DidentRight | |
| 22 | // DSyncWidth bit enables same column width synchronization. | |
| 23 | // Effective with multiple bars only. | |
| 24 | DSyncWidth | |
| 25 | ||
| 26 | // DSyncWidthR is shortcut for DSyncWidth|DidentRight | |
| 27 | DSyncWidthR = DSyncWidth | DidentRight | |
| 28 | ||
| 29 | // DSyncSpace is shortcut for DSyncWidth|DextraSpace | |
| 30 | DSyncSpace = DSyncWidth | DextraSpace | |
| 31 | ||
| 32 | // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DidentRight | |
| 33 | DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight | |
| 34 | ) | |
| 35 | ||
| 36 | const ( | |
| 37 | ET_STYLE_GO = iota | |
| 38 | ET_STYLE_HHMMSS | |
| 39 | ET_STYLE_HHMM | |
| 40 | ET_STYLE_MMSS | |
| 29 | 41 | ) |
| 30 | 42 | |
| 31 | 43 | // Statistics contains values useful for implementing a DecoratorFunc. |
| 32 | 44 | type Statistics struct { |
| 33 | ID int | |
| 34 | Completed bool | |
| 35 | Total int64 | |
| 36 | Current int64 | |
| 37 | StartTime time.Time | |
| 38 | TimeElapsed time.Duration | |
| 39 | TimeRemaining time.Duration | |
| 40 | TimePerItemEstimate time.Duration | |
| 41 | } | |
| 42 | ||
| 43 | // DecoratorFunc is a function that can be prepended and appended to the progress bar | |
| 45 | ID int | |
| 46 | Completed bool | |
| 47 | Total int64 | |
| 48 | Current int64 | |
| 49 | StartTime time.Time | |
| 50 | TimeElapsed time.Duration | |
| 51 | } | |
| 52 | ||
| 53 | type Decorator interface { | |
| 54 | Decor(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string | |
| 55 | } | |
| 56 | ||
| 57 | type CompleteMessenger interface { | |
| 58 | OnComplete(string, ...WC) | |
| 59 | } | |
| 60 | ||
| 61 | // DecoratorFunc is an adapter for Decorator interface | |
| 44 | 62 | type DecoratorFunc func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string |
| 45 | 63 | |
| 46 | // OnComplete returns decorator, which wraps provided `fn` decorator, with sole | |
| 47 | // purpose to display final on complete message. | |
| 48 | // | |
| 49 | // `fn` DecoratorFunc to wrap | |
| 50 | // | |
| 51 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 52 | // | |
| 53 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 54 | func OnComplete(fn DecoratorFunc, message string, width, conf int) DecoratorFunc { | |
| 55 | msgDecorator := StaticName(message, width, conf) | |
| 56 | return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 64 | func (f DecoratorFunc) Decor(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 65 | return f(s, widthAccumulator, widthDistributor) | |
| 66 | } | |
| 67 | ||
| 68 | // WC is a struct with two public fields W and C, both of int type. | |
| 69 | // W represents width and C represents bit set of width related config. | |
| 70 | type WC struct { | |
| 71 | W int | |
| 72 | C int | |
| 73 | format string | |
| 74 | } | |
| 75 | ||
| 76 | func (wc WC) formatMsg(msg string, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 77 | format := wc.buildFormat() | |
| 78 | if (wc.C & DSyncWidth) != 0 { | |
| 79 | widthAccumulator <- utf8.RuneCountInString(msg) | |
| 80 | max := <-widthDistributor | |
| 81 | if max == 0 { | |
| 82 | max = wc.W | |
| 83 | } | |
| 84 | if (wc.C & DextraSpace) != 0 { | |
| 85 | max++ | |
| 86 | } | |
| 87 | return fmt.Sprintf(fmt.Sprintf(format, max), msg) | |
| 88 | } | |
| 89 | return fmt.Sprintf(fmt.Sprintf(format, wc.W), msg) | |
| 90 | } | |
| 91 | ||
| 92 | func (wc *WC) buildFormat() string { | |
| 93 | if wc.format != "" { | |
| 94 | return wc.format | |
| 95 | } | |
| 96 | wc.format = "%%" | |
| 97 | if (wc.C & DidentRight) != 0 { | |
| 98 | wc.format += "-" | |
| 99 | } | |
| 100 | wc.format += "%ds" | |
| 101 | return wc.format | |
| 102 | } | |
| 103 | ||
| 104 | // Global convenience shortcuts | |
| 105 | var ( | |
| 106 | WCSyncWidth = WC{C: DSyncWidth} | |
| 107 | WCSyncWidthR = WC{C: DSyncWidthR} | |
| 108 | WCSyncSpace = WC{C: DSyncSpace} | |
| 109 | WCSyncSpaceR = WC{C: DSyncSpaceR} | |
| 110 | ) | |
| 111 | ||
| 112 | // OnComplete returns decorator, which wraps provided decorator, with sole | |
| 113 | // purpose to display provided message on complete event. | |
| 114 | // | |
| 115 | // `decorator` Decorator to wrap | |
| 116 | // | |
| 117 | // `message` message to display on complete event | |
| 118 | // | |
| 119 | // `wc` optional WC config | |
| 120 | func OnComplete(decorator Decorator, message string, wc ...WC) Decorator { | |
| 121 | if cm, ok := decorator.(CompleteMessenger); ok { | |
| 122 | cm.OnComplete(message, wc...) | |
| 123 | return decorator | |
| 124 | } | |
| 125 | msgDecorator := Name(message, wc...) | |
| 126 | return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 57 | 127 | if s.Completed { |
| 58 | return msgDecorator(s, widthAccumulator, widthDistributor) | |
| 59 | } | |
| 60 | return fn(s, widthAccumulator, widthDistributor) | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | // StaticName returns static name/message decorator. | |
| 128 | return msgDecorator.Decor(s, widthAccumulator, widthDistributor) | |
| 129 | } | |
| 130 | return decorator.Decor(s, widthAccumulator, widthDistributor) | |
| 131 | }) | |
| 132 | } | |
| 133 | ||
| 134 | // StaticName returns name decorator. | |
| 65 | 135 | // |
| 66 | 136 | // `name` string to display |
| 67 | 137 | // |
| 68 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 69 | // | |
| 70 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 71 | func StaticName(name string, width, conf int) DecoratorFunc { | |
| 72 | nameFn := func(*Statistics) string { | |
| 73 | return name | |
| 74 | } | |
| 75 | return DynamicName(nameFn, width, conf) | |
| 76 | } | |
| 77 | ||
| 78 | // DynamicName returns dynamic name/message decorator. | |
| 79 | // | |
| 80 | // `messageFn` callback function to get dynamic string message | |
| 81 | // | |
| 82 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 83 | // | |
| 84 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 85 | func DynamicName(messageFn func(*Statistics) string, width, conf int) DecoratorFunc { | |
| 86 | format := "%%" | |
| 87 | if (conf & DidentRight) != 0 { | |
| 88 | format += "-" | |
| 89 | } | |
| 90 | format += "%ds" | |
| 91 | return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 92 | name := messageFn(s) | |
| 93 | if (conf & DwidthSync) != 0 { | |
| 94 | widthAccumulator <- utf8.RuneCountInString(name) | |
| 95 | max := <-widthDistributor | |
| 96 | if (conf & DextraSpace) != 0 { | |
| 97 | max++ | |
| 98 | } | |
| 99 | return fmt.Sprintf(fmt.Sprintf(format, max), name) | |
| 100 | } | |
| 101 | return fmt.Sprintf(fmt.Sprintf(format, width), name) | |
| 102 | } | |
| 138 | // `wc` optional WC config | |
| 139 | func StaticName(name string, wc ...WC) Decorator { | |
| 140 | return Name(name, wc...) | |
| 141 | } | |
| 142 | ||
| 143 | // Name returns name decorator. | |
| 144 | // | |
| 145 | // `name` string to display | |
| 146 | // | |
| 147 | // `wc` optional WC config | |
| 148 | func Name(name string, wc ...WC) Decorator { | |
| 149 | var wc0 WC | |
| 150 | if len(wc) > 0 { | |
| 151 | wc0 = wc[0] | |
| 152 | } | |
| 153 | return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 154 | return wc0.formatMsg(name, widthAccumulator, widthDistributor) | |
| 155 | }) | |
| 103 | 156 | } |
| 104 | 157 | |
| 105 | 158 | // CountersNoUnit returns raw counters decorator |
| 106 | 159 | // |
| 107 | 160 | // `pairFormat` printf compatible verbs for current and total, like "%f" or "%d" |
| 108 | 161 | // |
| 109 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 110 | // | |
| 111 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 112 | func CountersNoUnit(pairFormat string, width, conf int) DecoratorFunc { | |
| 113 | return counters(pairFormat, 0, width, conf) | |
| 162 | // `wc` optional WC config | |
| 163 | func CountersNoUnit(pairFormat string, wc ...WC) Decorator { | |
| 164 | return counters(pairFormat, 0, wc...) | |
| 114 | 165 | } |
| 115 | 166 | |
| 116 | 167 | // CountersKibiByte returns human friendly byte counters decorator, where counters unit is multiple by 1024. |
| 117 | 168 | // |
| 118 | 169 | // `pairFormat` printf compatible verbs for current and total, like "%f" or "%d" |
| 119 | 170 | // |
| 120 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 121 | // | |
| 122 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 171 | // `wc` optional WC config | |
| 123 | 172 | // |
| 124 | 173 | // pairFormat example: |
| 125 | 174 | // |
| 126 | 175 | // "%.1f / %.1f" = "1.0MiB / 12.0MiB" or "% .1f / % .1f" = "1.0 MiB / 12.0 MiB" |
| 127 | func CountersKibiByte(pairFormat string, width, conf int) DecoratorFunc { | |
| 128 | return counters(pairFormat, unitKiB, width, conf) | |
| 176 | func CountersKibiByte(pairFormat string, wc ...WC) Decorator { | |
| 177 | return counters(pairFormat, unitKiB, wc...) | |
| 129 | 178 | } |
| 130 | 179 | |
| 131 | 180 | // CountersKiloByte returns human friendly byte counters decorator, where counters unit is multiple by 1000. |
| 132 | 181 | // |
| 133 | 182 | // `pairFormat` printf compatible verbs for current and total, like "%f" or "%d" |
| 134 | 183 | // |
| 135 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 136 | // | |
| 137 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 184 | // `wc` optional WC config | |
| 138 | 185 | // |
| 139 | 186 | // pairFormat example: |
| 140 | 187 | // |
| 141 | 188 | // "%.1f / %.1f" = "1.0MB / 12.0MB" or "% .1f / % .1f" = "1.0 MB / 12.0 MB" |
| 142 | func CountersKiloByte(pairFormat string, width, conf int) DecoratorFunc { | |
| 143 | return counters(pairFormat, unitKB, width, conf) | |
| 144 | } | |
| 145 | ||
| 146 | func counters(pairFormat string, unit, width, conf int) DecoratorFunc { | |
| 147 | format := "%%" | |
| 148 | if (conf & DidentRight) != 0 { | |
| 149 | format += "-" | |
| 150 | } | |
| 151 | format += "%ds" | |
| 152 | return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 189 | func CountersKiloByte(pairFormat string, wc ...WC) Decorator { | |
| 190 | return counters(pairFormat, unitKB, wc...) | |
| 191 | } | |
| 192 | ||
| 193 | func counters(pairFormat string, unit int, wc ...WC) Decorator { | |
| 194 | var wc0 WC | |
| 195 | if len(wc) > 0 { | |
| 196 | wc0 = wc[0] | |
| 197 | } | |
| 198 | return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 153 | 199 | var str string |
| 154 | 200 | switch unit { |
| 155 | 201 | case unitKiB: |
| 159 | 205 | default: |
| 160 | 206 | str = fmt.Sprintf(pairFormat, s.Current, s.Total) |
| 161 | 207 | } |
| 162 | if (conf & DwidthSync) != 0 { | |
| 163 | widthAccumulator <- utf8.RuneCountInString(str) | |
| 164 | max := <-widthDistributor | |
| 165 | if (conf & DextraSpace) != 0 { | |
| 166 | max++ | |
| 167 | } | |
| 168 | return fmt.Sprintf(fmt.Sprintf(format, max), str) | |
| 169 | } | |
| 170 | return fmt.Sprintf(fmt.Sprintf(format, width), str) | |
| 171 | } | |
| 208 | return wc0.formatMsg(str, widthAccumulator, widthDistributor) | |
| 209 | }) | |
| 172 | 210 | } |
| 173 | 211 | |
| 174 | 212 | // ETA returns exponential-weighted-moving-average ETA decorator. |
| 175 | 213 | // |
| 176 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 177 | // | |
| 178 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 179 | // | |
| 180 | // To correctly estimate non io progress, *Bar.StartBlock should be called, | |
| 181 | // before each increment block. | |
| 182 | func ETA(width, conf int) DecoratorFunc { | |
| 183 | format := "%%" | |
| 184 | if (conf & DidentRight) != 0 { | |
| 185 | format += "-" | |
| 186 | } | |
| 187 | format += "%ds" | |
| 188 | return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 189 | str := fmt.Sprint(time.Duration(s.TimeRemaining.Seconds()) * time.Second) | |
| 190 | if (conf & DwidthSync) != 0 { | |
| 191 | widthAccumulator <- utf8.RuneCountInString(str) | |
| 192 | max := <-widthDistributor | |
| 193 | if (conf & DextraSpace) != 0 { | |
| 194 | max++ | |
| 195 | } | |
| 196 | return fmt.Sprintf(fmt.Sprintf(format, max), str) | |
| 197 | } | |
| 198 | return fmt.Sprintf(fmt.Sprintf(format, width), str) | |
| 199 | } | |
| 214 | // `style` onfe of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] | |
| 215 | // | |
| 216 | // `age` is a decay factor alpha for underlying ewma. | |
| 217 | // General rule of thumb, for the best value: | |
| 218 | // expected progress time in seconds divided by two. | |
| 219 | // For example expected progress duration is one hour. | |
| 220 | // age = 3600 / 2 | |
| 221 | // | |
| 222 | // `startBlock` is channel, user suppose to send time.Now() on each iteration of block start. | |
| 223 | // | |
| 224 | // `wc` optional WC config | |
| 225 | func ETA(style int, age float64, startBlock chan time.Time, wc ...WC) Decorator { | |
| 226 | var wc0 WC | |
| 227 | if len(wc) > 0 { | |
| 228 | wc0 = wc[0] | |
| 229 | } | |
| 230 | if age == .0 { | |
| 231 | age = ewma.AVG_METRIC_AGE | |
| 232 | } | |
| 233 | return &EwmaETA{ | |
| 234 | MovingAverage: ewma.NewMovingAverage(age), | |
| 235 | StartBlockCh: startBlock, | |
| 236 | style: style, | |
| 237 | wc: wc0, | |
| 238 | } | |
| 239 | } | |
| 240 | ||
| 241 | type EwmaETA struct { | |
| 242 | ewma.MovingAverage | |
| 243 | StartBlockCh chan time.Time | |
| 244 | style int | |
| 245 | wc WC | |
| 246 | onComplete *struct { | |
| 247 | msg string | |
| 248 | wc WC | |
| 249 | } | |
| 250 | } | |
| 251 | ||
| 252 | func (s *EwmaETA) Decor(st *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 253 | if st.Completed && s.onComplete != nil { | |
| 254 | return s.onComplete.wc.formatMsg(s.onComplete.msg, widthAccumulator, widthDistributor) | |
| 255 | } | |
| 256 | ||
| 257 | var str string | |
| 258 | timeRemaining := time.Duration(float64(st.Total-st.Current) * s.MovingAverage.Value()) | |
| 259 | hours := int64((timeRemaining / time.Hour) % 60) | |
| 260 | minutes := int64((timeRemaining / time.Minute) % 60) | |
| 261 | seconds := int64((timeRemaining / time.Second) % 60) | |
| 262 | ||
| 263 | switch s.style { | |
| 264 | case ET_STYLE_GO: | |
| 265 | str = fmt.Sprint(time.Duration(timeRemaining.Seconds()) * time.Second) | |
| 266 | case ET_STYLE_HHMMSS: | |
| 267 | str = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) | |
| 268 | case ET_STYLE_HHMM: | |
| 269 | str = fmt.Sprintf("%02d:%02d", hours, minutes) | |
| 270 | case ET_STYLE_MMSS: | |
| 271 | str = fmt.Sprintf("%02d:%02d", minutes, seconds) | |
| 272 | } | |
| 273 | ||
| 274 | return s.wc.formatMsg(str, widthAccumulator, widthDistributor) | |
| 275 | } | |
| 276 | ||
| 277 | func (s *EwmaETA) OnComplete(msg string, wc ...WC) { | |
| 278 | var wc0 WC | |
| 279 | if len(wc) > 0 { | |
| 280 | wc0 = wc[0] | |
| 281 | } | |
| 282 | s.onComplete = &struct { | |
| 283 | msg string | |
| 284 | wc WC | |
| 285 | }{msg, wc0} | |
| 200 | 286 | } |
| 201 | 287 | |
| 202 | 288 | // Elapsed returns elapsed time decorator. |
| 203 | 289 | // |
| 204 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 205 | // | |
| 206 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 207 | func Elapsed(width, conf int) DecoratorFunc { | |
| 208 | format := "%%" | |
| 209 | if (conf & DidentRight) != 0 { | |
| 210 | format += "-" | |
| 211 | } | |
| 212 | format += "%ds" | |
| 213 | return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 214 | str := fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second) | |
| 215 | if (conf & DwidthSync) != 0 { | |
| 216 | widthAccumulator <- utf8.RuneCountInString(str) | |
| 217 | max := <-widthDistributor | |
| 218 | if (conf & DextraSpace) != 0 { | |
| 219 | max++ | |
| 220 | } | |
| 221 | return fmt.Sprintf(fmt.Sprintf(format, max), str) | |
| 222 | } | |
| 223 | return fmt.Sprintf(fmt.Sprintf(format, width), str) | |
| 224 | } | |
| 290 | // `style` onfe of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] | |
| 291 | // | |
| 292 | // `wc` optional WC config | |
| 293 | func Elapsed(style int, wc ...WC) Decorator { | |
| 294 | var wc0 WC | |
| 295 | if len(wc) > 0 { | |
| 296 | wc0 = wc[0] | |
| 297 | } | |
| 298 | return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 299 | var str string | |
| 300 | hours := int64((s.TimeElapsed / time.Hour) % 60) | |
| 301 | minutes := int64((s.TimeElapsed / time.Minute) % 60) | |
| 302 | seconds := int64((s.TimeElapsed / time.Second) % 60) | |
| 303 | ||
| 304 | switch style { | |
| 305 | case ET_STYLE_GO: | |
| 306 | str = fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second) | |
| 307 | case ET_STYLE_HHMMSS: | |
| 308 | str = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) | |
| 309 | case ET_STYLE_HHMM: | |
| 310 | str = fmt.Sprintf("%02d:%02d", hours, minutes) | |
| 311 | case ET_STYLE_MMSS: | |
| 312 | str = fmt.Sprintf("%02d:%02d", minutes, seconds) | |
| 313 | } | |
| 314 | return wc0.formatMsg(str, widthAccumulator, widthDistributor) | |
| 315 | }) | |
| 225 | 316 | } |
| 226 | 317 | |
| 227 | 318 | // Percentage returns percentage decorator. |
| 228 | 319 | // |
| 229 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 230 | // | |
| 231 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 232 | func Percentage(width, conf int) DecoratorFunc { | |
| 233 | format := "%%" | |
| 234 | if (conf & DidentRight) != 0 { | |
| 235 | format += "-" | |
| 236 | } | |
| 237 | format += "%ds" | |
| 238 | return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 320 | // `wc` optional WC config | |
| 321 | func Percentage(wc ...WC) Decorator { | |
| 322 | var wc0 WC | |
| 323 | if len(wc) > 0 { | |
| 324 | wc0 = wc[0] | |
| 325 | } | |
| 326 | return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 239 | 327 | str := fmt.Sprintf("%d %%", CalcPercentage(s.Total, s.Current, 100)) |
| 240 | if (conf & DwidthSync) != 0 { | |
| 241 | widthAccumulator <- utf8.RuneCountInString(str) | |
| 242 | max := <-widthDistributor | |
| 243 | if (conf & DextraSpace) != 0 { | |
| 244 | max++ | |
| 245 | } | |
| 246 | return fmt.Sprintf(fmt.Sprintf(format, max), str) | |
| 247 | } | |
| 248 | return fmt.Sprintf(fmt.Sprintf(format, width), str) | |
| 249 | } | |
| 328 | return wc0.formatMsg(str, widthAccumulator, widthDistributor) | |
| 329 | }) | |
| 250 | 330 | } |
| 251 | 331 | |
| 252 | 332 | // CalcPercentage is a helper function, to calculate percentage. |
| 273 | 353 | // |
| 274 | 354 | // `unitFormat` printf compatible verb for value, like "%f" or "%d" |
| 275 | 355 | // |
| 276 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 277 | // | |
| 278 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 356 | // `wc` optional WC config | |
| 279 | 357 | // |
| 280 | 358 | // unitFormat example: |
| 281 | 359 | // |
| 282 | 360 | // "%.1f" = "1.0" or "% .1f" = "1.0" |
| 283 | func SpeedNoUnit(unitFormat string, width, conf int) DecoratorFunc { | |
| 284 | return speed(unitFormat, 0, width, conf) | |
| 361 | func SpeedNoUnit(unitFormat string, wc ...WC) Decorator { | |
| 362 | return speed(unitFormat, 0, wc...) | |
| 285 | 363 | } |
| 286 | 364 | |
| 287 | 365 | // SpeedKibiByte returns human friendly I/O operation speed decorator, |
| 288 | 366 | // |
| 289 | 367 | // `unitFormat` printf compatible verb for value, like "%f" or "%d" |
| 290 | 368 | // |
| 291 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 292 | // | |
| 293 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 369 | // `wc` optional WC config | |
| 294 | 370 | // |
| 295 | 371 | // unitFormat example: |
| 296 | 372 | // |
| 297 | 373 | // "%.1f" = "1.0MiB/s" or "% .1f" = "1.0 MiB/s" |
| 298 | func SpeedKibiByte(unitFormat string, width, conf int) DecoratorFunc { | |
| 299 | return speed(unitFormat, unitKiB, width, conf) | |
| 374 | func SpeedKibiByte(unitFormat string, wc ...WC) Decorator { | |
| 375 | return speed(unitFormat, unitKiB, wc...) | |
| 300 | 376 | } |
| 301 | 377 | |
| 302 | 378 | // SpeedKiloByte returns human friendly I/O operation speed decorator, |
| 303 | 379 | // |
| 304 | 380 | // `unitFormat` printf compatible verb for value, like "%f" or "%d" |
| 305 | 381 | // |
| 306 | // `width` width reservation to apply, ignored if `DwidthSync` bit is set | |
| 307 | // | |
| 308 | // `conf` bit set config, [DidentRight|DwidthSync|DextraSpace] | |
| 382 | // `wc` optional WC config | |
| 309 | 383 | // |
| 310 | 384 | // unitFormat example: |
| 311 | 385 | // |
| 312 | 386 | // "%.1f" = "1.0MB/s" or "% .1f" = "1.0 MB/s" |
| 313 | func SpeedKiloByte(unitFormat string, width, conf int) DecoratorFunc { | |
| 314 | return speed(unitFormat, unitKB, width, conf) | |
| 315 | } | |
| 316 | ||
| 317 | func speed(unitFormat string, unit, width, conf int) DecoratorFunc { | |
| 318 | format := "%%" | |
| 319 | if (conf & DidentRight) != 0 { | |
| 320 | format += "-" | |
| 321 | } | |
| 322 | format += "%ds" | |
| 323 | ||
| 324 | return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 387 | func SpeedKiloByte(unitFormat string, wc ...WC) Decorator { | |
| 388 | return speed(unitFormat, unitKB, wc...) | |
| 389 | } | |
| 390 | ||
| 391 | func speed(unitFormat string, unit int, wc ...WC) Decorator { | |
| 392 | var wc0 WC | |
| 393 | if len(wc) > 0 { | |
| 394 | wc0 = wc[0] | |
| 395 | } | |
| 396 | return DecoratorFunc(func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string { | |
| 325 | 397 | var str string |
| 326 | ||
| 327 | 398 | speed := float64(s.Current) / s.TimeElapsed.Seconds() |
| 328 | ||
| 329 | 399 | if math.IsNaN(speed) || math.IsInf(speed, 0) { |
| 330 | 400 | speed = .0 |
| 331 | 401 | } |
| 338 | 408 | default: |
| 339 | 409 | str = fmt.Sprintf(unitFormat, speed) |
| 340 | 410 | } |
| 341 | if (conf & DwidthSync) != 0 { | |
| 342 | widthAccumulator <- utf8.RuneCountInString(str) | |
| 343 | max := <-widthDistributor | |
| 344 | if (conf & DextraSpace) != 0 { | |
| 345 | max++ | |
| 346 | } | |
| 347 | return fmt.Sprintf(fmt.Sprintf(format, max), str) | |
| 348 | } | |
| 349 | return fmt.Sprintf(fmt.Sprintf(format, width), str) | |
| 350 | } | |
| 351 | } | |
| 411 | return wc0.formatMsg(str, widthAccumulator, widthDistributor) | |
| 412 | }) | |
| 413 | } | |
| 8 | 8 | "github.com/vbauerster/mpb/decor" |
| 9 | 9 | ) |
| 10 | 10 | |
| 11 | func TestStaticName(t *testing.T) { | |
| 11 | func TestNameDecorator(t *testing.T) { | |
| 12 | 12 | tests := []struct { |
| 13 | fn decor.DecoratorFunc | |
| 14 | want string | |
| 13 | decorator decor.Decorator | |
| 14 | want string | |
| 15 | 15 | }{ |
| 16 | 16 | { |
| 17 | fn: decor.StaticName("Test", 0, 0), | |
| 18 | want: "Test", | |
| 19 | }, | |
| 20 | { | |
| 21 | fn: decor.StaticName("Test", len("Test"), 0), | |
| 22 | want: "Test", | |
| 23 | }, | |
| 24 | { | |
| 25 | fn: decor.StaticName("Test", 10, 0), | |
| 26 | want: " Test", | |
| 27 | }, | |
| 28 | { | |
| 29 | fn: decor.StaticName("Test", 10, decor.DidentRight), | |
| 30 | want: "Test ", | |
| 17 | decorator: decor.Name("Test"), | |
| 18 | want: "Test", | |
| 19 | }, | |
| 20 | { | |
| 21 | decorator: decor.Name("Test", decor.WC{W: len("Test")}), | |
| 22 | want: "Test", | |
| 23 | }, | |
| 24 | { | |
| 25 | decorator: decor.Name("Test", decor.WC{W: 10}), | |
| 26 | want: " Test", | |
| 27 | }, | |
| 28 | { | |
| 29 | decorator: decor.Name("Test", decor.WC{W: 10, C: decor.DidentRight}), | |
| 30 | want: "Test ", | |
| 31 | 31 | }, |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | 34 | for _, test := range tests { |
| 35 | got := test.fn(nil, nil, nil) | |
| 35 | got := test.decorator.Decor(nil, nil, nil) | |
| 36 | 36 | if got != test.want { |
| 37 | 37 | t.Errorf("Want: %q, Got: %q\n", test.want, got) |
| 38 | 38 | } |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | 42 | type step struct { |
| 43 | stat *decor.Statistics | |
| 44 | dfn decor.DecoratorFunc | |
| 45 | want string | |
| 43 | stat *decor.Statistics | |
| 44 | decorator decor.Decorator | |
| 45 | want string | |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | 48 | func TestPercentageDwidthSync(t *testing.T) { |
| 51 | 51 | []step{ |
| 52 | 52 | { |
| 53 | 53 | &decor.Statistics{Total: 100, Current: 8}, |
| 54 | decor.Percentage(0, decor.DwidthSync), | |
| 54 | decor.Percentage(decor.WCSyncWidth), | |
| 55 | 55 | "8 %", |
| 56 | 56 | }, |
| 57 | 57 | { |
| 58 | 58 | &decor.Statistics{Total: 100, Current: 9}, |
| 59 | decor.Percentage(0, decor.DwidthSync), | |
| 59 | decor.Percentage(decor.WCSyncWidth), | |
| 60 | 60 | "9 %", |
| 61 | 61 | }, |
| 62 | 62 | }, |
| 63 | 63 | []step{ |
| 64 | 64 | { |
| 65 | 65 | &decor.Statistics{Total: 100, Current: 9}, |
| 66 | decor.Percentage(0, decor.DwidthSync), | |
| 66 | decor.Percentage(decor.WCSyncWidth), | |
| 67 | 67 | " 9 %", |
| 68 | 68 | }, |
| 69 | 69 | { |
| 70 | 70 | &decor.Statistics{Total: 100, Current: 10}, |
| 71 | decor.Percentage(0, decor.DwidthSync), | |
| 71 | decor.Percentage(decor.WCSyncWidth), | |
| 72 | 72 | "10 %", |
| 73 | 73 | }, |
| 74 | 74 | }, |
| 75 | 75 | []step{ |
| 76 | 76 | { |
| 77 | 77 | &decor.Statistics{Total: 100, Current: 9}, |
| 78 | decor.Percentage(0, decor.DwidthSync), | |
| 78 | decor.Percentage(decor.WCSyncWidth), | |
| 79 | 79 | " 9 %", |
| 80 | 80 | }, |
| 81 | 81 | { |
| 82 | 82 | &decor.Statistics{Total: 100, Current: 100}, |
| 83 | decor.Percentage(0, decor.DwidthSync), | |
| 83 | decor.Percentage(decor.WCSyncWidth), | |
| 84 | 84 | "100 %", |
| 85 | 85 | }, |
| 86 | 86 | }, |
| 95 | 95 | []step{ |
| 96 | 96 | { |
| 97 | 97 | &decor.Statistics{Total: 100, Current: 8}, |
| 98 | decor.Percentage(0, decor.DwidthSync|decor.DidentRight), | |
| 98 | decor.Percentage(decor.WCSyncWidthR), | |
| 99 | 99 | "8 %", |
| 100 | 100 | }, |
| 101 | 101 | { |
| 102 | 102 | &decor.Statistics{Total: 100, Current: 9}, |
| 103 | decor.Percentage(0, decor.DwidthSync|decor.DidentRight), | |
| 103 | decor.Percentage(decor.WCSyncWidthR), | |
| 104 | 104 | "9 %", |
| 105 | 105 | }, |
| 106 | 106 | }, |
| 107 | 107 | []step{ |
| 108 | 108 | { |
| 109 | 109 | &decor.Statistics{Total: 100, Current: 9}, |
| 110 | decor.Percentage(0, decor.DwidthSync|decor.DidentRight), | |
| 110 | decor.Percentage(decor.WCSyncWidthR), | |
| 111 | 111 | "9 % ", |
| 112 | 112 | }, |
| 113 | 113 | { |
| 114 | 114 | &decor.Statistics{Total: 100, Current: 10}, |
| 115 | decor.Percentage(0, decor.DwidthSync|decor.DidentRight), | |
| 115 | decor.Percentage(decor.WCSyncWidthR), | |
| 116 | 116 | "10 %", |
| 117 | 117 | }, |
| 118 | 118 | }, |
| 119 | 119 | []step{ |
| 120 | 120 | { |
| 121 | 121 | &decor.Statistics{Total: 100, Current: 9}, |
| 122 | decor.Percentage(0, decor.DwidthSync|decor.DidentRight), | |
| 122 | decor.Percentage(decor.WCSyncWidthR), | |
| 123 | 123 | "9 % ", |
| 124 | 124 | }, |
| 125 | 125 | { |
| 126 | 126 | &decor.Statistics{Total: 100, Current: 100}, |
| 127 | decor.Percentage(0, decor.DwidthSync|decor.DidentRight), | |
| 127 | decor.Percentage(decor.WCSyncWidthR), | |
| 128 | 128 | "100 %", |
| 129 | 129 | }, |
| 130 | 130 | }, |
| 139 | 139 | []step{ |
| 140 | 140 | { |
| 141 | 141 | &decor.Statistics{Total: 100, Current: 8}, |
| 142 | decor.Percentage(0, decor.DSyncSpace), | |
| 142 | decor.Percentage(decor.WCSyncSpace), | |
| 143 | 143 | " 8 %", |
| 144 | 144 | }, |
| 145 | 145 | { |
| 146 | 146 | &decor.Statistics{Total: 100, Current: 9}, |
| 147 | decor.Percentage(0, decor.DSyncSpace), | |
| 147 | decor.Percentage(decor.WCSyncSpace), | |
| 148 | 148 | " 9 %", |
| 149 | 149 | }, |
| 150 | 150 | }, |
| 151 | 151 | []step{ |
| 152 | 152 | { |
| 153 | 153 | &decor.Statistics{Total: 100, Current: 9}, |
| 154 | decor.Percentage(0, decor.DSyncSpace), | |
| 154 | decor.Percentage(decor.WCSyncSpace), | |
| 155 | 155 | " 9 %", |
| 156 | 156 | }, |
| 157 | 157 | { |
| 158 | 158 | &decor.Statistics{Total: 100, Current: 10}, |
| 159 | decor.Percentage(0, decor.DSyncSpace), | |
| 159 | decor.Percentage(decor.WCSyncSpace), | |
| 160 | 160 | " 10 %", |
| 161 | 161 | }, |
| 162 | 162 | }, |
| 163 | 163 | []step{ |
| 164 | 164 | { |
| 165 | 165 | &decor.Statistics{Total: 100, Current: 9}, |
| 166 | decor.Percentage(0, decor.DSyncSpace), | |
| 166 | decor.Percentage(decor.WCSyncSpace), | |
| 167 | 167 | " 9 %", |
| 168 | 168 | }, |
| 169 | 169 | { |
| 170 | 170 | &decor.Statistics{Total: 100, Current: 100}, |
| 171 | decor.Percentage(0, decor.DSyncSpace), | |
| 171 | decor.Percentage(decor.WCSyncSpace), | |
| 172 | 172 | " 100 %", |
| 173 | 173 | }, |
| 174 | 174 | }, |
| 196 | 196 | res[i] = make(chan string, 1) |
| 197 | 197 | go func(s step, ch chan string) { |
| 198 | 198 | defer wg.Done() |
| 199 | ch <- s.dfn(s.stat, ws.Accumulator[0], ws.Distributor[0]) | |
| 199 | ch <- s.decorator.Decor(s.stat, ws.Accumulator[0], ws.Distributor[0]) | |
| 200 | 200 | }(columnCase[i], res[i]) |
| 201 | 201 | } |
| 202 | 202 | wg.Wait() |
| 13 | 13 | func Example() { |
| 14 | 14 | p := mpb.New( |
| 15 | 15 | // override default (80) width |
| 16 | mpb.WithWidth(100), | |
| 16 | mpb.WithWidth(64), | |
| 17 | 17 | // override default "[=>-]" format |
| 18 | 18 | mpb.WithFormat("╢▌▌░╟"), |
| 19 | 19 | // override default 120ms refresh rate |
| 20 | mpb.WithRefreshRate(100*time.Millisecond), | |
| 20 | mpb.WithRefreshRate(180*time.Millisecond), | |
| 21 | 21 | ) |
| 22 | 22 | |
| 23 | 23 | total := 100 |
| 24 | 24 | name := "Single Bar:" |
| 25 | startBlock := make(chan time.Time) | |
| 25 | 26 | // adding a single bar |
| 26 | 27 | bar := p.AddBar(int64(total), |
| 27 | 28 | mpb.PrependDecorators( |
| 28 | // Display our static name with one space on the right | |
| 29 | decor.StaticName(name, len(name)+1, decor.DidentRight), | |
| 30 | // ETA decorator with width reservation of 3 runes | |
| 31 | decor.ETA(3, 0), | |
| 29 | // Display our name with one space on the right | |
| 30 | decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), | |
| 31 | // Replace ETA decorator with message, OnComplete event | |
| 32 | decor.OnComplete( | |
| 33 | // ETA decorator with default eta age, and width reservation of 4 | |
| 34 | decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WC{W: 4}), | |
| 35 | "done", | |
| 36 | ), | |
| 32 | 37 | ), |
| 33 | 38 | mpb.AppendDecorators( |
| 34 | // Percentage decorator with width reservation of 5 runes | |
| 35 | decor.Percentage(5, 0), | |
| 39 | decor.Percentage(), | |
| 36 | 40 | ), |
| 37 | 41 | ) |
| 38 | 42 | |
| 39 | 43 | // simulating some work |
| 40 | 44 | max := 100 * time.Millisecond |
| 41 | 45 | for i := 0; i < total; i++ { |
| 42 | bar.StartBlock() // optional call, required for ETA | |
| 46 | // update start block time, required for ETA calculation | |
| 47 | startBlock <- time.Now() | |
| 43 | 48 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) |
| 44 | // increment by 1 (there is bar.IncrBy(int) method, if needed) | |
| 49 | // Increment by 1 (there is bar.IncrBy(int) method, if needed) | |
| 45 | 50 | bar.Increment() |
| 46 | 51 | } |
| 47 | 52 | // wait for our bar to complete and flush |
| 70 | 75 | // Assuming ContentLength > 0 |
| 71 | 76 | bar := p.AddBar(resp.ContentLength, |
| 72 | 77 | mpb.AppendDecorators( |
| 73 | decor.CountersKibiByte("%6.1f / %6.1f", 12, 0), | |
| 78 | decor.CountersKibiByte("%6.1f / %6.1f"), | |
| 74 | 79 | ), |
| 75 | 80 | ) |
| 76 | 81 | |
| 31 | 31 | |
| 32 | 32 | for i := 0; i < numBars; i++ { |
| 33 | 33 | name := fmt.Sprintf("Bar#%d:", i) |
| 34 | startBlock := make(chan time.Time) | |
| 34 | 35 | bar := p.AddBar(int64(total), |
| 35 | 36 | mpb.PrependDecorators( |
| 36 | decor.StaticName(name, 0, decor.DwidthSync|decor.DidentRight), | |
| 37 | decor.ETA(4, decor.DSyncSpace), | |
| 37 | decor.Name(name), | |
| 38 | decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WCSyncSpace), | |
| 38 | 39 | ), |
| 39 | 40 | mpb.AppendDecorators( |
| 40 | decor.Percentage(5, 0), | |
| 41 | decor.Percentage(decor.WC{W: 5}), | |
| 41 | 42 | ), |
| 42 | 43 | ) |
| 43 | 44 | |
| 45 | 46 | defer wg.Done() |
| 46 | 47 | max := 100 * time.Millisecond |
| 47 | 48 | for !bar.Completed() { |
| 48 | bar.StartBlock() | |
| 49 | startBlock <- time.Now() | |
| 49 | 50 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) |
| 50 | 51 | bar.Increment() |
| 51 | 52 | } |
| 29 | 29 | b := p.AddBar(rand.Int63n(201)+100, |
| 30 | 30 | mpb.BarRemoveOnComplete(), |
| 31 | 31 | mpb.PrependDecorators( |
| 32 | decor.StaticName(task, len(task)+1, decor.DidentRight), | |
| 33 | decor.StaticName(job, 0, decor.DSyncSpaceR), | |
| 34 | decor.CountersNoUnit("%d / %d", 0, decor.DwidthSync), | |
| 32 | decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}), | |
| 33 | decor.Name(job, decor.WCSyncSpaceR), | |
| 34 | decor.CountersNoUnit("%d / %d", decor.WCSyncWidth), | |
| 35 | 35 | ), |
| 36 | mpb.AppendDecorators(decor.Percentage(5, 0)), | |
| 36 | mpb.AppendDecorators(decor.Percentage(decor.WC{W: 5})), | |
| 37 | 37 | ) |
| 38 | go newTask(wg, b, i+1) | |
| 38 | go newTask(wg, b, i+1, nil) | |
| 39 | 39 | bars = append(bars, b) |
| 40 | 40 | } |
| 41 | 41 | |
| 43 | 43 | doneWg.Add(1) |
| 44 | 44 | i := i |
| 45 | 45 | go func() { |
| 46 | startBlock := make(chan time.Time) | |
| 46 | 47 | task := fmt.Sprintf("Task#%02d:", i) |
| 47 | 48 | job := "installing" |
| 48 | 49 | // preparing delayed bars |
| 50 | 51 | mpb.BarReplaceOnComplete(bars[i]), |
| 51 | 52 | mpb.BarClearOnComplete(), |
| 52 | 53 | mpb.PrependDecorators( |
| 53 | decor.StaticName(task, len(task)+1, decor.DidentRight), | |
| 54 | decor.OnComplete(decor.StaticName(job, 0, decor.DSyncSpaceR), "done!", 0, decor.DSyncSpaceR), | |
| 55 | decor.OnComplete(decor.ETA(0, decor.DwidthSync), "", 0, decor.DwidthSync), | |
| 54 | decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}), | |
| 55 | decor.OnComplete(decor.Name(job, decor.WCSyncSpaceR), "done!", decor.WCSyncSpaceR), | |
| 56 | decor.OnComplete( | |
| 57 | decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WCSyncWidth), | |
| 58 | "", | |
| 59 | decor.WCSyncSpace, | |
| 60 | ), | |
| 56 | 61 | ), |
| 57 | 62 | mpb.AppendDecorators( |
| 58 | decor.OnComplete(decor.Percentage(5, 0), "", 0, 0), | |
| 63 | decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""), | |
| 59 | 64 | ), |
| 60 | 65 | ) |
| 61 | 66 | // waiting for download to complete, before starting install job |
| 62 | 67 | downloadWgg[i].Wait() |
| 63 | go newTask(doneWg, b, numBars-i) | |
| 68 | go newTask(doneWg, b, numBars-i, startBlock) | |
| 64 | 69 | }() |
| 65 | 70 | } |
| 66 | 71 | |
| 67 | 72 | p.Wait() |
| 68 | 73 | } |
| 69 | 74 | |
| 70 | func newTask(wg *sync.WaitGroup, b *mpb.Bar, incrBy int) { | |
| 75 | func newTask(wg *sync.WaitGroup, b *mpb.Bar, incrBy int, startBlock chan<- time.Time) { | |
| 71 | 76 | defer wg.Done() |
| 72 | 77 | max := 100 * time.Millisecond |
| 73 | 78 | for !b.Completed() { |
| 74 | b.StartBlock() | |
| 79 | if startBlock != nil { | |
| 80 | startBlock <- time.Now() | |
| 81 | } | |
| 75 | 82 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) |
| 76 | 83 | b.IncrBy(incrBy) |
| 77 | 84 | } |
| 21 | 21 | // trigger total auto increment by 1, when 18 % remains till bar completion |
| 22 | 22 | mpb.BarAutoIncrTotal(18, 1), |
| 23 | 23 | mpb.PrependDecorators( |
| 24 | decor.CountersNoUnit("%d / %d", 12, 0), | |
| 24 | decor.CountersNoUnit("%d / %d", decor.WC{W: 12}), | |
| 25 | 25 | ), |
| 26 | 26 | mpb.AppendDecorators( |
| 27 | decor.Percentage(4, 0), | |
| 27 | decor.Percentage(), | |
| 28 | 28 | ), |
| 29 | 29 | ) |
| 30 | 30 |
| 7 | 7 | "os" |
| 8 | 8 | "path/filepath" |
| 9 | 9 | "sync" |
| 10 | "time" | |
| 10 | 11 | |
| 11 | 12 | "github.com/vbauerster/mpb" |
| 12 | 13 | "github.com/vbauerster/mpb/decor" |
| 56 | 57 | return |
| 57 | 58 | } |
| 58 | 59 | |
| 60 | startBlock := make(chan time.Time) | |
| 61 | ||
| 59 | 62 | // create bar with appropriate decorators |
| 60 | 63 | bar := p.AddBar(size, mpb.BarPriority(n), |
| 61 | 64 | mpb.PrependDecorators( |
| 62 | decor.StaticName(name, len(name)+1, decor.DidentRight), | |
| 63 | decor.CountersKibiByte("%6.1f / %6.1f", 0, decor.DwidthSync), | |
| 65 | decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), | |
| 66 | decor.CountersKibiByte("%6.1f / %6.1f", decor.WCSyncWidth), | |
| 64 | 67 | ), |
| 65 | 68 | mpb.AppendDecorators( |
| 66 | decor.ETA(0, decor.DwidthSync), | |
| 67 | decor.SpeedKibiByte("%6.1f", 18, 0), | |
| 69 | decor.ETA(decor.ET_STYLE_HHMMSS, 60, startBlock, decor.WCSyncWidth), | |
| 70 | decor.SpeedKibiByte("%6.1f", decor.WCSyncSpace), | |
| 68 | 71 | ), |
| 69 | 72 | ) |
| 70 | 73 | |
| 71 | 74 | // create proxy reader |
| 72 | reader := bar.ProxyReader(resp.Body) | |
| 75 | reader := bar.ProxyReader(resp.Body, startBlock) | |
| 73 | 76 | // and copy from reader |
| 74 | 77 | _, err = io.Copy(dest, reader) |
| 75 | 78 | |
| 5 | 5 | "net/http" |
| 6 | 6 | "os" |
| 7 | 7 | "path/filepath" |
| 8 | "time" | |
| 8 | 9 | |
| 9 | 10 | "github.com/vbauerster/mpb" |
| 10 | 11 | "github.com/vbauerster/mpb/decor" |
| 11 | 12 | ) |
| 12 | 13 | |
| 13 | 14 | func main() { |
| 14 | url := "https://homebrew.bintray.com/bottles/libtiff-4.0.7.sierra.bottle.tar.gz" | |
| 15 | url := "https://github.com/onivim/oni/releases/download/v0.3.4/Oni-0.3.4-osx.dmg" | |
| 15 | 16 | |
| 16 | 17 | resp, err := http.Get(url) |
| 17 | 18 | if err != nil { |
| 37 | 38 | |
| 38 | 39 | p := mpb.New(mpb.WithWidth(64)) |
| 39 | 40 | |
| 41 | startBlock := make(chan time.Time) | |
| 40 | 42 | bar := p.AddBar(size, |
| 41 | 43 | mpb.PrependDecorators( |
| 42 | decor.CountersKibiByte("% 6.1f / % 6.1f", 18, 0), | |
| 44 | decor.CountersKibiByte("% 6.1f / % 6.1f", decor.WC{W: 18}), | |
| 43 | 45 | ), |
| 44 | 46 | mpb.AppendDecorators( |
| 45 | decor.ETA(0, 0), | |
| 46 | decor.SpeedKibiByte("% 6.1f", 18, 0), | |
| 47 | decor.ETA(decor.ET_STYLE_HHMMSS, 900, startBlock), | |
| 48 | decor.SpeedKibiByte("% 6.1f", decor.WC{W: 14}), | |
| 47 | 49 | ), |
| 48 | 50 | ) |
| 49 | 51 | |
| 50 | 52 | // create proxy reader |
| 51 | reader := bar.ProxyReader(resp.Body) | |
| 53 | reader := bar.ProxyReader(resp.Body, startBlock) | |
| 52 | 54 | |
| 53 | 55 | // and copy from reader, ignoring errors |
| 54 | 56 | io.Copy(dest, reader) |
| 20 | 20 | for i := 0; i < numBars; i++ { |
| 21 | 21 | name := fmt.Sprintf("b#%02d:", i) |
| 22 | 22 | bar := p.AddBar(100, mpb.BarID(i), mpb.PrependDecorators( |
| 23 | func(s *decor.Statistics, _ chan<- int, _ <-chan int) string { | |
| 23 | decor.DecoratorFunc(func(s *decor.Statistics, _ chan<- int, _ <-chan int) string { | |
| 24 | 24 | // s.Current == 42 may never happen, if sleep btw increments is |
| 25 | 25 | // too short, thus using s.Current >= 42 |
| 26 | 26 | if s.ID == 1 && s.Current >= 42 { |
| 27 | 27 | panic(wantPanic) |
| 28 | 28 | } |
| 29 | 29 | return name |
| 30 | }, | |
| 30 | }), | |
| 31 | 31 | )) |
| 32 | 32 | |
| 33 | 33 | go func() { |
| 25 | 25 | if i != 1 { |
| 26 | 26 | name = fmt.Sprintf("Bar#%d:", i) |
| 27 | 27 | } |
| 28 | startBlock := make(chan time.Time) | |
| 28 | 29 | b := p.AddBar(int64(total), |
| 29 | 30 | mpb.PrependDecorators( |
| 30 | decor.StaticName(name, 0, decor.DwidthSync), | |
| 31 | decor.OnComplete(decor.ETA(4, 0), "Done", 0, decor.DSyncSpace), | |
| 31 | decor.Name(name, decor.WCSyncWidth), | |
| 32 | decor.OnComplete( | |
| 33 | decor.ETA(decor.ET_STYLE_MMSS, 0, startBlock, decor.WC{W: 6}), | |
| 34 | "Done", | |
| 35 | decor.WCSyncSpace, | |
| 36 | ), | |
| 32 | 37 | ), |
| 33 | 38 | mpb.AppendDecorators( |
| 34 | decor.Percentage(5, 0), | |
| 39 | decor.Percentage(decor.WC{W: 5}), | |
| 35 | 40 | ), |
| 36 | 41 | ) |
| 37 | 42 | go func() { |
| 38 | 43 | defer wg.Done() |
| 39 | 44 | max := 100 * time.Millisecond |
| 40 | 45 | for i := 0; i < total; i++ { |
| 41 | b.StartBlock() | |
| 46 | startBlock <- time.Now() | |
| 42 | 47 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) |
| 43 | 48 | b.Increment() |
| 44 | 49 | } |
| 27 | 27 | } |
| 28 | 28 | b := p.AddBar(int64(total), |
| 29 | 29 | mpb.PrependDecorators( |
| 30 | decor.StaticName(name, 0, decor.DwidthSync|decor.DidentRight), | |
| 31 | decor.Elapsed(3, decor.DSyncSpace), | |
| 30 | decor.Name(name, decor.WCSyncWidthR), | |
| 31 | decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncSpace), | |
| 32 | 32 | ), |
| 33 | 33 | mpb.AppendDecorators( |
| 34 | decor.Percentage(5, 0), | |
| 34 | decor.Percentage(decor.WC{W: 5}), | |
| 35 | 35 | ), |
| 36 | 36 | ) |
| 37 | 37 | go func() { |
| 21 | 21 | wg.Add(numBars) |
| 22 | 22 | |
| 23 | 23 | for i := 0; i < numBars; i++ { |
| 24 | var name string | |
| 25 | name = fmt.Sprintf("Bar#%d:", i) | |
| 24 | name := fmt.Sprintf("Bar#%d:", i) | |
| 26 | 25 | |
| 27 | 26 | var bOption mpb.BarOption |
| 28 | 27 | if i == 0 { |
| 29 | 28 | bOption = mpb.BarRemoveOnComplete() |
| 30 | 29 | } |
| 31 | 30 | |
| 31 | startBlock := make(chan time.Time) | |
| 32 | 32 | b := p.AddBar(int64(total), mpb.BarID(i), |
| 33 | 33 | bOption, |
| 34 | 34 | mpb.PrependDecorators( |
| 35 | decor.StaticName(name, 0, decor.DwidthSync|decor.DidentRight), | |
| 36 | decor.ETA(4, decor.DSyncSpace), | |
| 35 | decor.Name(name), | |
| 36 | decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WCSyncSpace), | |
| 37 | 37 | ), |
| 38 | 38 | mpb.AppendDecorators( |
| 39 | decor.Percentage(5, 0), | |
| 39 | decor.Percentage(), | |
| 40 | 40 | ), |
| 41 | 41 | ) |
| 42 | 42 | go func() { |
| 43 | 43 | defer wg.Done() |
| 44 | 44 | max := 100 * time.Millisecond |
| 45 | 45 | for i := 0; i < total; i++ { |
| 46 | b.StartBlock() | |
| 46 | startBlock <- time.Now() | |
| 47 | 47 | if b.ID() == 2 && i == 42 { |
| 48 | 48 | p.Abort(b) |
| 49 | 49 | return |
| 21 | 21 | |
| 22 | 22 | for i := 0; i < numBars; i++ { |
| 23 | 23 | name := fmt.Sprintf("Bar#%d:", i) |
| 24 | startBlock := make(chan time.Time) | |
| 24 | 25 | bar := p.AddBar(int64(total), |
| 25 | 26 | mpb.PrependDecorators( |
| 26 | // Display our static name with one space on the right | |
| 27 | decor.StaticName(name, len(name)+1, decor.DidentRight), | |
| 28 | // DwidthSync bit enables same column width synchronization | |
| 29 | decor.Percentage(0, decor.DwidthSync), | |
| 27 | // Display our name with one space on the right | |
| 28 | decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), | |
| 29 | // decor.DSyncWidth bit enables same column width synchronization | |
| 30 | decor.Percentage(decor.WCSyncWidth), | |
| 30 | 31 | ), |
| 31 | 32 | mpb.AppendDecorators( |
| 32 | // replace our ETA decorator with "done!", on bar completion event | |
| 33 | decor.OnComplete(decor.ETA(3, 0), "done!", 0, 0), | |
| 33 | // Replace ETA decorator with message, OnComplete event | |
| 34 | decor.OnComplete( | |
| 35 | // ETA decorator with default eta age, and width reservation of 3 | |
| 36 | decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WC{W: 3}), | |
| 37 | "done!", | |
| 38 | ), | |
| 34 | 39 | ), |
| 35 | 40 | ) |
| 36 | 41 | // simulating some work |
| 38 | 43 | defer wg.Done() |
| 39 | 44 | max := 100 * time.Millisecond |
| 40 | 45 | for i := 0; i < total; i++ { |
| 41 | bar.StartBlock() | |
| 46 | startBlock <- time.Now() | |
| 42 | 47 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) |
| 43 | 48 | bar.Increment() |
| 44 | 49 | } |
| 45 | 50 | }() |
| 46 | 51 | } |
| 47 | // first wait for provided wg, then | |
| 48 | 52 | // wait for all bars to complete and flush |
| 49 | 53 | p.Wait() |
| 50 | 54 | } |
| 19 | 19 | |
| 20 | 20 | total := 100 |
| 21 | 21 | name := "Single Bar:" |
| 22 | startBlock := make(chan time.Time) | |
| 22 | 23 | // adding a single bar |
| 23 | 24 | bar := p.AddBar(int64(total), |
| 24 | 25 | mpb.PrependDecorators( |
| 25 | // Display our static name with one space on the right | |
| 26 | decor.StaticName(name, len(name)+1, decor.DidentRight), | |
| 27 | // ETA decorator with width reservation of 3 runes | |
| 28 | // decor.ETA(3, 0), | |
| 29 | decor.OnComplete(decor.ETA(4, 0), "done", 0, 0), | |
| 26 | // Display our name with one space on the right | |
| 27 | decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), | |
| 28 | // Replace ETA decorator with message, OnComplete event | |
| 29 | decor.OnComplete( | |
| 30 | // ETA decorator with default eta age, and width reservation of 4 | |
| 31 | decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WC{W: 4}), | |
| 32 | "done", | |
| 33 | ), | |
| 30 | 34 | ), |
| 31 | 35 | mpb.AppendDecorators( |
| 32 | // Percentage decorator with width reservation of 5 runes | |
| 33 | decor.Percentage(5, 0), | |
| 36 | decor.Percentage(), | |
| 34 | 37 | ), |
| 35 | 38 | ) |
| 36 | 39 | |
| 37 | 40 | // simulating some work |
| 38 | 41 | max := 100 * time.Millisecond |
| 39 | 42 | for i := 0; i < total; i++ { |
| 40 | bar.StartBlock() // optional call, required for ETA | |
| 43 | // update start block time, required for ETA calculation | |
| 44 | startBlock <- time.Now() | |
| 41 | 45 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) |
| 42 | 46 | // Increment by 1 (there is bar.IncrBy(int) method, if needed) |
| 43 | 47 | bar.Increment() |
| 25 | 25 | if i != 1 { |
| 26 | 26 | name = fmt.Sprintf("Bar#%d:", i) |
| 27 | 27 | } |
| 28 | startBlock := make(chan time.Time) | |
| 28 | 29 | b := p.AddBar(int64(total), |
| 29 | 30 | mpb.PrependDecorators( |
| 30 | decor.StaticName(name, 0, decor.DwidthSync), | |
| 31 | decor.CountersNoUnit("%d / %d", 10, decor.DSyncSpace), | |
| 31 | decor.Name(name, decor.WCSyncWidth), | |
| 32 | decor.CountersNoUnit("%d / %d", decor.WCSyncSpace), | |
| 32 | 33 | ), |
| 33 | 34 | mpb.AppendDecorators( |
| 34 | decor.ETA(3, 0), | |
| 35 | decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WC{W: 3}), | |
| 35 | 36 | ), |
| 36 | 37 | ) |
| 37 | 38 | go func() { |
| 38 | 39 | defer wg.Done() |
| 39 | 40 | max := 100 * time.Millisecond |
| 40 | 41 | for i := 0; i < total; i++ { |
| 41 | b.StartBlock() | |
| 42 | startBlock <- time.Now() | |
| 42 | 43 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) |
| 43 | 44 | if i&1 == 1 { |
| 44 | 45 | priority := total - int(b.Current()) |
| 26 | 26 | for i := 0; i < totalBars; i++ { |
| 27 | 27 | name := fmt.Sprintf("Bar#%02d: ", i) |
| 28 | 28 | total := rand.Intn(120) + 10 |
| 29 | startBlock := make(chan time.Time) | |
| 29 | 30 | bar := p.AddBar(int64(total), |
| 30 | 31 | mpb.PrependDecorators( |
| 31 | decor.StaticName(name, len(name), 0), | |
| 32 | decor.ETA(4, decor.DSyncSpace), | |
| 32 | decor.Name(name), | |
| 33 | decor.ETA(decor.ET_STYLE_GO, 0, startBlock, decor.WCSyncSpace), | |
| 33 | 34 | ), |
| 34 | 35 | mpb.AppendDecorators( |
| 35 | decor.Percentage(5, 0), | |
| 36 | decor.Percentage(decor.WC{W: 5}), | |
| 36 | 37 | ), |
| 37 | 38 | ) |
| 38 | 39 | |
| 40 | 41 | defer wg.Done() |
| 41 | 42 | max := 100 * time.Millisecond |
| 42 | 43 | for i := 0; i < total; i++ { |
| 43 | bar.StartBlock() | |
| 44 | startBlock <- time.Now() | |
| 44 | 45 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) |
| 45 | 46 | bar.Increment() |
| 46 | 47 | } |
| 0 | 0 | package mpb |
| 1 | 1 | |
| 2 | import "io" | |
| 2 | import ( | |
| 3 | "io" | |
| 4 | "time" | |
| 5 | ) | |
| 3 | 6 | |
| 4 | 7 | // Reader is io.Reader wrapper, for proxy read bytes |
| 5 | 8 | type Reader struct { |
| 6 | 9 | io.Reader |
| 7 | bar *Bar | |
| 10 | bar *Bar | |
| 11 | startBlockCh chan<- time.Time | |
| 8 | 12 | } |
| 9 | 13 | |
| 10 | 14 | func (r *Reader) Read(p []byte) (int, error) { |
| 11 | r.bar.StartBlock() | |
| 15 | if r.startBlockCh != nil { | |
| 16 | r.startBlockCh <- time.Now() | |
| 17 | } | |
| 12 | 18 | n, err := r.Reader.Read(p) |
| 13 | 19 | r.bar.IncrBy(n) |
| 14 | 20 | return n, err |