pax_global_header00006660000000000000000000000064146631275730014530gustar00rootroot0000000000000052 comment=99ffa837971b7a12db44ff49c3de4c54dac257a0 mpb-8.8.3/000077500000000000000000000000001466312757300123265ustar00rootroot00000000000000mpb-8.8.3/.github/000077500000000000000000000000001466312757300136665ustar00rootroot00000000000000mpb-8.8.3/.github/workflows/000077500000000000000000000000001466312757300157235ustar00rootroot00000000000000mpb-8.8.3/.github/workflows/golangci-lint.yml000066400000000000000000000020031466312757300211700ustar00rootroot00000000000000name: golangci-lint on: push: tags: - v* branches: - master - main pull_request: permissions: contents: read # Optional: allow read access to pull request. Use with `only-new-issues` option. pull-requests: read jobs: golangci: strategy: matrix: go-version: ['stable'] os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} cache: false - uses: golangci/golangci-lint-action@v6 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: latest # Optional: golangci-lint command line arguments. # args: --issues-exit-code=0 # Optional: show only new issues if it's a pull request. The default value is `false`. only-new-issues: true mpb-8.8.3/.github/workflows/test.yml000066400000000000000000000007561466312757300174350ustar00rootroot00000000000000name: test on: push: tags: - v* branches: - master - main pull_request: permissions: contents: read pull-requests: read jobs: test: strategy: matrix: go-version: ['stable', 'oldstable'] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - run: go test -race ./... mpb-8.8.3/.gitignore000066400000000000000000000001731466312757300143170ustar00rootroot00000000000000# Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out mpb-8.8.3/CONTRIBUTING000066400000000000000000000012011466312757300141520ustar00rootroot00000000000000When contributing your first changes, please include an empty commit for copyright waiver using the following message (replace 'John Doe' with your name or nickname): John Doe Copyright Waiver I dedicate any and all copyright interest in this software to the public domain. I make this dedication for the benefit of the public at large and to the detriment of my heirs and successors. I intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. The command to create an empty commit from the command-line is: git commit --allow-empty mpb-8.8.3/README.md000066400000000000000000000101021466312757300135770ustar00rootroot00000000000000# Multi Progress Bar [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v8) [![Test status](https://github.com/vbauerster/mpb/actions/workflows/test.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/test.yml) [![Lint status](https://github.com/vbauerster/mpb/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/golangci-lint.yml) **mpb** is a Go lib for rendering progress bars in terminal applications. ## Features - **Multiple Bars**: Multiple progress bars are supported - **Dynamic Total**: Set total while bar is running - **Dynamic Add/Remove**: Dynamically add or remove bars - **Cancellation**: Cancel whole rendering process - **Predefined Decorators**: Elapsed time, [ewma](https://github.com/VividCortex/ewma) based ETA, Percentage, Bytes counter - **Decorator's width sync**: Synchronized decorator's width among multiple bars ## Usage #### [Rendering single bar](_examples/singleBar/main.go) ```go package main import ( "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { // initialize progress container, with custom width p := mpb.New(mpb.WithWidth(64)) total := 100 name := "Single Bar:" // create a single bar, which will inherit container's width bar := p.New(int64(total), // BarFillerBuilder with custom style mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"), mpb.PrependDecorators( // display our name with one space on the right decor.Name(name, decor.WC{C: decor.DindentRight | decor.DextraSpace}), // replace ETA decorator with "done" message, OnComplete event decor.OnComplete(decor.AverageETA(decor.ET_STYLE_GO), "done"), ), mpb.AppendDecorators(decor.Percentage()), ) // simulating some work max := 100 * time.Millisecond for i := 0; i < total; i++ { time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } // wait for our bar to complete and flush p.Wait() } ``` #### [Rendering multiple bars](_examples/multiBars/main.go) ```go var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New(mpb.WithWaitGroup(&wg)) total, numBars := 100, 3 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(int64(total), mpb.PrependDecorators( // simple name decorator decor.Name(name), // decor.DSyncWidth bit enables column width synchronization decor.Percentage(decor.WCSyncSpace), ), mpb.AppendDecorators( // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( // ETA decorator with ewma age of 30 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done", ), ), ) // simulating some work go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() ``` #### [Dynamic total](_examples/dynTotal/main.go) ![dynamic total](_svg/godEMrCZmJkHYH1X9dN4Nm0U7.svg) #### [Complex example](_examples/complex/main.go) ![complex](_svg/wHzf1M7sd7B3zVa2scBMnjqRf.svg) #### [Bytes counters](_examples/io/main.go) ![byte counters](_svg/hIpTa3A5rQz65ssiVuRJu87X6.svg) mpb-8.8.3/UNLICENSE000066400000000000000000000022731466312757300136020ustar00rootroot00000000000000This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to mpb-8.8.3/_examples/000077500000000000000000000000001466312757300143035ustar00rootroot00000000000000mpb-8.8.3/_examples/.gitignore000066400000000000000000000000071466312757300162700ustar00rootroot00000000000000go.sum mpb-8.8.3/_examples/barExtender/000077500000000000000000000000001466312757300165465ustar00rootroot00000000000000mpb-8.8.3/_examples/barExtender/go.mod000066400000000000000000000005741466312757300176620ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/barExtender go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/barExtender/main.go000066400000000000000000000031241466312757300200210ustar00rootroot00000000000000package main import ( "fmt" "io" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New(mpb.WithWaitGroup(&wg)) total, numBars := 100, 3 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) efn := func(w io.Writer, s decor.Statistics) (err error) { if s.Completed { _, err = fmt.Fprintf(w, "Bar id: %d has been completed\n", s.ID) } return err } bar := p.AddBar(int64(total), mpb.BarExtender(mpb.BarFillerFunc(efn), false), mpb.PrependDecorators( // simple name decorator decor.Name(name), // decor.DSyncWidth bit enables column width synchronization decor.Percentage(decor.WCSyncSpace), ), mpb.AppendDecorators( // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( // ETA decorator with ewma age of 30 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done", ), ), ) // simulating some work go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/barExtenderRev/000077500000000000000000000000001466312757300172235ustar00rootroot00000000000000mpb-8.8.3/_examples/barExtenderRev/go.mod000066400000000000000000000005771466312757300203420ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/barExtenderRev go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/barExtenderRev/main.go000066400000000000000000000052151466312757300205010ustar00rootroot00000000000000package main import ( "fmt" "io" "math/rand" "sync/atomic" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) var curTask uint32 var doneTasks uint32 type task struct { id uint32 total int64 bar *mpb.Bar } func main() { numTasks := 4 var total int64 var filler mpb.BarFiller tasks := make([]*task, numTasks) for i := 0; i < numTasks; i++ { task := &task{ id: uint32(i), total: rand.Int63n(666) + 100, } total += task.total filler = middleware(filler, task.id) tasks[i] = task } filler = newLineMiddleware(filler) p := mpb.New() for i := 0; i < numTasks; i++ { bar := p.AddBar(tasks[i].total, mpb.BarExtender(filler, true), // all bars share same extender filler mpb.BarFuncOptional(func() mpb.BarOption { return mpb.BarQueueAfter(tasks[i-1].bar) }, i != 0), mpb.PrependDecorators( decor.Name("current:", decor.WCSyncWidthR), ), mpb.AppendDecorators( decor.Percentage(decor.WCSyncWidth), ), ) tasks[i].bar = bar } tb := p.AddBar(0, mpb.PrependDecorators( decor.Any(func(st decor.Statistics) string { return fmt.Sprintf("TOTAL(%d/%d)", atomic.LoadUint32(&doneTasks), len(tasks)) }, decor.WCSyncWidthR), ), mpb.AppendDecorators( decor.Percentage(decor.WCSyncWidth), ), ) tb.SetTotal(total, false) for _, t := range tasks { atomic.StoreUint32(&curTask, t.id) complete(t.bar, tb) atomic.AddUint32(&doneTasks, 1) } tb.EnableTriggerComplete() p.Wait() } func middleware(base mpb.BarFiller, id uint32) mpb.BarFiller { var done bool fn := func(w io.Writer, st decor.Statistics) error { if !done { if atomic.LoadUint32(&curTask) != id { _, err := fmt.Fprintf(w, " Taksk %02d\n", id) return err } if !st.Completed { _, err := fmt.Fprintf(w, "=> Taksk %02d\n", id) return err } done = true } _, err := fmt.Fprintf(w, " Taksk %02d: Done!\n", id) return err } if base == nil { return mpb.BarFillerFunc(fn) } return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error { err := fn(w, st) if err != nil { return err } return base.Fill(w, st) }) } func newLineMiddleware(base mpb.BarFiller) mpb.BarFiller { return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error { _, err := fmt.Fprintln(w) if err != nil { return err } return base.Fill(w, st) }) } func complete(bar, totalBar *mpb.Bar) { max := 100 * time.Millisecond for !bar.Completed() { n := rand.Int63n(10) + 1 incrementBars(n, bar, totalBar) time.Sleep(time.Duration(n) * max / 10) } bar.Wait() } func incrementBars(n int64, bb ...*mpb.Bar) { for _, b := range bb { b.IncrInt64(n) } } mpb-8.8.3/_examples/cancel/000077500000000000000000000000001466312757300155305ustar00rootroot00000000000000mpb-8.8.3/_examples/cancel/go.mod000066400000000000000000000005671466312757300166460ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/cancel go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/cancel/main.go000066400000000000000000000025261466312757300170100ustar00rootroot00000000000000package main import ( "context" "fmt" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.NewWithContext(ctx, mpb.WithWaitGroup(&wg)) total := 300 numBars := 3 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%02d: ", i) bar := p.AddBar(int64(total), mpb.PrependDecorators( decor.Name(name, decor.WCSyncWidthR), decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), ), mpb.AppendDecorators( // note that OnComplete will not be fired, because of cancel decor.OnComplete(decor.Percentage(decor.WC{W: 5}), "done"), ), ) go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for bar.IsRunning() { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/complex/000077500000000000000000000000001466312757300157525ustar00rootroot00000000000000mpb-8.8.3/_examples/complex/go.mod000066400000000000000000000010001466312757300170470ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/complex go 1.17 require ( github.com/fatih/color v1.17.0 github.com/vbauerster/mpb/v8 v8.8.3 ) require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/complex/main.go000066400000000000000000000040061466312757300172250ustar00rootroot00000000000000package main import ( "fmt" "math/rand" "time" "github.com/fatih/color" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { numBars := 4 // to support color in Windows following both options are required p := mpb.New( mpb.WithOutput(color.Output), mpb.WithAutoRefresh(), ) red, green := color.New(color.FgRed), color.New(color.FgGreen) for i := 0; i < numBars; i++ { task := fmt.Sprintf("Task#%02d:", i) queue := make([]*mpb.Bar, 2) queue[0] = p.AddBar(rand.Int63n(201)+100, mpb.PrependDecorators( decor.Name(task, decor.WC{C: decor.DindentRight | decor.DextraSpace}), decor.Name("downloading", decor.WCSyncSpaceR), decor.CountersNoUnit("%d / %d", decor.WCSyncWidth), ), mpb.AppendDecorators( decor.OnComplete(decor.Percentage(decor.WC{W: 5}), "done"), ), ) queue[1] = p.AddBar(rand.Int63n(101)+100, mpb.BarQueueAfter(queue[0]), // this bar is queued mpb.BarFillerClearOnComplete(), mpb.PrependDecorators( decor.Name(task, decor.WC{C: decor.DindentRight | decor.DextraSpace}), decor.OnCompleteMeta( decor.OnComplete( decor.Meta(decor.Name("installing", decor.WCSyncSpaceR), toMetaFunc(red)), "done!", ), toMetaFunc(green), ), decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_MMSS, 0, decor.WCSyncWidth), ""), ), mpb.AppendDecorators( decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""), ), ) go func() { for _, b := range queue { complete(b) } }() } p.Wait() } func complete(bar *mpb.Bar) { max := 100 * time.Millisecond for !bar.Completed() { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrInt64(rand.Int63n(5)+1, time.Since(start)) } } func toMetaFunc(c *color.Color) func(string) string { return func(s string) string { return c.Sprint(s) } } mpb-8.8.3/_examples/decoratorsOnTop/000077500000000000000000000000001466312757300174305ustar00rootroot00000000000000mpb-8.8.3/_examples/decoratorsOnTop/go.mod000066400000000000000000000006001466312757300205320ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/decoratorsOnTop go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/decoratorsOnTop/main.go000066400000000000000000000020401466312757300206770ustar00rootroot00000000000000package main import ( "io" "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { p := mpb.New() total := 100 bar := p.New(int64(total), mpb.NopStyle(), // make main bar style nop, so there are just decorators mpb.BarExtender(extended(mpb.BarStyle().Build()), false), // extend wtih normal bar on the next line mpb.PrependDecorators( decor.Name("Percentage: "), decor.NewPercentage("%d"), ), mpb.AppendDecorators( decor.Name("ETA: "), decor.OnComplete( decor.AverageETA(decor.ET_STYLE_GO), "done", ), ), ) // simulating some work max := 100 * time.Millisecond for i := 0; i < total; i++ { time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } // wait for our bar to complete and flush p.Wait() } func extended(base mpb.BarFiller) mpb.BarFiller { return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error { err := base.Fill(w, st) if err != nil { return err } _, err = io.WriteString(w, "\n") return err }) } mpb-8.8.3/_examples/differentWidth/000077500000000000000000000000001466312757300172515ustar00rootroot00000000000000mpb-8.8.3/_examples/differentWidth/go.mod000066400000000000000000000005771466312757300203700ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/differentWidth go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/differentWidth/main.go000066400000000000000000000027341466312757300205320ustar00rootroot00000000000000package main import ( "fmt" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New( mpb.WithWaitGroup(&wg), mpb.WithWidth(60), ) total, numBars := 100, 3 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(int64(total), // set BarWidth 40 for bar 1 and 2 mpb.BarOptional(mpb.BarWidth(40), i > 0), mpb.PrependDecorators( // simple name decorator decor.Name(name), // decor.DSyncWidth bit enables column width synchronization decor.Percentage(decor.WCSyncSpace), ), mpb.AppendDecorators( // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( // ETA decorator with ewma age of 30 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done", ), ), ) // simulating some work go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/dynTotal/000077500000000000000000000000001466312757300161015ustar00rootroot00000000000000mpb-8.8.3/_examples/dynTotal/go.mod000066400000000000000000000005711466312757300172120ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/dynTotal go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/dynTotal/main.go000066400000000000000000000020571466312757300173600ustar00rootroot00000000000000package main import ( "io" "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { p := mpb.New(mpb.WithWidth(64)) // new bar with 'trigger complete event' disabled, because total is zero bar := p.AddBar(0, mpb.PrependDecorators(decor.Counters(decor.SizeB1024(0), "% .1f / % .1f")), mpb.AppendDecorators(decor.Percentage()), ) maxSleep := 100 * time.Millisecond read := makeStream(200) for { n, err := read() if err == io.EOF { // triggering complete event now bar.SetTotal(-1, true) break } // increment methods won't trigger complete event because bar was constructed with total = 0 bar.IncrBy(n) // following call is not required, it's called to show some progress instead of an empty bar bar.SetTotal(bar.Current()+2048, false) time.Sleep(time.Duration(rand.Intn(10)+1) * maxSleep / 10) } p.Wait() } func makeStream(limit int) func() (int, error) { return func() (int, error) { if limit <= 0 { return 0, io.EOF } limit-- return rand.Intn(1024) + 1, nil } } mpb-8.8.3/_examples/gomodtidyall000077500000000000000000000002141466312757300167160ustar00rootroot00000000000000#!/bin/sh set -e for d in *; do [ ! -d "$d" ] && continue pushd "$d" >/dev/null 2>&1 go mod tidy popd >/dev/null 2>&1 done mpb-8.8.3/_examples/io/000077500000000000000000000000001466312757300147125ustar00rootroot00000000000000mpb-8.8.3/_examples/io/go.mod000066400000000000000000000005631466312757300160240ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/io go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/io/main.go000066400000000000000000000016141466312757300161670ustar00rootroot00000000000000package main import ( "crypto/rand" "io" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var total int64 = 64 * 1024 * 1024 r, w := io.Pipe() go func() { for i := 0; i < 1024; i++ { _, _ = io.Copy(w, io.LimitReader(rand.Reader, 64*1024)) time.Sleep(time.Second / 10) } w.Close() }() p := mpb.New( mpb.WithWidth(60), mpb.WithRefreshRate(180*time.Millisecond), ) bar := p.New(total, mpb.BarStyle().Rbound("|"), mpb.PrependDecorators( decor.Counters(decor.SizeB1024(0), "% .2f / % .2f"), ), mpb.AppendDecorators( decor.EwmaETA(decor.ET_STYLE_GO, 30), decor.Name(" ] "), decor.EwmaSpeed(decor.SizeB1024(0), "% .2f", 30), ), ) // create proxy reader proxyReader := bar.ProxyReader(r) defer proxyReader.Close() // copy from proxyReader, ignoring errors _, _ = io.Copy(io.Discard, proxyReader) p.Wait() } mpb-8.8.3/_examples/mexicanBar/000077500000000000000000000000001466312757300163545ustar00rootroot00000000000000mpb-8.8.3/_examples/mexicanBar/go.mod000066400000000000000000000005731466312757300174670ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/mexicanBar go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/mexicanBar/main.go000066400000000000000000000022031466312757300176240ustar00rootroot00000000000000package main import ( "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { // initialize progress container, with custom width p := mpb.New(mpb.WithWidth(80)) total := 100 name := "Complex Filler:" bs := mpb.BarStyle() bs = bs.LboundMeta(func(s string) string { return "\033[34m" + s + "\033[0m" // blue }) bs = bs.Filler("_").FillerMeta(func(s string) string { return "\033[36m" + s + "\033[0m" // cyan }) bs = bs.Tip("⛵").TipMeta(func(s string) string { return "\033[31m" + s + "\033[0m" // red }) bs = bs.TipOnComplete() // leave tip on complete bs = bs.Padding("_").PaddingMeta(func(s string) string { return "\033[36m" + s + "\033[0m" // cyan }) bs = bs.RboundMeta(func(s string) string { return "\033[34m" + s + "\033[0m" // blue }) bar := p.New(int64(total), bs, mpb.PrependDecorators(decor.Name(name)), mpb.AppendDecorators(decor.Percentage()), ) // simulating some work max := 100 * time.Millisecond for i := 0; i < total; i++ { time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } // wait for our bar to complete p.Wait() } mpb-8.8.3/_examples/multiBars/000077500000000000000000000000001466312757300162455ustar00rootroot00000000000000mpb-8.8.3/_examples/multiBars/go.mod000066400000000000000000000005721466312757300173570ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/multiBars go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/multiBars/main.go000066400000000000000000000026011466312757300175170ustar00rootroot00000000000000package main import ( "fmt" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New(mpb.WithWaitGroup(&wg)) total, numBars := 100, 3 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(int64(total), mpb.PrependDecorators( // simple name decorator decor.Name(name), // decor.DSyncWidth bit enables column width synchronization decor.Percentage(decor.WCSyncSpace), ), mpb.AppendDecorators( // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( // ETA decorator with ewma age of 30 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done", ), ), ) // simulating some work go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/poplog/000077500000000000000000000000001466312757300156035ustar00rootroot00000000000000mpb-8.8.3/_examples/poplog/go.mod000066400000000000000000000005671466312757300167210ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/poplog go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/poplog/main.go000066400000000000000000000021621466312757300170570ustar00rootroot00000000000000package main import ( "fmt" "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { rng := rand.New(rand.NewSource(time.Now().UnixNano())) p := mpb.New(mpb.PopCompletedMode()) total, numBars := 100, 4 for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(int64(total), mpb.BarFillerOnComplete(fmt.Sprintf("%s has been completed", name)), mpb.BarFillerTrim(), mpb.PrependDecorators( decor.OnComplete(decor.Name(name), ""), decor.OnComplete(decor.NewPercentage(" % d "), ""), ), mpb.AppendDecorators( decor.OnComplete(decor.Name(" "), ""), decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_GO, 30), ""), ), ) // simulating some work max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } } p.Wait() } mpb-8.8.3/_examples/progressAsWriter/000077500000000000000000000000001466312757300176305ustar00rootroot00000000000000mpb-8.8.3/_examples/progressAsWriter/go.mod000066400000000000000000000006011466312757300207330ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/progressAsWriter go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/progressAsWriter/main.go000066400000000000000000000032061466312757300211040ustar00rootroot00000000000000package main import ( "fmt" "log" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { total, numBars := 100, 2 var bwg, qwg sync.WaitGroup bwg.Add(numBars) qwg.Add(1) done := make(chan interface{}) p := mpb.New(mpb.WithWidth(64), mpb.WithShutdownNotifier(done), mpb.WithWaitGroup(&qwg)) log.SetOutput(p) go func() { defer qwg.Done() for { select { case <-done: // after done, underlying io.Writer returns mpb.DoneError // so following isn't printed log.Println("all done") return default: log.Println("waiting for done") time.Sleep(150 * time.Millisecond) } } }() nopBar := p.MustAdd(0, nil) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(int64(total), mpb.PrependDecorators( decor.Name(name), decor.Percentage(decor.WCSyncSpace), ), mpb.AppendDecorators( decor.OnComplete( decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done", ), ), ) // simulating some work go func() { defer bwg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } log.Println(name, "done") }() } bwg.Wait() log.Println("completing nop bar") nopBar.EnableTriggerComplete() p.Wait() } mpb-8.8.3/_examples/quietMode/000077500000000000000000000000001466312757300162375ustar00rootroot00000000000000mpb-8.8.3/_examples/quietMode/go.mod000066400000000000000000000005721466312757300173510ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/quietMode go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/quietMode/main.go000066400000000000000000000030761466312757300175200ustar00rootroot00000000000000package main import ( "flag" "fmt" "io" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) var quietMode bool func init() { flag.BoolVar(&quietMode, "q", false, "quiet mode") } func main() { flag.Parse() var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New( mpb.WithWaitGroup(&wg), mpb.ContainerOptional(mpb.WithOutput(io.Discard), quietMode), ) total, numBars := 100, 3 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(int64(total), mpb.PrependDecorators( // simple name decorator decor.Name(name), // decor.DSyncWidth bit enables column width synchronization decor.Percentage(decor.WCSyncSpace), ), mpb.AppendDecorators( // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( // ETA decorator with ewma age of 30 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done", ), ), ) // simulating some work go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() fmt.Println("done") } mpb-8.8.3/_examples/remove/000077500000000000000000000000001466312757300156005ustar00rootroot00000000000000mpb-8.8.3/_examples/remove/go.mod000066400000000000000000000005671466312757300167160ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/remove go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/remove/main.go000066400000000000000000000025211466312757300170530ustar00rootroot00000000000000package main import ( "fmt" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New(mpb.WithWaitGroup(&wg)) total := 100 numBars := 3 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(int64(total), mpb.BarID(i), mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0), mpb.PrependDecorators( decor.Name(name), ), mpb.AppendDecorators( decor.Any(func(s decor.Statistics) string { return fmt.Sprintf("completed: %v", s.Completed) }, decor.WCSyncSpaceR), decor.Any(func(s decor.Statistics) string { return fmt.Sprintf("aborted: %v", s.Aborted) }, decor.WCSyncSpaceR), decor.OnComplete(decor.NewPercentage("%d", decor.WCSyncSpace), "done"), decor.OnAbort(decor.NewPercentage("%d", decor.WCSyncSpace), "ohno"), ), ) go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; bar.IsRunning(); i++ { if bar.ID() == 2 && i >= 42 { go bar.Abort(false) } time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) bar.Increment() } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/reverseBar/000077500000000000000000000000001466312757300164035ustar00rootroot00000000000000mpb-8.8.3/_examples/reverseBar/go.mod000066400000000000000000000005731466312757300175160ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/reverseBar go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/reverseBar/main.go000066400000000000000000000030651466312757300176620ustar00rootroot00000000000000package main import ( "fmt" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New(mpb.WithWaitGroup(&wg)) total, numBars := 100, 3 wg.Add(numBars) condFillerBuilder := func(cond bool) mpb.BarFillerBuilder { if cond { // reverse Bar on cond return mpb.BarStyle().Tip("<").Reverse() } return mpb.BarStyle() } for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.New(int64(total), condFillerBuilder(i == 1), mpb.PrependDecorators( // simple name decorator decor.Name(name), // decor.DSyncWidth bit enables column width synchronization decor.Percentage(decor.WCSyncSpace), ), mpb.AppendDecorators( // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( // ETA decorator with ewma age of 30 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done", ), ), ) // simulating some work go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/singleBar/000077500000000000000000000000001466312757300162115ustar00rootroot00000000000000mpb-8.8.3/_examples/singleBar/go.mod000066400000000000000000000005721466312757300173230ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/singleBar go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/singleBar/main.go000066400000000000000000000020001466312757300174540ustar00rootroot00000000000000package main import ( "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { // initialize progress container, with custom width p := mpb.New(mpb.WithWidth(64)) total := 100 name := "Single Bar:" // create a single bar, which will inherit container's width bar := p.New(int64(total), // BarFillerBuilder with custom style mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"), mpb.PrependDecorators( // display our name with one space on the right decor.Name(name, decor.WC{C: decor.DindentRight | decor.DextraSpace}), // replace ETA decorator with "done" message, OnComplete event decor.OnComplete(decor.AverageETA(decor.ET_STYLE_GO), "done"), ), mpb.AppendDecorators(decor.Percentage()), ) // simulating some work max := 100 * time.Millisecond for i := 0; i < total; i++ { time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } // wait for our bar to complete and flush p.Wait() } mpb-8.8.3/_examples/spinTipBar/000077500000000000000000000000001466312757300163565ustar00rootroot00000000000000mpb-8.8.3/_examples/spinTipBar/go.mod000066400000000000000000000005731466312757300174710ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/spinTipBar go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/spinTipBar/main.go000066400000000000000000000013131466312757300176270ustar00rootroot00000000000000package main import ( "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { // initialize progress container, with custom width p := mpb.New(mpb.WithWidth(80)) total := 100 name := "Single Bar:" bar := p.New(int64(total), mpb.BarStyle().Tip(`-`, `\`, `|`, `/`).TipMeta(func(s string) string { return "\033[31m" + s + "\033[0m" // red }), mpb.PrependDecorators(decor.Name(name)), mpb.AppendDecorators(decor.Percentage()), ) // simulating some work max := 100 * time.Millisecond for i := 0; i < total; i++ { time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } // wait for our bar to complete and flush p.Wait() } mpb-8.8.3/_examples/spinnerBar/000077500000000000000000000000001466312757300164065ustar00rootroot00000000000000mpb-8.8.3/_examples/spinnerBar/go.mod000066400000000000000000000005731466312757300175210ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/spinnerBar go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/spinnerBar/main.go000066400000000000000000000032351466312757300176640ustar00rootroot00000000000000package main import ( "fmt" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New( mpb.WithWaitGroup(&wg), mpb.WithWidth(16), ) total, numBars := 101, 3 wg.Add(numBars) condFillerBuilder := func(cond bool) mpb.BarFillerBuilder { if cond { s := mpb.SpinnerStyle("∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙") return s.Meta(func(s string) string { return "\033[31m" + s + "\033[0m" // red }) } return mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟") } for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.New(int64(total), condFillerBuilder(i != 0), mpb.PrependDecorators( // simple name decorator decor.Name(name), ), mpb.AppendDecorators( // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( // ETA decorator with ewma age of 30 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done", ), ), ) // simulating some work go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { // start variable is solely for EWMA calculation // EWMA's unit of measure is an iteration's duration start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) // we need to call EwmaIncrement to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/spinnerDecorator/000077500000000000000000000000001466312757300176245ustar00rootroot00000000000000mpb-8.8.3/_examples/spinnerDecorator/go.mod000066400000000000000000000006011466312757300207270ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/spinnerDecorator go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/spinnerDecorator/main.go000066400000000000000000000021351466312757300211000ustar00rootroot00000000000000package main import ( "fmt" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(64)) total, numBars := 100, 3 wg.Add(numBars) for i := 0; i < numBars; i++ { name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(int64(total), mpb.PrependDecorators( // simple name decorator decor.Name(name), decor.OnComplete( // spinner decorator with default style decor.Spinner(nil, decor.WCSyncSpace), "done", ), ), mpb.AppendDecorators( // decor.DSyncWidth bit enables column width synchronization decor.Percentage(decor.WCSyncWidth), ), ) // simulating some work go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for i := 0; i < total; i++ { time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) bar.Increment() } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/stress/000077500000000000000000000000001466312757300156265ustar00rootroot00000000000000mpb-8.8.3/_examples/stress/go.mod000066400000000000000000000010211466312757300167260ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/stress go 1.17 require ( github.com/pkg/profile v1.7.0 github.com/vbauerster/mpb/v8 v8.8.3 ) require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/felixge/fgprof v0.9.4 // indirect github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/stress/main.go000066400000000000000000000027671466312757300171150ustar00rootroot00000000000000package main import ( "flag" "fmt" "math/rand" "os" "sync" "time" "github.com/pkg/profile" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) const ( totalBars = 42 ) var proftype = flag.String("prof", "", "profile type (cpu, mem)") func main() { flag.Parse() switch *proftype { case "cpu": defer profile.Start(profile.CPUProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop() case "mem": defer profile.Start(profile.MemProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop() } var wg sync.WaitGroup // passed wg will be accounted at p.Wait() call p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithDebugOutput(os.Stderr)) wg.Add(totalBars) for i := 0; i < totalBars; i++ { name := fmt.Sprintf("Bar#%02d: ", i) total := rand.Intn(320) + 10 bar := p.AddBar(int64(total), mpb.PrependDecorators( decor.Name(name, decor.WCSyncWidthR), decor.OnComplete(decor.Percentage(decor.WCSyncWidth), "done"), ), mpb.AppendDecorators( decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), ""), decor.EwmaSpeed(decor.SizeB1024(0), "", 30, decor.WCSyncSpace), ), ) go func() { defer wg.Done() rng := rand.New(rand.NewSource(time.Now().UnixNano())) max := 100 * time.Millisecond for bar.IsRunning() { start := time.Now() time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) bar.EwmaIncrement(time.Since(start)) } }() } // wait for passed wg and for all bars to complete and flush p.Wait() } mpb-8.8.3/_examples/suppressBar/000077500000000000000000000000001466312757300166145ustar00rootroot00000000000000mpb-8.8.3/_examples/suppressBar/go.mod000066400000000000000000000005741466312757300177300ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/suppressBar go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/suppressBar/main.go000066400000000000000000000040411466312757300200660ustar00rootroot00000000000000package main import ( "errors" "fmt" "math" "math/rand" "sync" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { p := mpb.New() total, numBars := 100, 3 err := new(errorWrapper) timer := time.AfterFunc(2*time.Second, func() { err.set(errors.New("timeout"), rand.Intn(numBars)) }) defer timer.Stop() for i := 0; i < numBars; i++ { msgCh := make(chan string, 1) bar := p.AddBar(int64(total), mpb.PrependDecorators(newTitleDecorator(fmt.Sprintf("Bar#%d:", i), msgCh, 16)), mpb.AppendDecorators(decor.Percentage(decor.WCSyncWidth)), ) // simulating some work barID := i go func() { max := 100 * time.Millisecond for i := 0; i < total; i++ { if err.check(barID) { msgCh <- fmt.Sprintf("%s at %d, retrying...", err.Error(), i) err.reset() i-- bar.SetRefill(int64(i)) continue } time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } }() } p.Wait() } type errorWrapper struct { sync.RWMutex err error barID int } func (ew *errorWrapper) Error() string { ew.RLock() defer ew.RUnlock() return ew.err.Error() } func (ew *errorWrapper) check(barID int) bool { ew.RLock() defer ew.RUnlock() return ew.err != nil && ew.barID == barID } func (ew *errorWrapper) set(err error, barID int) { ew.Lock() ew.err = err ew.barID = barID ew.Unlock() } func (ew *errorWrapper) reset() { ew.Lock() ew.err = nil ew.Unlock() } type title struct { decor.Decorator name string msgCh <-chan string msg string count int limit int } func (d *title) Decor(stat decor.Statistics) (string, int) { if d.count == 0 { select { case msg := <-d.msgCh: d.count = d.limit d.msg = msg default: return d.Decorator.Decor(stat) } } d.count-- _, _ = d.Format("") return fmt.Sprintf("%s %s", d.name, d.msg), math.MaxInt } func newTitleDecorator(name string, msgCh <-chan string, limit int) decor.Decorator { return &title{ Decorator: decor.Name(name), name: name, msgCh: msgCh, limit: limit, } } mpb-8.8.3/_examples/tipOnComplete/000077500000000000000000000000001466312757300170655ustar00rootroot00000000000000mpb-8.8.3/_examples/tipOnComplete/go.mod000066400000000000000000000005761466312757300202030ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/tipOnComplete go 1.17 require github.com/vbauerster/mpb/v8 v8.8.3 require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sys v0.24.0 // indirect ) mpb-8.8.3/_examples/tipOnComplete/main.go000066400000000000000000000011621466312757300203400ustar00rootroot00000000000000package main import ( "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { // initialize progress container, with custom width p := mpb.New(mpb.WithWidth(80)) total := 100 name := "Single Bar:" bar := p.New(int64(total), mpb.BarStyle().TipOnComplete(), mpb.PrependDecorators(decor.Name(name)), mpb.AppendDecorators(decor.Percentage()), ) // simulating some work max := 100 * time.Millisecond for i := 0; i < total; i++ { time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } // wait for our bar to complete and flush p.Wait() } mpb-8.8.3/_svg/000077500000000000000000000000001466312757300132645ustar00rootroot00000000000000mpb-8.8.3/_svg/godEMrCZmJkHYH1X9dN4Nm0U7.svg000066400000000000000000002025171466312757300201000ustar00rootroot00000000000000~/go/src/github.com/vbauerster/mpb/examples/dynTotal~/go/src/github.com/vbauerster/mpb/examples/dynTotalmaster*gorun-racemain.gogorun-racemain.gogorun-racemain.go55.7KiB/56.7KiB[============================================================>-]98%100.7KiB/100.7KiB[==============================================================]100%~/go/src/github.com/vbauerster/mpb/examples/dynTotalmaster*13s~/go/src/github.com/vbauerster/mpb/examples/dynTotalmaster~/go/src/github.com/vbauerster/mpb/examples/dynTotalmastergorun-racemain.gogorun-racemain.gogorun-racemain.gogorun-racemain.go519b/1.5KiB[====================>-----------------------------------------]34%2.2KiB/3.2KiB[==========================================>-------------------]69%2.5KiB/3.5KiB[===========================================>------------------]72%3.2KiB/4.2KiB[==============================================>---------------]76%3.6KiB/4.6KiB[================================================>-------------]78%6.5KiB/7.5KiB[=====================================================>--------]87%8.0KiB/9.0KiB[======================================================>-------]89%10.6KiB/11.6KiB[========================================================>-----]91%12.1KiB/13.1KiB[========================================================>-----]92%13.1KiB/14.1KiB[=========================================================>----]93%13.2KiB/14.2KiB[=========================================================>----]93%13.9KiB/14.9KiB[=========================================================>----]93%15.5KiB/16.5KiB[=========================================================>----]94%15.8KiB/16.8KiB[=========================================================>----]94%17.6KiB/18.6KiB[==========================================================>---]95%18.9KiB/19.9KiB[==========================================================>---]95%20.0KiB/21.0KiB[==========================================================>---]95%21.0KiB/22.0KiB[==========================================================>---]95%21.3KiB/22.3KiB[==========================================================>---]96%22.6KiB/23.6KiB[==========================================================>---]96%23.9KiB/24.9KiB[===========================================================>--]96%25.7KiB/26.7KiB[===========================================================>--]96%26.1KiB/27.1KiB[===========================================================>--]96%26.8KiB/27.8KiB[===========================================================>--]96%28.0KiB/29.0KiB[===========================================================>--]97%28.4KiB/29.4KiB[===========================================================>--]97%29.6KiB/30.6KiB[===========================================================>--]97%30.0KiB/31.0KiB[===========================================================>--]97%30.4KiB/31.4KiB[===========================================================>--]97%31.8KiB/32.8KiB[===========================================================>--]97%34.4KiB/35.4KiB[===========================================================>--]97%34.5KiB/35.5KiB[===========================================================>--]97%37.0KiB/38.0KiB[===========================================================>--]97%38.5KiB/39.5KiB[===========================================================>--]97%40.4KiB/41.4KiB[============================================================>-]98%41.1KiB/42.1KiB[============================================================>-]98%42.2KiB/43.2KiB[============================================================>-]98%43.9KiB/44.9KiB[============================================================>-]98%44.9KiB/45.9KiB[============================================================>-]98%46.2KiB/47.2KiB[============================================================>-]98%46.9KiB/47.9KiB[============================================================>-]98%48.4KiB/49.4KiB[============================================================>-]98%48.7KiB/49.7KiB[============================================================>-]98%49.3KiB/50.3KiB[============================================================>-]98%50.1KiB/51.1KiB[============================================================>-]98%50.5KiB/51.5KiB[============================================================>-]98%50.6KiB/51.6KiB[============================================================>-]98%50.8KiB/51.8KiB[============================================================>-]98%51.7KiB/52.7KiB[============================================================>-]98%52.7KiB/53.7KiB[============================================================>-]98%53.6KiB/54.6KiB[============================================================>-]98%57.5KiB/58.5KiB[============================================================>-]98%58.3KiB/59.3KiB[============================================================>-]98%58.7KiB/59.7KiB[============================================================>-]98%60.1KiB/61.1KiB[============================================================>-]98%62.0KiB/63.0KiB[============================================================>-]98%63.7KiB/64.7KiB[============================================================>-]98%64.7KiB/65.7KiB[============================================================>-]98%65.2KiB/66.2KiB[============================================================>-]98%65.8KiB/66.8KiB[============================================================>-]99%66.4KiB/67.4KiB[============================================================>-]99%67.6KiB/68.6KiB[============================================================>-]99%68.5KiB/69.5KiB[============================================================>-]99%70.0KiB/71.0KiB[============================================================>-]99%70.4KiB/71.4KiB[============================================================>-]99%70.8KiB/71.8KiB[============================================================>-]99%72.3KiB/73.3KiB[============================================================>-]99%73.1KiB/74.1KiB[============================================================>-]99%74.4KiB/75.4KiB[============================================================>-]99%75.7KiB/76.7KiB[============================================================>-]99%78.2KiB/79.2KiB[============================================================>-]99%79.3KiB/80.3KiB[============================================================>-]99%80.1KiB/81.1KiB[============================================================>-]99%81.3KiB/82.3KiB[============================================================>-]99%82.3KiB/83.3KiB[============================================================>-]99%82.6KiB/83.6KiB[============================================================>-]99%84.0KiB/85.0KiB[============================================================>-]99%84.7KiB/85.7KiB[============================================================>-]99%85.9KiB/86.9KiB[============================================================>-]99%87.7KiB/88.7KiB[============================================================>-]99%88.8KiB/89.8KiB[============================================================>-]99%90.3KiB/91.3KiB[============================================================>-]99%91.6KiB/92.6KiB[============================================================>-]99%92.8KiB/93.8KiB[============================================================>-]99%93.5KiB/94.5KiB[============================================================>-]99%93.6KiB/94.6KiB[============================================================>-]99%95.1KiB/96.1KiB[============================================================>-]99%96.4KiB/97.4KiB[============================================================>-]99%97.6KiB/98.6KiB[============================================================>-]99%98.8KiB/99.8KiB[============================================================>-]99%99.7KiB/100.7KiB[============================================================>-]99%100.6KiB/101.6KiB[============================================================>-]99%100.7KiB/101.7KiB[============================================================>-]99%mpb-8.8.3/_svg/hIpTa3A5rQz65ssiVuRJu87X6.svg000066400000000000000000005242741466312757300202210ustar00rootroot00000000000000~/go/src/github.com/vbauerster/mpb/examples/io/single~/go/src/github.com/vbauerster/mpb/examples/io/singlemaster*gorun-racemain.gogorun-racemain.gogorun-racemain.go40.6MiB/40.6MiB[==========================================================|00:00]7.54MiB/s~/go/src/github.com/vbauerster/mpb/examples/io/singlemaster*46s~/go/src/github.com/vbauerster/mpb/examples/io/singlemastergitclean-fdxgorun-racemain.gogorun-racemain.gogorun-racemain.go16.6KiB/40.6MiB[----------------------------------------------------------|00:00]0b/s66.5KiB/40.6MiB[----------------------------------------------------------|00:00]0b/s134.5KiB/40.6MiB[----------------------------------------------------------|09:01]6.18MiB/s339.6KiB/40.6MiB[----------------------------------------------------------|08:18]6.41MiB/s577.6KiB/40.6MiB[>---------------------------------------------------------|07:32]7.05MiB/s866.6KiB/40.6MiB[>---------------------------------------------------------|06:43]7.36MiB/s1.5MiB/40.6MiB[=>--------------------------------------------------------|05:12]8.12MiB/s1.8MiB/40.6MiB[==>-------------------------------------------------------|04:28]8.48MiB/s2.4MiB/40.6MiB[==>-------------------------------------------------------|03:29]16.62MiB/s2.9MiB/40.6MiB[===>------------------------------------------------------|02:52]15.37MiB/s3.5MiB/40.6MiB[====>-----------------------------------------------------|02:17]14.22MiB/s3.9MiB/40.6MiB[=====>----------------------------------------------------|01:59]13.18MiB/s4.3MiB/40.6MiB[=====>----------------------------------------------------|01:40]12.42MiB/s4.9MiB/40.6MiB[======>---------------------------------------------------|01:19]18.55MiB/s5.2MiB/40.6MiB[======>---------------------------------------------------|01:11]17.39MiB/s5.5MiB/40.6MiB[=======>--------------------------------------------------|01:03]16.23MiB/s5.6MiB/40.6MiB[=======>--------------------------------------------------|01:02]16.04MiB/s6.2MiB/40.6MiB[========>-------------------------------------------------|00:50]17.91MiB/s6.5MiB/40.6MiB[========>-------------------------------------------------|00:47]16.80MiB/s6.7MiB/40.6MiB[=========>------------------------------------------------|00:45]15.85MiB/s7.0MiB/40.6MiB[=========>------------------------------------------------|00:41]14.66MiB/s7.3MiB/40.6MiB[=========>------------------------------------------------|00:38]17.03MiB/s7.5MiB/40.6MiB[==========>-----------------------------------------------|00:39]16.26MiB/s7.7MiB/40.6MiB[==========>-----------------------------------------------|00:38]15.16MiB/s8.0MiB/40.6MiB[==========>-----------------------------------------------|00:36]14.16MiB/s8.1MiB/40.6MiB[===========>----------------------------------------------|00:35]13.56MiB/s8.4MiB/40.6MiB[===========>----------------------------------------------|00:36]13.08MiB/s8.6MiB/40.6MiB[===========>----------------------------------------------|00:34]12.51MiB/s8.8MiB/40.6MiB[============>---------------------------------------------|00:34]12.05MiB/s9.0MiB/40.6MiB[============>---------------------------------------------|00:35]11.56MiB/s9.3MiB/40.6MiB[============>---------------------------------------------|00:32]11.12MiB/s9.5MiB/40.6MiB[=============>--------------------------------------------|00:31]10.75MiB/s9.7MiB/40.6MiB[=============>--------------------------------------------|00:30]10.23MiB/s10.0MiB/40.6MiB[=============>--------------------------------------------|00:29]9.89MiB/s10.2MiB/40.6MiB[==============>-------------------------------------------|00:30]9.54MiB/s10.4MiB/40.6MiB[==============>-------------------------------------------|00:28]9.34MiB/s10.7MiB/40.6MiB[==============>-------------------------------------------|00:27]9.04MiB/s10.9MiB/40.6MiB[===============>------------------------------------------|00:26]8.79MiB/s11.0MiB/40.6MiB[===============>------------------------------------------|00:26]8.75MiB/s11.3MiB/40.6MiB[===============>------------------------------------------|00:25]11.13MiB/s11.5MiB/40.6MiB[===============>------------------------------------------|00:24]10.81MiB/s11.7MiB/40.6MiB[================>-----------------------------------------|00:24]10.45MiB/s11.9MiB/40.6MiB[================>-----------------------------------------|00:25]10.00MiB/s12.1MiB/40.6MiB[================>-----------------------------------------|00:25]9.63MiB/s12.3MiB/40.6MiB[=================>----------------------------------------|00:24]9.26MiB/s12.4MiB/40.6MiB[=================>----------------------------------------|00:24]9.14MiB/s12.6MiB/40.6MiB[=================>----------------------------------------|00:23]8.79MiB/s12.8MiB/40.6MiB[=================>----------------------------------------|00:23]10.07MiB/s12.9MiB/40.6MiB[=================>----------------------------------------|00:23]9.96MiB/s13.1MiB/40.6MiB[==================>---------------------------------------|00:22]9.80MiB/s13.2MiB/40.6MiB[==================>---------------------------------------|00:22]9.71MiB/s13.4MiB/40.6MiB[==================>---------------------------------------|00:22]9.61MiB/s13.6MiB/40.6MiB[==================>---------------------------------------|00:25]9.36MiB/s13.7MiB/40.6MiB[===================>--------------------------------------|00:24]9.19MiB/s13.9MiB/40.6MiB[===================>--------------------------------------|00:24]8.93MiB/s14.0MiB/40.6MiB[===================>--------------------------------------|00:24]8.65MiB/s14.2MiB/40.6MiB[===================>--------------------------------------|00:24]8.48MiB/s14.4MiB/40.6MiB[====================>-------------------------------------|00:23]8.32MiB/s14.5MiB/40.6MiB[====================>-------------------------------------|00:23]8.20MiB/s14.7MiB/40.6MiB[====================>-------------------------------------|00:22]8.12MiB/s14.8MiB/40.6MiB[====================>-------------------------------------|00:22]7.97MiB/s15.0MiB/40.6MiB[====================>-------------------------------------|00:22]7.88MiB/s15.2MiB/40.6MiB[=====================>------------------------------------|00:21]7.74MiB/s15.3MiB/40.6MiB[=====================>------------------------------------|00:21]7.73MiB/s15.4MiB/40.6MiB[=====================>------------------------------------|00:20]9.07MiB/s15.6MiB/40.6MiB[=====================>------------------------------------|00:20]8.84MiB/s15.7MiB/40.6MiB[=====================>------------------------------------|00:20]8.72MiB/s15.9MiB/40.6MiB[======================>-----------------------------------|00:20]8.45MiB/s16.0MiB/40.6MiB[======================>-----------------------------------|00:20]8.27MiB/s16.1MiB/40.6MiB[======================>-----------------------------------|00:22]8.05MiB/s16.3MiB/40.6MiB[======================>-----------------------------------|00:21]7.88MiB/s16.4MiB/40.6MiB[======================>-----------------------------------|00:21]7.69MiB/s16.6MiB/40.6MiB[=======================>----------------------------------|00:21]7.60MiB/s16.7MiB/40.6MiB[=======================>----------------------------------|00:22]7.49MiB/s16.9MiB/40.6MiB[=======================>----------------------------------|00:22]7.41MiB/s17.0MiB/40.6MiB[=======================>----------------------------------|00:21]7.43MiB/s17.2MiB/40.6MiB[========================>---------------------------------|00:21]7.38MiB/s17.4MiB/40.6MiB[========================>---------------------------------|00:23]7.23MiB/s17.5MiB/40.6MiB[========================>---------------------------------|00:23]7.15MiB/s17.7MiB/40.6MiB[========================>---------------------------------|00:24]6.99MiB/s17.9MiB/40.6MiB[=========================>--------------------------------|00:25]6.90MiB/s18.0MiB/40.6MiB[=========================>--------------------------------|00:24]6.83MiB/s18.2MiB/40.6MiB[=========================>--------------------------------|00:23]8.15MiB/s18.3MiB/40.6MiB[=========================>--------------------------------|00:24]7.99MiB/s18.4MiB/40.6MiB[=========================>--------------------------------|00:24]7.88MiB/s18.6MiB/40.6MiB[==========================>-------------------------------|00:24]7.62MiB/s18.7MiB/40.6MiB[==========================>-------------------------------|00:23]7.58MiB/s18.8MiB/40.6MiB[==========================>-------------------------------|00:23]7.47MiB/s19.0MiB/40.6MiB[==========================>-------------------------------|00:22]7.39MiB/s19.1MiB/40.6MiB[==========================>-------------------------------|00:22]7.33MiB/s19.2MiB/40.6MiB[===========================>------------------------------|00:22]8.48MiB/s19.4MiB/40.6MiB[===========================>------------------------------|00:25]8.27MiB/s19.5MiB/40.6MiB[===========================>------------------------------|00:25]8.13MiB/s19.6MiB/40.6MiB[===========================>------------------------------|00:25]7.89MiB/s19.7MiB/40.6MiB[===========================>------------------------------|00:25]7.67MiB/s19.8MiB/40.6MiB[===========================>------------------------------|00:24]7.52MiB/s20.0MiB/40.6MiB[============================>-----------------------------|00:24]7.42MiB/s20.1MiB/40.6MiB[============================>-----------------------------|00:24]7.25MiB/s20.2MiB/40.6MiB[============================>-----------------------------|00:23]7.12MiB/s20.3MiB/40.6MiB[============================>-----------------------------|00:23]6.94MiB/s20.5MiB/40.6MiB[============================>-----------------------------|00:24]6.75MiB/s20.6MiB/40.6MiB[============================>-----------------------------|00:24]6.59MiB/s20.7MiB/40.6MiB[=============================>----------------------------|00:27]6.57MiB/s20.8MiB/40.6MiB[=============================>----------------------------|00:27]6.45MiB/s21.0MiB/40.6MiB[=============================>----------------------------|00:28]6.40MiB/s21.1MiB/40.6MiB[=============================>----------------------------|00:30]6.36MiB/s21.2MiB/40.6MiB[=============================>----------------------------|00:29]6.28MiB/s21.3MiB/40.6MiB[=============================>----------------------------|00:28]6.22MiB/s21.4MiB/40.6MiB[==============================>---------------------------|00:27]6.19MiB/s21.5MiB/40.6MiB[==============================>---------------------------|00:29]6.10MiB/s21.7MiB/40.6MiB[==============================>---------------------------|00:28]6.09MiB/s21.8MiB/40.6MiB[==============================>---------------------------|00:27]6.04MiB/s21.9MiB/40.6MiB[==============================>---------------------------|00:27]5.88MiB/s22.0MiB/40.6MiB[===============================>--------------------------|00:27]5.83MiB/s22.2MiB/40.6MiB[===============================>--------------------------|00:26]5.78MiB/s22.3MiB/40.6MiB[===============================>--------------------------|00:25]5.80MiB/s22.4MiB/40.6MiB[===============================>--------------------------|00:24]5.76MiB/s22.5MiB/40.6MiB[===============================>--------------------------|00:24]5.72MiB/s22.7MiB/40.6MiB[===============================>--------------------------|00:23]5.70MiB/s22.8MiB/40.6MiB[================================>-------------------------|00:24]5.60MiB/s22.8MiB/40.6MiB[================================>-------------------------|00:24]5.58MiB/s23.0MiB/40.6MiB[================================>-------------------------|00:25]6.35MiB/s23.1MiB/40.6MiB[================================>-------------------------|00:25]6.33MiB/s23.2MiB/40.6MiB[================================>-------------------------|00:24]6.33MiB/s23.4MiB/40.6MiB[================================>-------------------------|00:24]6.31MiB/s23.5MiB/40.6MiB[=================================>------------------------|00:23]6.28MiB/s23.6MiB/40.6MiB[=================================>------------------------|00:23]6.20MiB/s23.7MiB/40.6MiB[=================================>------------------------|00:22]6.26MiB/s23.8MiB/40.6MiB[=================================>------------------------|00:22]6.26MiB/s23.9MiB/40.6MiB[=================================>------------------------|00:21]6.31MiB/s24.0MiB/40.6MiB[=================================>------------------------|00:21]6.26MiB/s24.2MiB/40.6MiB[==================================>-----------------------|00:22]6.25MiB/s24.3MiB/40.6MiB[==================================>-----------------------|00:25]6.20MiB/s24.4MiB/40.6MiB[==================================>-----------------------|00:24]6.20MiB/s24.5MiB/40.6MiB[==================================>-----------------------|00:23]6.14MiB/s24.7MiB/40.6MiB[==================================>-----------------------|00:24]6.16MiB/s24.8MiB/40.6MiB[==================================>-----------------------|00:24]6.13MiB/s24.9MiB/40.6MiB[===================================>----------------------|00:23]6.14MiB/s25.0MiB/40.6MiB[===================================>----------------------|00:23]6.17MiB/s25.1MiB/40.6MiB[===================================>----------------------|00:23]6.16MiB/s25.3MiB/40.6MiB[===================================>----------------------|00:22]6.05MiB/s25.4MiB/40.6MiB[===================================>----------------------|00:22]6.14MiB/s25.5MiB/40.6MiB[===================================>----------------------|00:21]6.06MiB/s25.6MiB/40.6MiB[====================================>---------------------|00:22]6.04MiB/s25.8MiB/40.6MiB[====================================>---------------------|00:21]6.02MiB/s25.9MiB/40.6MiB[====================================>---------------------|00:22]5.96MiB/s26.0MiB/40.6MiB[====================================>---------------------|00:24]6.05MiB/s26.1MiB/40.6MiB[====================================>---------------------|00:23]6.03MiB/s26.2MiB/40.6MiB[=====================================>--------------------|00:22]7.18MiB/s26.3MiB/40.6MiB[=====================================>--------------------|00:22]7.10MiB/s26.4MiB/40.6MiB[=====================================>--------------------|00:21]7.09MiB/s26.5MiB/40.6MiB[=====================================>--------------------|00:21]7.09MiB/s26.7MiB/40.6MiB[=====================================>--------------------|00:21]7.07MiB/s26.8MiB/40.6MiB[=====================================>--------------------|00:20]7.02MiB/s26.8MiB/40.6MiB[=====================================>--------------------|00:20]6.92MiB/s27.0MiB/40.6MiB[======================================>-------------------|00:19]6.89MiB/s27.1MiB/40.6MiB[======================================>-------------------|00:19]6.99MiB/s27.2MiB/40.6MiB[======================================>-------------------|00:18]6.98MiB/s27.3MiB/40.6MiB[======================================>-------------------|00:18]6.99MiB/s27.5MiB/40.6MiB[======================================>-------------------|00:20]6.98MiB/s27.5MiB/40.6MiB[======================================>-------------------|00:20]6.94MiB/s27.7MiB/40.6MiB[=======================================>------------------|00:20]7.98MiB/s27.8MiB/40.6MiB[=======================================>------------------|00:19]7.88MiB/s27.8MiB/40.6MiB[=======================================>------------------|00:19]7.81MiB/s28.0MiB/40.6MiB[=======================================>------------------|00:18]7.71MiB/s28.0MiB/40.6MiB[=======================================>------------------|00:18]7.58MiB/s28.1MiB/40.6MiB[=======================================>------------------|00:18]7.44MiB/s28.2MiB/40.6MiB[=======================================>------------------|00:18]7.38MiB/s28.3MiB/40.6MiB[========================================>-----------------|00:17]7.33MiB/s28.4MiB/40.6MiB[========================================>-----------------|00:17]7.19MiB/s28.5MiB/40.6MiB[========================================>-----------------|00:18]7.17MiB/s28.6MiB/40.6MiB[========================================>-----------------|00:17]7.17MiB/s28.7MiB/40.6MiB[========================================>-----------------|00:20]7.11MiB/s28.8MiB/40.6MiB[========================================>-----------------|00:19]7.03MiB/s28.9MiB/40.6MiB[========================================>-----------------|00:20]6.89MiB/s29.0MiB/40.6MiB[=========================================>----------------|00:20]6.79MiB/s29.1MiB/40.6MiB[=========================================>----------------|00:19]6.72MiB/s29.2MiB/40.6MiB[=========================================>----------------|00:21]6.60MiB/s29.4MiB/40.6MiB[=========================================>----------------|00:20]6.50MiB/s29.4MiB/40.6MiB[=========================================>----------------|00:20]6.48MiB/s29.5MiB/40.6MiB[=========================================>----------------|00:20]6.44MiB/s29.7MiB/40.6MiB[=========================================>----------------|00:19]6.37MiB/s29.7MiB/40.6MiB[==========================================>---------------|00:19]6.31MiB/s29.8MiB/40.6MiB[==========================================>---------------|00:21]6.31MiB/s29.9MiB/40.6MiB[==========================================>---------------|00:20]6.34MiB/s30.0MiB/40.6MiB[==========================================>---------------|00:20]6.32MiB/s30.2MiB/40.6MiB[==========================================>---------------|00:19]6.22MiB/s30.2MiB/40.6MiB[==========================================>---------------|00:19]6.18MiB/s30.3MiB/40.6MiB[==========================================>---------------|00:19]6.16MiB/s30.5MiB/40.6MiB[===========================================>--------------|00:19]6.21MiB/s30.6MiB/40.6MiB[===========================================>--------------|00:18]6.19MiB/s30.7MiB/40.6MiB[===========================================>--------------|00:17]6.18MiB/s30.8MiB/40.6MiB[===========================================>--------------|00:17]6.19MiB/s30.9MiB/40.6MiB[===========================================>--------------|00:17]5.99MiB/s31.0MiB/40.6MiB[===========================================>--------------|00:17]5.89MiB/s31.1MiB/40.6MiB[============================================>-------------|00:16]5.92MiB/s31.3MiB/40.6MiB[============================================>-------------|00:15]5.80MiB/s31.4MiB/40.6MiB[============================================>-------------|00:15]5.85MiB/s31.5MiB/40.6MiB[============================================>-------------|00:14]5.87MiB/s31.7MiB/40.6MiB[============================================>-------------|00:13]5.93MiB/s31.9MiB/40.6MiB[=============================================>------------|00:13]5.95MiB/s32.0MiB/40.6MiB[=============================================>------------|00:12]5.98MiB/s32.2MiB/40.6MiB[=============================================>------------|00:11]5.99MiB/s32.4MiB/40.6MiB[=============================================>------------|00:11]6.04MiB/s32.5MiB/40.6MiB[=============================================>------------|00:10]6.08MiB/s32.7MiB/40.6MiB[==============================================>-----------|00:10]6.03MiB/s32.9MiB/40.6MiB[==============================================>-----------|00:09]6.05MiB/s33.1MiB/40.6MiB[==============================================>-----------|00:09]6.08MiB/s33.3MiB/40.6MiB[===============================================>----------|00:08]6.13MiB/s33.6MiB/40.6MiB[===============================================>----------|00:07]6.30MiB/s33.8MiB/40.6MiB[===============================================>----------|00:07]6.33MiB/s34.0MiB/40.6MiB[================================================>---------|00:07]6.44MiB/s34.3MiB/40.6MiB[================================================>---------|00:06]6.88MiB/s34.6MiB/40.6MiB[================================================>---------|00:05]6.88MiB/s34.8MiB/40.6MiB[=================================================>--------|00:05]6.88MiB/s35.2MiB/40.6MiB[=================================================>--------|00:04]10.50MiB/s35.4MiB/40.6MiB[==================================================>-------|00:04]10.17MiB/s35.6MiB/40.6MiB[==================================================>-------|00:03]9.88MiB/s35.9MiB/40.6MiB[==================================================>-------|00:03]9.36MiB/s36.2MiB/40.6MiB[===================================================>------|00:03]9.27MiB/s36.4MiB/40.6MiB[===================================================>------|00:02]9.08MiB/s36.7MiB/40.6MiB[===================================================>------|00:02]8.83MiB/s37.0MiB/40.6MiB[====================================================>-----|00:02]8.64MiB/s37.2MiB/40.6MiB[====================================================>-----|00:02]8.48MiB/s37.5MiB/40.6MiB[=====================================================>----|00:02]8.26MiB/s37.9MiB/40.6MiB[=====================================================>----|00:02]8.16MiB/s38.1MiB/40.6MiB[=====================================================>----|00:01]8.21MiB/s38.4MiB/40.6MiB[======================================================>---|00:01]8.38MiB/s38.7MiB/40.6MiB[======================================================>---|00:01]8.31MiB/s39.0MiB/40.6MiB[=======================================================>--|00:01]8.40MiB/s39.3MiB/40.6MiB[=======================================================>--|00:01]8.21MiB/s39.6MiB/40.6MiB[========================================================>-|00:00]8.14MiB/s40.0MiB/40.6MiB[========================================================>-|00:00]7.97MiB/s40.2MiB/40.6MiB[==========================================================|00:00]7.75MiB/smpb-8.8.3/_svg/wHzf1M7sd7B3zVa2scBMnjqRf.svg000066400000000000000000005756551466312757300203530ustar00rootroot00000000000000~/go/src/github.com/vbauerster/mpb/examples/complex~/go/src/github.com/vbauerster/mpb/examples/complexmaster*gorun-racemain.gogorun-racemain.gogorun-racemain.goTask#03:installing00:08[======>-------------------------------------------------------]11%Task#03:installing00:07[==============>-----------------------------------------------]24%Task#02:done!Task#03:done!Task#01:done!Task#00:done!~/go/src/github.com/vbauerster/mpb/examples/complexmaster*19s~/go/src/github.com/vbauerster/mpb/examples/complexmastergorun-racemain.gogorun-racemain.gogorun-racemain.gogorun-racemain.goTask#00:downloading4/268[>-------------------------------------------------------------]1%Task#01:downloading2/274[--------------------------------------------------------------]1%Task#02:downloading3/114[=>------------------------------------------------------------]3%Task#03:downloading4/114[=>------------------------------------------------------------]4%Task#00:downloading6/268[>-------------------------------------------------------------]2%Task#01:downloading8/274[=>------------------------------------------------------------]3%Task#02:downloading9/114[====>---------------------------------------------------------]8%Task#03:downloading12/114[======>-------------------------------------------------------]11%Task#00:downloading9/268[=>------------------------------------------------------------]3%Task#01:downloading12/274[==>-----------------------------------------------------------]4%Task#02:downloading12/114[======>-------------------------------------------------------]11%Task#03:downloading24/114[============>-------------------------------------------------]21%Task#00:downloading11/268[==>-----------------------------------------------------------]4%Task#01:downloading16/274[===>----------------------------------------------------------]6%Task#02:downloading18/114[=========>----------------------------------------------------]16%Task#03:downloading36/114[===================>------------------------------------------]32%Task#00:downloading13/268[==>-----------------------------------------------------------]5%Task#01:downloading22/274[====>---------------------------------------------------------]8%Task#02:downloading27/114[==============>-----------------------------------------------]24%Task#03:downloading40/114[=====================>----------------------------------------]35%Task#00:downloading15/268[==>-----------------------------------------------------------]6%Task#01:downloading24/274[====>---------------------------------------------------------]9%Task#02:downloading30/114[===============>----------------------------------------------]26%Task#03:downloading48/114[=========================>------------------------------------]42%Task#00:downloading16/268[===>----------------------------------------------------------]6%Task#01:downloading30/274[======>-------------------------------------------------------]11%Task#02:downloading39/114[====================>-----------------------------------------]34%Task#03:downloading56/114[=============================>--------------------------------]49%Task#00:downloading18/268[===>----------------------------------------------------------]7%Task#01:downloading34/274[=======>------------------------------------------------------]12%Task#02:downloading42/114[======================>---------------------------------------]37%Task#03:downloading64/114[==================================>---------------------------]56%Task#00:downloading19/268[===>----------------------------------------------------------]7%Task#01:downloading40/274[========>-----------------------------------------------------]15%Task#02:downloading45/114[=======================>--------------------------------------]39%Task#03:downloading68/114[====================================>-------------------------]60%Task#00:downloading21/268[====>---------------------------------------------------------]8%Task#01:downloading44/274[=========>----------------------------------------------------]16%Task#02:downloading54/114[============================>---------------------------------]47%Task#03:downloading76/114[========================================>---------------------]67%Task#00:downloading25/268[=====>--------------------------------------------------------]9%Task#01:downloading52/274[===========>--------------------------------------------------]19%Task#02:downloading60/114[================================>-----------------------------]53%Task#03:downloading80/114[===========================================>------------------]70%Task#00:downloading27/268[=====>--------------------------------------------------------]10%Task#01:downloading54/274[===========>--------------------------------------------------]20%Task#02:downloading63/114[=================================>----------------------------]55%Task#03:downloading88/114[===============================================>--------------]77%Task#00:downloading29/268[======>-------------------------------------------------------]11%Task#01:downloading58/274[============>-------------------------------------------------]21%Task#02:downloading69/114[=====================================>------------------------]61%Task#03:downloading92/114[=================================================>------------]81%Task#00:downloading30/268[======>-------------------------------------------------------]11%Task#01:downloading60/274[=============>------------------------------------------------]22%Task#02:downloading75/114[========================================>---------------------]66%Task#03:downloading100/114[=====================================================>--------]88%Task#00:downloading32/268[======>-------------------------------------------------------]12%Task#01:downloading66/274[==============>-----------------------------------------------]24%Task#02:downloading78/114[=========================================>--------------------]68%Task#03:downloading108/114[==========================================================>---]95%Task#00:downloading34/268[=======>------------------------------------------------------]13%Task#01:downloading70/274[===============>----------------------------------------------]26%Task#02:downloading84/114[=============================================>----------------]74%Task#03:downloading114/114[==============================================================]100%Task#00:downloading35/268[=======>------------------------------------------------------]13%Task#01:downloading74/274[================>---------------------------------------------]27%Task#02:downloading90/114[================================================>-------------]79%Task#03:installing00:00[--------------------------------------------------------------]1%Task#00:downloading37/268[========>-----------------------------------------------------]14%Task#01:downloading76/274[================>---------------------------------------------]28%Task#02:downloading96/114[===================================================>----------]84%Task#03:installing00:00[=>------------------------------------------------------------]2%Task#00:downloading40/268[========>-----------------------------------------------------]15%Task#01:downloading78/274[=================>--------------------------------------------]28%Task#02:downloading102/114[======================================================>-------]89%Task#03:installing00:00[=>------------------------------------------------------------]4%Task#00:downloading42/268[=========>----------------------------------------------------]16%Task#01:downloading82/274[==================>-------------------------------------------]30%Task#02:downloading108/114[==========================================================>---]95%Task#03:installing00:00[==>-----------------------------------------------------------]4%Task#00:downloading44/268[=========>----------------------------------------------------]16%Task#01:downloading86/274[==================>-------------------------------------------]31%Task#02:downloading114/114[==============================================================]100%Task#03:installing00:00[==>-----------------------------------------------------------]6%Task#00:downloading47/268[==========>---------------------------------------------------]18%Task#01:downloading88/274[===================>------------------------------------------]32%Task#02:installing00:00[=>------------------------------------------------------------]3%Task#03:installing00:09[====>---------------------------------------------------------]7%Task#00:downloading50/268[===========>--------------------------------------------------]19%Task#01:downloading92/274[====================>-----------------------------------------]34%Task#02:installing00:00[===>----------------------------------------------------------]6%Task#03:installing00:09[====>---------------------------------------------------------]9%Task#00:downloading52/268[===========>--------------------------------------------------]19%Task#01:downloading96/274[=====================>----------------------------------------]35%Task#02:installing00:00[=====>--------------------------------------------------------]9%Task#00:downloading54/268[===========>--------------------------------------------------]20%Task#01:downloading98/274[=====================>----------------------------------------]36%Task#02:installing00:03[========>-----------------------------------------------------]14%Task#00:downloading56/268[============>-------------------------------------------------]21%Task#01:downloading102/274[======================>---------------------------------------]37%Task#02:installing00:03[=========>----------------------------------------------------]17%Task#03:installing00:08[=======>------------------------------------------------------]12%Task#00:downloading57/268[============>-------------------------------------------------]21%Task#01:downloading106/274[=======================>--------------------------------------]39%Task#02:installing00:03[===========>--------------------------------------------------]19%Task#03:installing00:08[=======>------------------------------------------------------]14%Task#00:downloading59/268[=============>------------------------------------------------]22%Task#01:downloading114/274[=========================>------------------------------------]42%Task#02:installing00:03[=============>------------------------------------------------]23%Task#03:installing00:08[========>-----------------------------------------------------]14%Task#00:downloading61/268[=============>------------------------------------------------]23%Task#01:downloading120/274[==========================>-----------------------------------]44%Task#02:installing00:03[===============>----------------------------------------------]25%Task#03:installing00:08[=========>----------------------------------------------------]16%Task#00:downloading63/268[==============>-----------------------------------------------]24%Task#01:downloading126/274[============================>---------------------------------]46%Task#02:installing00:02[=================>--------------------------------------------]29%Task#03:installing00:08[=========>----------------------------------------------------]17%Task#00:downloading67/268[===============>----------------------------------------------]25%Task#01:downloading130/274[============================>---------------------------------]47%Task#02:installing00:02[==================>-------------------------------------------]31%Task#03:installing00:08[==========>---------------------------------------------------]17%Task#00:downloading68/268[===============>----------------------------------------------]25%Task#01:downloading132/274[=============================>--------------------------------]48%Task#02:installing00:02[=====================>----------------------------------------]36%Task#03:installing00:08[===========>--------------------------------------------------]19%Task#00:downloading69/268[===============>----------------------------------------------]26%Task#01:downloading136/274[==============================>-------------------------------]50%Task#02:installing00:02[======================>---------------------------------------]37%Task#03:installing00:08[============>-------------------------------------------------]20%Task#00:downloading71/268[===============>----------------------------------------------]26%Task#01:downloading140/274[===============================>------------------------------]51%Task#02:installing00:02[========================>-------------------------------------]41%Task#03:installing00:07[============>-------------------------------------------------]22%Task#00:downloading73/268[================>---------------------------------------------]27%Task#01:downloading144/274[================================>-----------------------------]53%Task#02:installing00:02[=========================>------------------------------------]42%Task#00:downloading75/268[================>---------------------------------------------]28%Task#01:downloading150/274[=================================>----------------------------]55%Task#02:installing00:02[===========================>----------------------------------]45%Task#00:downloading78/268[=================>--------------------------------------------]29%Task#01:downloading154/274[==================================>---------------------------]56%Task#02:installing00:02[============================>---------------------------------]47%Task#03:installing00:07[===============>----------------------------------------------]26%Task#00:downloading80/268[==================>-------------------------------------------]30%Task#01:downloading158/274[===================================>--------------------------]58%Task#02:installing00:02[==============================>-------------------------------]50%Task#03:installing00:07[================>---------------------------------------------]27%Task#00:downloading82/268[==================>-------------------------------------------]31%Task#01:downloading162/274[====================================>-------------------------]59%Task#02:installing00:02[===============================>------------------------------]51%Task#03:installing00:06[=================>--------------------------------------------]29%Task#00:downloading87/268[===================>------------------------------------------]32%Task#01:downloading164/274[====================================>-------------------------]60%Task#02:installing00:01[==================================>---------------------------]56%Task#03:installing00:06[==================>-------------------------------------------]31%Task#00:downloading90/268[====================>-----------------------------------------]34%Task#01:downloading166/274[=====================================>------------------------]61%Task#02:installing00:01[====================================>-------------------------]60%Task#03:installing00:06[===================>------------------------------------------]32%Task#00:downloading92/268[====================>-----------------------------------------]34%Task#01:downloading172/274[======================================>-----------------------]63%Task#02:installing00:01[======================================>-----------------------]62%Task#03:installing00:05[====================>-----------------------------------------]34%Task#00:downloading93/268[=====================>----------------------------------------]35%Task#01:downloading176/274[=======================================>----------------------]64%Task#02:installing00:01[=======================================>----------------------]65%Task#03:installing00:05[=====================>----------------------------------------]35%Task#00:downloading96/268[=====================>----------------------------------------]36%Task#01:downloading178/274[=======================================>----------------------]65%Task#02:installing00:01[==========================================>-------------------]69%Task#03:installing00:05[=====================>----------------------------------------]36%Task#00:downloading99/268[======================>---------------------------------------]37%Task#01:downloading182/274[========================================>---------------------]66%Task#02:installing00:01[===========================================>------------------]71%Task#03:installing00:05[======================>---------------------------------------]37%Task#00:downloading102/268[=======================>--------------------------------------]38%Task#01:downloading186/274[=========================================>--------------------]68%Task#02:installing00:00[==============================================>---------------]75%Task#03:installing00:06[======================>---------------------------------------]37%Task#00:downloading105/268[=======================>--------------------------------------]39%Task#01:downloading188/274[==========================================>-------------------]69%Task#02:installing00:00[===============================================>--------------]78%Task#03:installing00:06[=======================>--------------------------------------]39%Task#00:downloading107/268[========================>-------------------------------------]40%Task#01:downloading192/274[==========================================>-------------------]70%Task#02:installing00:00[=================================================>------------]80%Task#03:installing00:05[========================>-------------------------------------]40%Task#00:downloading109/268[========================>-------------------------------------]41%Task#01:downloading194/274[===========================================>------------------]71%Task#02:installing00:00[==================================================>-----------]82%Task#03:installing00:05[========================>-------------------------------------]41%Task#00:downloading112/268[=========================>------------------------------------]42%Task#01:downloading198/274[============================================>-----------------]72%Task#02:installing00:00[===================================================>----------]84%Task#03:installing00:05[=========================>------------------------------------]42%Task#00:downloading114/268[=========================>------------------------------------]43%Task#01:downloading202/274[=============================================>----------------]74%Task#02:installing00:00[=====================================================>--------]88%Task#03:installing00:05[==========================>-----------------------------------]44%Task#00:downloading116/268[==========================>-----------------------------------]43%Task#01:downloading206/274[==============================================>---------------]75%Task#02:installing00:00[========================================================>-----]92%Task#03:installing00:04[===========================>----------------------------------]46%Task#00:downloading119/268[===========================>----------------------------------]44%Task#01:downloading210/274[===============================================>--------------]77%Task#02:installing00:00[=========================================================>----]94%Task#03:installing00:04[=============================>--------------------------------]48%Task#00:downloading122/268[===========================>----------------------------------]46%Task#01:downloading212/274[===============================================>--------------]77%Task#02:installing00:00[===========================================================>--]97%Task#03:installing00:04[=============================>--------------------------------]49%Task#00:downloading124/268[============================>---------------------------------]46%Task#01:downloading214/274[===============================================>--------------]78%Task#02:installing00:00[==============================================================]99%Task#03:installing00:04[==============================>-------------------------------]50%Task#00:downloading126/268[============================>---------------------------------]47%Task#01:downloading218/274[================================================>-------------]80%Task#02:installing00:00[==============================================================]100%Task#03:installing00:04[===============================>------------------------------]52%Task#00:downloading127/268[============================>---------------------------------]47%Task#01:downloading220/274[=================================================>------------]80%Task#03:installing00:04[================================>-----------------------------]53%Task#00:downloading130/268[=============================>--------------------------------]49%Task#01:downloading224/274[==================================================>-----------]82%Task#03:installing00:03[=================================>----------------------------]55%Task#00:downloading132/268[==============================>-------------------------------]49%Task#01:downloading230/274[===================================================>----------]84%Task#03:installing00:03[==================================>---------------------------]57%Task#00:downloading134/268[==============================>-------------------------------]50%Task#01:downloading234/274[====================================================>---------]85%Task#03:installing00:03[====================================>-------------------------]59%Task#00:downloading136/268[==============================>-------------------------------]51%Task#01:downloading238/274[=====================================================>--------]87%Task#03:installing00:03[====================================>-------------------------]60%Task#00:downloading139/268[===============================>------------------------------]52%Task#01:downloading242/274[======================================================>-------]88%Task#03:installing00:03[=====================================>------------------------]61%Task#00:downloading141/268[================================>-----------------------------]53%Task#01:downloading246/274[=======================================================>------]90%Task#03:installing00:03[======================================>-----------------------]63%Task#00:downloading143/268[================================>-----------------------------]53%Task#01:downloading254/274[========================================================>-----]93%Task#03:installing00:02[=======================================>----------------------]65%Task#00:downloading147/268[=================================>----------------------------]55%Task#01:downloading258/274[=========================================================>----]94%Task#03:installing00:02[========================================>---------------------]66%Task#00:downloading149/268[=================================>----------------------------]56%Task#01:downloading262/274[==========================================================>---]96%Task#03:installing00:02[=========================================>--------------------]68%Task#00:downloading150/268[==================================>---------------------------]56%Task#01:downloading268/274[============================================================>-]98%Task#03:installing00:02[==========================================>-------------------]69%Task#00:downloading152/268[==================================>---------------------------]57%Task#01:downloading272/274[==============================================================]99%Task#03:installing00:02[===========================================>------------------]70%Task#00:downloading156/268[===================================>--------------------------]58%Task#01:downloading274/274[==============================================================]100%Task#03:installing00:02[===========================================>------------------]71%Task#00:downloading160/268[====================================>-------------------------]60%Task#01:installing00:00[==>-----------------------------------------------------------]5%Task#03:installing00:02[============================================>-----------------]73%Task#00:downloading162/268[====================================>-------------------------]60%Task#01:installing00:00[====>---------------------------------------------------------]8%Task#03:installing00:02[=============================================>----------------]74%Task#00:downloading163/268[=====================================>------------------------]61%Task#01:installing00:00[=======>------------------------------------------------------]14%Task#03:installing00:02[=============================================>----------------]75%Task#00:downloading168/268[======================================>-----------------------]63%Task#01:installing00:00[==========>---------------------------------------------------]17%Task#03:installing00:02[==============================================>---------------]76%Task#00:downloading170/268[======================================>-----------------------]63%Task#01:installing00:02[============>-------------------------------------------------]20%Task#03:installing00:02[===============================================>--------------]77%Task#00:downloading173/268[=======================================>----------------------]65%Task#01:installing00:02[===============>----------------------------------------------]25%Task#03:installing00:01[================================================>-------------]78%Task#00:downloading176/268[========================================>---------------------]66%Task#01:installing00:02[==================>-------------------------------------------]31%Task#03:installing00:01[================================================>-------------]79%Task#00:downloading178/268[========================================>---------------------]66%Task#01:installing00:01[=====================>----------------------------------------]36%Task#03:installing00:01[=================================================>------------]81%Task#00:downloading180/268[=========================================>--------------------]67%Task#01:installing00:01[======================>---------------------------------------]37%Task#03:installing00:01[==================================================>-----------]83%Task#00:downloading182/268[=========================================>--------------------]68%Task#01:installing00:01[========================>-------------------------------------]41%Task#03:installing00:01[===================================================>----------]83%Task#00:downloading185/268[==========================================>-------------------]69%Task#01:installing00:01[=========================>------------------------------------]42%Task#03:installing00:01[===================================================>----------]84%Task#00:downloading188/268[==========================================>-------------------]70%Task#01:installing00:01[===========================>----------------------------------]46%Task#03:installing00:01[====================================================>---------]85%Task#00:downloading190/268[===========================================>------------------]71%Task#01:installing00:01[=============================>--------------------------------]49%Task#03:installing00:01[=====================================================>--------]87%Task#00:downloading192/268[===========================================>------------------]72%Task#01:installing00:01[================================>-----------------------------]53%Task#03:installing00:01[======================================================>-------]88%Task#00:downloading194/268[============================================>-----------------]72%Task#01:installing00:01[=================================>----------------------------]54%Task#03:installing00:00[======================================================>-------]89%Task#00:downloading197/268[=============================================>----------------]74%Task#01:installing00:01[===================================>--------------------------]58%Task#03:installing00:00[=======================================================>------]91%Task#00:downloading198/268[=============================================>----------------]74%Task#01:installing00:01[=====================================>------------------------]61%Task#03:installing00:00[========================================================>-----]93%Task#00:downloading202/268[==============================================>---------------]75%Task#01:installing00:01[======================================>-----------------------]63%Task#03:installing00:00[=========================================================>----]93%Task#00:downloading204/268[==============================================>---------------]76%Task#01:installing00:01[========================================>---------------------]66%Task#03:installing00:00[==========================================================>---]94%Task#00:downloading206/268[===============================================>--------------]77%Task#01:installing00:01[==========================================>-------------------]69%Task#03:installing00:00[===========================================================>--]97%Task#00:downloading209/268[===============================================>--------------]78%Task#01:installing00:00[============================================>-----------------]73%Task#03:installing00:00[===========================================================>--]98%Task#00:downloading212/268[================================================>-------------]79%Task#01:installing00:00[==============================================>---------------]76%Task#03:installing00:00[============================================================>-]99%Task#00:downloading213/268[================================================>-------------]79%Task#01:installing00:00[================================================>-------------]80%Task#03:installing00:00[==============================================================]100%Task#00:downloading215/268[=================================================>------------]80%Task#01:installing00:00[=================================================>------------]81%Task#00:downloading217/268[=================================================>------------]81%Task#01:installing00:00[=====================================================>--------]86%Task#00:downloading219/268[==================================================>-----------]82%Task#01:installing00:00[======================================================>-------]88%Task#00:downloading220/268[==================================================>-----------]82%Task#01:installing00:00[========================================================>-----]92%Task#00:downloading221/268[==================================================>-----------]82%Task#01:installing00:00[=========================================================>----]93%Task#00:downloading225/268[===================================================>----------]84%Task#01:installing00:00[===========================================================>--]97%Task#00:downloading227/268[====================================================>---------]85%Task#01:installing00:00[============================================================>-]98%Task#00:downloading229/268[====================================================>---------]85%Task#01:installing00:00[==============================================================]100%Task#00:downloading230/268[====================================================>---------]86%Task#00:downloading232/268[=====================================================>--------]87%Task#00:downloading233/268[=====================================================>--------]87%Task#00:downloading235/268[=====================================================>--------]88%Task#00:downloading236/268[======================================================>-------]88%Task#00:downloading237/268[======================================================>-------]88%Task#00:downloading240/268[=======================================================>------]90%Task#00:downloading241/268[=======================================================>------]90%Task#00:downloading244/268[=======================================================>------]91%Task#00:downloading246/268[========================================================>-----]92%Task#00:downloading249/268[=========================================================>----]93%Task#00:downloading251/268[=========================================================>----]94%Task#00:downloading253/268[==========================================================>---]94%Task#00:downloading256/268[==========================================================>---]96%Task#00:downloading257/268[==========================================================>---]96%Task#00:downloading259/268[===========================================================>--]97%Task#00:downloading261/268[===========================================================>--]97%Task#00:downloading262/268[============================================================>-]98%Task#00:downloading263/268[============================================================>-]98%Task#00:downloading266/268[==============================================================]99%Task#00:downloading268/268[==============================================================]100%Task#00:installing00:00[=>------------------------------------------------------------]3%Task#00:installing00:00[======>-------------------------------------------------------]12%Task#00:installing00:00[========>-----------------------------------------------------]14%Task#00:installing00:00[============>-------------------------------------------------]20%Task#00:installing00:00[=================>--------------------------------------------]29%Task#00:installing00:01[===================>------------------------------------------]32%Task#00:installing00:01[==========================>-----------------------------------]43%Task#00:installing00:01[==============================>-------------------------------]49%Task#00:installing00:00[=================================>----------------------------]55%Task#00:installing00:00[=====================================>------------------------]61%Task#00:installing00:00[========================================>---------------------]67%Task#00:installing00:00[============================================>-----------------]72%Task#00:installing00:00[=================================================>------------]81%Task#00:installing00:00[=====================================================>--------]87%Task#00:installing00:00[=========================================================>----]93%Task#00:installing00:00[============================================================>-]99%Task#00:installing00:00[==============================================================]100%mpb-8.8.3/bar.go000066400000000000000000000337221466312757300134300ustar00rootroot00000000000000package mpb import ( "bytes" "context" "io" "strings" "sync" "time" "github.com/acarl005/stripansi" "github.com/mattn/go-runewidth" "github.com/vbauerster/mpb/v8/decor" ) // Bar represents a progress bar. type Bar struct { index int // used by heap priority int // used by heap frameCh chan *renderFrame operateState chan func(*bState) container *Progress bs *bState bsOk chan struct{} ctx context.Context cancel func() } type syncTable [2][]chan int type extenderFunc func(decor.Statistics, ...io.Reader) ([]io.Reader, error) // bState is actual bar's state. type bState struct { id int priority int reqWidth int shutdown int total int64 current int64 refill int64 trimSpace bool aborted bool triggerComplete bool rmOnComplete bool noPop bool autoRefresh bool buffers [3]*bytes.Buffer decorators [2][]decor.Decorator ewmaDecorators []decor.EwmaDecorator filler BarFiller extender extenderFunc renderReq chan<- time.Time waitBar *Bar // key for (*pState).queueBars } type renderFrame struct { rows []io.Reader shutdown int rmOnComplete bool noPop bool err error } func newBar(ctx context.Context, container *Progress, bs *bState) *Bar { ctx, cancel := context.WithCancel(ctx) bar := &Bar{ priority: bs.priority, frameCh: make(chan *renderFrame, 1), operateState: make(chan func(*bState)), bsOk: make(chan struct{}), container: container, ctx: ctx, cancel: cancel, } container.bwg.Add(1) go bar.serve(bs) return bar } // ProxyReader wraps io.Reader with metrics required for progress // tracking. If `r` is 'unknown total/size' reader it's mandatory // to call `(*Bar).SetTotal(-1, true)` after the wrapper returns // `io.EOF`. If bar is already completed or aborted, returns nil. // Panics if `r` is nil. func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser { if r == nil { panic("expected non nil io.Reader") } result := make(chan io.ReadCloser) select { case b.operateState <- func(s *bState) { result <- newProxyReader(r, b, len(s.ewmaDecorators) != 0) }: return <-result case <-b.ctx.Done(): return nil } } // ProxyWriter wraps io.Writer with metrics required for progress tracking. // If bar is already completed or aborted, returns nil. // Panics if `w` is nil. func (b *Bar) ProxyWriter(w io.Writer) io.WriteCloser { if w == nil { panic("expected non nil io.Writer") } result := make(chan io.WriteCloser) select { case b.operateState <- func(s *bState) { result <- newProxyWriter(w, b, len(s.ewmaDecorators) != 0) }: return <-result case <-b.ctx.Done(): return nil } } // ID returs id of the bar. func (b *Bar) ID() int { result := make(chan int) select { case b.operateState <- func(s *bState) { result <- s.id }: return <-result case <-b.bsOk: return b.bs.id } } // Current returns bar's current value, in other words sum of all increments. func (b *Bar) Current() int64 { result := make(chan int64) select { case b.operateState <- func(s *bState) { result <- s.current }: return <-result case <-b.bsOk: return b.bs.current } } // SetRefill sets refill flag with specified amount. // The underlying BarFiller will change its visual representation, to // indicate refill event. Refill event may be referred to some retry // operation for example. func (b *Bar) SetRefill(amount int64) { select { case b.operateState <- func(s *bState) { if amount < s.current { s.refill = amount } else { s.refill = s.current } }: case <-b.ctx.Done(): } } // TraverseDecorators traverses available decorators and calls cb func // on each in a new goroutine. Decorators implementing decor.Wrapper // interface are unwrapped first. func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) { select { case b.operateState <- func(s *bState) { var wg sync.WaitGroup for _, decorators := range s.decorators { wg.Add(len(decorators)) for _, d := range decorators { d := d go func() { cb(unwrap(d)) wg.Done() }() } } wg.Wait() }: case <-b.ctx.Done(): } } // EnableTriggerComplete enables triggering complete event. It's effective // only for bars which were constructed with `total <= 0`. If `curren >= total` // at the moment of call, complete event is triggered right away. func (b *Bar) EnableTriggerComplete() { select { case b.operateState <- func(s *bState) { if s.triggerComplete { return } if s.current >= s.total { s.current = s.total s.triggerCompletion(b) } else { s.triggerComplete = true } }: case <-b.ctx.Done(): } } // SetTotal sets total to an arbitrary value. It's effective only for bar // which was constructed with `total <= 0`. Setting total to negative value // is equivalent to `(*Bar).SetTotal((*Bar).Current(), bool)` but faster. // If `complete` is true complete event is triggered right away. // Calling `(*Bar).EnableTriggerComplete` makes this one no operational. func (b *Bar) SetTotal(total int64, complete bool) { select { case b.operateState <- func(s *bState) { if s.triggerComplete { return } if total < 0 { s.total = s.current } else { s.total = total } if complete { s.current = s.total s.triggerCompletion(b) } }: case <-b.ctx.Done(): } } // SetCurrent sets progress' current to an arbitrary value. func (b *Bar) SetCurrent(current int64) { if current < 0 { return } select { case b.operateState <- func(s *bState) { s.current = current if s.triggerComplete && s.current >= s.total { s.current = s.total s.triggerCompletion(b) } }: case <-b.ctx.Done(): } } // Increment is a shorthand for b.IncrInt64(1). func (b *Bar) Increment() { b.IncrInt64(1) } // IncrBy is a shorthand for b.IncrInt64(int64(n)). func (b *Bar) IncrBy(n int) { b.IncrInt64(int64(n)) } // IncrInt64 increments progress by amount of n. func (b *Bar) IncrInt64(n int64) { select { case b.operateState <- func(s *bState) { s.current += n if s.triggerComplete && s.current >= s.total { s.current = s.total s.triggerCompletion(b) } }: case <-b.ctx.Done(): } } // EwmaIncrement is a shorthand for b.EwmaIncrInt64(1, iterDur). func (b *Bar) EwmaIncrement(iterDur time.Duration) { b.EwmaIncrInt64(1, iterDur) } // EwmaIncrBy is a shorthand for b.EwmaIncrInt64(int64(n), iterDur). func (b *Bar) EwmaIncrBy(n int, iterDur time.Duration) { b.EwmaIncrInt64(int64(n), iterDur) } // EwmaIncrInt64 increments progress by amount of n and updates EWMA based // decorators by dur of a single iteration. func (b *Bar) EwmaIncrInt64(n int64, iterDur time.Duration) { select { case b.operateState <- func(s *bState) { var wg sync.WaitGroup wg.Add(len(s.ewmaDecorators)) for _, d := range s.ewmaDecorators { d := d go func() { d.EwmaUpdate(n, iterDur) wg.Done() }() } s.current += n if s.triggerComplete && s.current >= s.total { s.current = s.total s.triggerCompletion(b) } wg.Wait() }: case <-b.ctx.Done(): } } // EwmaSetCurrent sets progress' current to an arbitrary value and updates // EWMA based decorators by dur of a single iteration. func (b *Bar) EwmaSetCurrent(current int64, iterDur time.Duration) { if current < 0 { return } select { case b.operateState <- func(s *bState) { n := current - s.current var wg sync.WaitGroup wg.Add(len(s.ewmaDecorators)) for _, d := range s.ewmaDecorators { d := d go func() { d.EwmaUpdate(n, iterDur) wg.Done() }() } s.current = current if s.triggerComplete && s.current >= s.total { s.current = s.total s.triggerCompletion(b) } wg.Wait() }: case <-b.ctx.Done(): } } // DecoratorAverageAdjust adjusts decorators implementing decor.AverageDecorator interface. // Call if there is need to set start time after decorators have been constructed. func (b *Bar) DecoratorAverageAdjust(start time.Time) { b.TraverseDecorators(func(d decor.Decorator) { if d, ok := d.(decor.AverageDecorator); ok { d.AverageAdjust(start) } }) } // SetPriority changes bar's order among multiple bars. Zero is highest // priority, i.e. bar will be on top. If you don't need to set priority // dynamically, better use BarPriority option. func (b *Bar) SetPriority(priority int) { b.container.UpdateBarPriority(b, priority, false) } // Abort interrupts bar's running goroutine. Abort won't be engaged // if bar is already in complete state. If drop is true bar will be // removed as well. To make sure that bar has been removed call // `(*Bar).Wait()` method. func (b *Bar) Abort(drop bool) { select { case b.operateState <- func(s *bState) { if s.aborted || s.completed() { return } s.aborted = true s.rmOnComplete = drop s.triggerCompletion(b) }: case <-b.ctx.Done(): } } // Aborted reports whether the bar is in aborted state. func (b *Bar) Aborted() bool { result := make(chan bool) select { case b.operateState <- func(s *bState) { result <- s.aborted }: return <-result case <-b.bsOk: return b.bs.aborted } } // Completed reports whether the bar is in completed state. func (b *Bar) Completed() bool { result := make(chan bool) select { case b.operateState <- func(s *bState) { result <- s.completed() }: return <-result case <-b.bsOk: return b.bs.completed() } } // IsRunning reports whether the bar is in running state. func (b *Bar) IsRunning() bool { select { case <-b.ctx.Done(): return false default: return true } } // Wait blocks until bar is completed or aborted. func (b *Bar) Wait() { <-b.bsOk } func (b *Bar) serve(bs *bState) { decoratorsOnShutdown := func(decorators []decor.Decorator) { for _, d := range decorators { if d, ok := unwrap(d).(decor.ShutdownListener); ok { b.container.bwg.Add(1) go func() { d.OnShutdown() b.container.bwg.Done() }() } } } for { select { case op := <-b.operateState: op(bs) case <-b.ctx.Done(): decoratorsOnShutdown(bs.decorators[0]) decoratorsOnShutdown(bs.decorators[1]) // bar can be aborted by canceling parent ctx without calling b.Abort bs.aborted = !bs.completed() b.bs = bs close(b.bsOk) b.container.bwg.Done() return } } } func (b *Bar) render(tw int) { fn := func(s *bState) { frame := new(renderFrame) stat := s.newStatistics(tw) r, err := s.draw(stat) if err != nil { for _, buf := range s.buffers { buf.Reset() } frame.err = err b.frameCh <- frame return } frame.rows, frame.err = s.extender(stat, r) if s.aborted || s.completed() { frame.shutdown = s.shutdown frame.rmOnComplete = s.rmOnComplete frame.noPop = s.noPop // post increment makes sure OnComplete decorators are rendered s.shutdown++ } b.frameCh <- frame } select { case b.operateState <- fn: case <-b.bsOk: fn(b.bs) } } func (b *Bar) tryEarlyRefresh(renderReq chan<- time.Time) { var otherRunning int b.container.traverseBars(func(bar *Bar) bool { if b != bar && bar.IsRunning() { otherRunning++ return false // stop traverse } return true // continue traverse }) if otherRunning == 0 { for { select { case renderReq <- time.Now(): case <-b.ctx.Done(): return } } } } func (b *Bar) wSyncTable() syncTable { result := make(chan syncTable) select { case b.operateState <- func(s *bState) { result <- s.wSyncTable() }: return <-result case <-b.bsOk: return b.bs.wSyncTable() } } func (s *bState) draw(stat decor.Statistics) (_ io.Reader, err error) { decorFiller := func(buf *bytes.Buffer, decorators []decor.Decorator) (err error) { for _, d := range decorators { // need to call Decor in any case becase of width synchronization str, width := d.Decor(stat) if err != nil { continue } if w := stat.AvailableWidth - width; w >= 0 { _, err = buf.WriteString(str) stat.AvailableWidth = w } else if stat.AvailableWidth > 0 { trunc := runewidth.Truncate(stripansi.Strip(str), stat.AvailableWidth, "…") _, err = buf.WriteString(trunc) stat.AvailableWidth = 0 } } return err } for i, buf := range s.buffers[:2] { err = decorFiller(buf, s.decorators[i]) if err != nil { return nil, err } } spaces := []io.Reader{ strings.NewReader(" "), strings.NewReader(" "), } if s.trimSpace || stat.AvailableWidth < 2 { for _, r := range spaces { _, _ = io.Copy(io.Discard, r) } } else { stat.AvailableWidth -= 2 } err = s.filler.Fill(s.buffers[2], stat) if err != nil { return nil, err } return io.MultiReader( s.buffers[0], spaces[0], s.buffers[2], spaces[1], s.buffers[1], strings.NewReader("\n"), ), nil } func (s *bState) wSyncTable() (table syncTable) { var count int var row []chan int for i, decorators := range s.decorators { for _, d := range decorators { if ch, ok := d.Sync(); ok { row = append(row, ch) count++ } } switch i { case 0: table[i] = row[0:count] default: table[i] = row[len(table[i-1]):count] } } return table } func (s *bState) populateEwmaDecorators(decorators []decor.Decorator) { for _, d := range decorators { if d, ok := unwrap(d).(decor.EwmaDecorator); ok { s.ewmaDecorators = append(s.ewmaDecorators, d) } } } func (s *bState) triggerCompletion(b *Bar) { s.triggerComplete = true if s.autoRefresh { // Technically this call isn't required, but if refresh rate is set to // one hour for example and bar completes within a few minutes p.Wait() // will wait for one hour. This call helps to avoid unnecessary waiting. go b.tryEarlyRefresh(s.renderReq) } else { b.cancel() } } func (s bState) completed() bool { return s.triggerComplete && s.current == s.total } func (s bState) newStatistics(tw int) decor.Statistics { return decor.Statistics{ AvailableWidth: tw, RequestedWidth: s.reqWidth, ID: s.id, Total: s.total, Current: s.current, Refill: s.refill, Completed: s.completed(), Aborted: s.aborted, } } func unwrap(d decor.Decorator) decor.Decorator { if d, ok := d.(decor.Wrapper); ok { return unwrap(d.Unwrap()) } return d } mpb-8.8.3/bar_filler.go000066400000000000000000000012331466312757300147550ustar00rootroot00000000000000package mpb import ( "io" "github.com/vbauerster/mpb/v8/decor" ) // BarFiller interface. // Bar (without decorators) renders itself by calling BarFiller's Fill method. type BarFiller interface { Fill(io.Writer, decor.Statistics) error } // BarFillerBuilder interface. // Default implementations are: // // BarStyle() // SpinnerStyle() // NopStyle() type BarFillerBuilder interface { Build() BarFiller } // BarFillerFunc is function type adapter to convert compatible function // into BarFiller interface. type BarFillerFunc func(io.Writer, decor.Statistics) error func (f BarFillerFunc) Fill(w io.Writer, stat decor.Statistics) error { return f(w, stat) } mpb-8.8.3/bar_filler_bar.go000066400000000000000000000154331466312757300156100ustar00rootroot00000000000000package mpb import ( "io" "github.com/mattn/go-runewidth" "github.com/vbauerster/mpb/v8/decor" "github.com/vbauerster/mpb/v8/internal" ) const ( iLbound = iota iRbound iRefiller iFiller iTip iPadding components ) var defaultBarStyle = [components]string{"[", "]", "+", "=", ">", "-"} // BarStyleComposer interface. type BarStyleComposer interface { BarFillerBuilder Lbound(string) BarStyleComposer LboundMeta(func(string) string) BarStyleComposer Rbound(string) BarStyleComposer RboundMeta(func(string) string) BarStyleComposer Filler(string) BarStyleComposer FillerMeta(func(string) string) BarStyleComposer Refiller(string) BarStyleComposer RefillerMeta(func(string) string) BarStyleComposer Padding(string) BarStyleComposer PaddingMeta(func(string) string) BarStyleComposer Tip(frames ...string) BarStyleComposer TipMeta(func(string) string) BarStyleComposer TipOnComplete() BarStyleComposer Reverse() BarStyleComposer } type component struct { width int bytes []byte } type flushSection struct { meta func(io.Writer, []byte) error bytes []byte } type bFiller struct { components [components]component meta [components]func(io.Writer, []byte) error flush func(io.Writer, ...flushSection) error tip struct { onComplete bool count uint frames []component } } type barStyle struct { style [components]string metaFuncs [components]func(io.Writer, []byte) error tipFrames []string tipOnComplete bool rev bool } // BarStyle constructs default bar style which can be altered via // BarStyleComposer interface. func BarStyle() BarStyleComposer { bs := barStyle{ style: defaultBarStyle, tipFrames: []string{defaultBarStyle[iTip]}, } for i := range bs.metaFuncs { bs.metaFuncs[i] = defaultMeta } return bs } func (s barStyle) Lbound(bound string) BarStyleComposer { s.style[iLbound] = bound return s } func (s barStyle) LboundMeta(fn func(string) string) BarStyleComposer { s.metaFuncs[iLbound] = makeMetaFunc(fn) return s } func (s barStyle) Rbound(bound string) BarStyleComposer { s.style[iRbound] = bound return s } func (s barStyle) RboundMeta(fn func(string) string) BarStyleComposer { s.metaFuncs[iRbound] = makeMetaFunc(fn) return s } func (s barStyle) Filler(filler string) BarStyleComposer { s.style[iFiller] = filler return s } func (s barStyle) FillerMeta(fn func(string) string) BarStyleComposer { s.metaFuncs[iFiller] = makeMetaFunc(fn) return s } func (s barStyle) Refiller(refiller string) BarStyleComposer { s.style[iRefiller] = refiller return s } func (s barStyle) RefillerMeta(fn func(string) string) BarStyleComposer { s.metaFuncs[iRefiller] = makeMetaFunc(fn) return s } func (s barStyle) Padding(padding string) BarStyleComposer { s.style[iPadding] = padding return s } func (s barStyle) PaddingMeta(fn func(string) string) BarStyleComposer { s.metaFuncs[iPadding] = makeMetaFunc(fn) return s } func (s barStyle) Tip(frames ...string) BarStyleComposer { if len(frames) != 0 { s.tipFrames = frames } return s } func (s barStyle) TipMeta(fn func(string) string) BarStyleComposer { s.metaFuncs[iTip] = makeMetaFunc(fn) return s } func (s barStyle) TipOnComplete() BarStyleComposer { s.tipOnComplete = true return s } func (s barStyle) Reverse() BarStyleComposer { s.rev = true return s } func (s barStyle) Build() BarFiller { bf := &bFiller{ meta: s.metaFuncs, } bf.components[iLbound] = component{ width: runewidth.StringWidth(s.style[iLbound]), bytes: []byte(s.style[iLbound]), } bf.components[iRbound] = component{ width: runewidth.StringWidth(s.style[iRbound]), bytes: []byte(s.style[iRbound]), } bf.components[iFiller] = component{ width: runewidth.StringWidth(s.style[iFiller]), bytes: []byte(s.style[iFiller]), } bf.components[iRefiller] = component{ width: runewidth.StringWidth(s.style[iRefiller]), bytes: []byte(s.style[iRefiller]), } bf.components[iPadding] = component{ width: runewidth.StringWidth(s.style[iPadding]), bytes: []byte(s.style[iPadding]), } bf.tip.onComplete = s.tipOnComplete bf.tip.frames = make([]component, len(s.tipFrames)) for i, t := range s.tipFrames { bf.tip.frames[i] = component{ width: runewidth.StringWidth(t), bytes: []byte(t), } } if s.rev { bf.flush = func(w io.Writer, sections ...flushSection) error { for i := len(sections) - 1; i >= 0; i-- { if s := sections[i]; len(s.bytes) != 0 { err := s.meta(w, s.bytes) if err != nil { return err } } } return nil } } else { bf.flush = func(w io.Writer, sections ...flushSection) error { for _, s := range sections { if len(s.bytes) != 0 { err := s.meta(w, s.bytes) if err != nil { return err } } } return nil } } return bf } func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) error { width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth) // don't count brackets as progress width -= (s.components[iLbound].width + s.components[iRbound].width) if width < 0 { return nil } err := s.meta[iLbound](w, s.components[iLbound].bytes) if err != nil { return err } if width == 0 { return s.meta[iRbound](w, s.components[iRbound].bytes) } var tip component var refilling, filling, padding []byte var fillCount int curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width))) if curWidth != 0 { if !stat.Completed || s.tip.onComplete { tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))] s.tip.count++ fillCount += tip.width } switch refWidth := 0; { case stat.Refill != 0: refWidth = int(internal.PercentageRound(stat.Total, stat.Refill, uint(width))) curWidth -= refWidth refWidth += curWidth fallthrough default: for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w { filling = append(filling, s.components[iFiller].bytes...) } for w := s.components[iRefiller].width; refWidth-fillCount >= w; fillCount += w { refilling = append(refilling, s.components[iRefiller].bytes...) } } } for w := s.components[iPadding].width; width-fillCount >= w; fillCount += w { padding = append(padding, s.components[iPadding].bytes...) } for w := 1; width-fillCount >= w; fillCount += w { padding = append(padding, "…"...) } err = s.flush(w, flushSection{s.meta[iRefiller], refilling}, flushSection{s.meta[iFiller], filling}, flushSection{s.meta[iTip], tip.bytes}, flushSection{s.meta[iPadding], padding}, ) if err != nil { return err } return s.meta[iRbound](w, s.components[iRbound].bytes) } func makeMetaFunc(fn func(string) string) func(io.Writer, []byte) error { return func(w io.Writer, p []byte) (err error) { _, err = io.WriteString(w, fn(string(p))) return err } } func defaultMeta(w io.Writer, p []byte) (err error) { _, err = w.Write(p) return err } mpb-8.8.3/bar_filler_nop.go000066400000000000000000000010261466312757300156310ustar00rootroot00000000000000package mpb import ( "io" "github.com/vbauerster/mpb/v8/decor" ) // barFillerBuilderFunc is function type adapter to convert compatible // function into BarFillerBuilder interface. type barFillerBuilderFunc func() BarFiller func (f barFillerBuilderFunc) Build() BarFiller { return f() } // NopStyle provides BarFillerBuilder which builds NOP BarFiller. func NopStyle() BarFillerBuilder { return barFillerBuilderFunc(func() BarFiller { return BarFillerFunc(func(io.Writer, decor.Statistics) error { return nil }) }) } mpb-8.8.3/bar_filler_spinner.go000066400000000000000000000044501466312757300165170ustar00rootroot00000000000000package mpb import ( "io" "strings" "github.com/mattn/go-runewidth" "github.com/vbauerster/mpb/v8/decor" "github.com/vbauerster/mpb/v8/internal" ) const ( positionLeft = 1 + iota positionRight ) var defaultSpinnerStyle = [...]string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} // SpinnerStyleComposer interface. type SpinnerStyleComposer interface { BarFillerBuilder PositionLeft() SpinnerStyleComposer PositionRight() SpinnerStyleComposer Meta(func(string) string) SpinnerStyleComposer } type sFiller struct { frames []string count uint meta func(string) string position func(string, int) string } type spinnerStyle struct { position uint frames []string meta func(string) string } // SpinnerStyle constructs default spinner style which can be altered via // SpinnerStyleComposer interface. func SpinnerStyle(frames ...string) SpinnerStyleComposer { ss := spinnerStyle{ meta: func(s string) string { return s }, } if len(frames) != 0 { ss.frames = frames } else { ss.frames = defaultSpinnerStyle[:] } return ss } func (s spinnerStyle) PositionLeft() SpinnerStyleComposer { s.position = positionLeft return s } func (s spinnerStyle) PositionRight() SpinnerStyleComposer { s.position = positionRight return s } func (s spinnerStyle) Meta(fn func(string) string) SpinnerStyleComposer { s.meta = fn return s } func (s spinnerStyle) Build() BarFiller { sf := &sFiller{ frames: s.frames, meta: s.meta, } switch s.position { case positionLeft: sf.position = func(frame string, padWidth int) string { return frame + strings.Repeat(" ", padWidth) } case positionRight: sf.position = func(frame string, padWidth int) string { return strings.Repeat(" ", padWidth) + frame } default: sf.position = func(frame string, padWidth int) string { return strings.Repeat(" ", padWidth/2) + frame + strings.Repeat(" ", padWidth/2+padWidth%2) } } return sf } func (s *sFiller) Fill(w io.Writer, stat decor.Statistics) error { width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth) frame := s.frames[s.count%uint(len(s.frames))] frameWidth := runewidth.StringWidth(frame) s.count++ if width < frameWidth { return nil } _, err := io.WriteString(w, s.position(s.meta(frame), width-frameWidth)) return err } mpb-8.8.3/bar_option.go000066400000000000000000000117711466312757300150200ustar00rootroot00000000000000package mpb import ( "bytes" "io" "github.com/vbauerster/mpb/v8/decor" ) // BarOption is a func option to alter default behavior of a bar. type BarOption func(*bState) func inspect(decorators []decor.Decorator) (dest []decor.Decorator) { for _, decorator := range decorators { if decorator == nil { continue } dest = append(dest, decorator) } return } // PrependDecorators let you inject decorators to the bar's left side. func PrependDecorators(decorators ...decor.Decorator) BarOption { decorators = inspect(decorators) return func(s *bState) { s.populateEwmaDecorators(decorators) s.decorators[0] = decorators } } // AppendDecorators let you inject decorators to the bar's right side. func AppendDecorators(decorators ...decor.Decorator) BarOption { decorators = inspect(decorators) return func(s *bState) { s.populateEwmaDecorators(decorators) s.decorators[1] = decorators } } // BarID sets bar id. func BarID(id int) BarOption { return func(s *bState) { s.id = id } } // BarWidth sets bar width independent of the container. func BarWidth(width int) BarOption { return func(s *bState) { s.reqWidth = width } } // BarQueueAfter puts this (being constructed) bar into the queue. // BarPriority will be inherited from the argument bar. // When argument bar completes or aborts queued bar replaces its place. func BarQueueAfter(bar *Bar) BarOption { return func(s *bState) { s.waitBar = bar } } // BarRemoveOnComplete removes both bar's filler and its decorators // on complete event. func BarRemoveOnComplete() BarOption { return func(s *bState) { s.rmOnComplete = true } } // BarFillerClearOnComplete clears bar's filler on complete event. // It's shortcut for BarFillerOnComplete(""). func BarFillerClearOnComplete() BarOption { return BarFillerOnComplete("") } // BarFillerOnComplete replaces bar's filler with message, on complete event. func BarFillerOnComplete(message string) BarOption { return BarFillerMiddleware(func(base BarFiller) BarFiller { return BarFillerFunc(func(w io.Writer, st decor.Statistics) error { if st.Completed { _, err := io.WriteString(w, message) return err } return base.Fill(w, st) }) }) } // BarFillerMiddleware provides a way to augment the underlying BarFiller. func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption { if middle == nil { return nil } return func(s *bState) { s.filler = middle(s.filler) } } // BarPriority sets bar's priority. Zero is highest priority, i.e. bar // will be on top. This option isn't effective with `BarQueueAfter` option. func BarPriority(priority int) BarOption { return func(s *bState) { s.priority = priority } } // BarExtender extends bar with arbitrary lines. Provided BarFiller will be // called at each render/flush cycle. Any lines written to the underlying // io.Writer will extend the bar either in above (rev = true) or below // (rev = false) direction. func BarExtender(filler BarFiller, rev bool) BarOption { if filler == nil { return nil } if f, ok := filler.(BarFillerFunc); ok && f == nil { return nil } fn := makeExtenderFunc(filler, rev) return func(s *bState) { s.extender = fn } } func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc { buf := new(bytes.Buffer) base := func(stat decor.Statistics, rows ...io.Reader) ([]io.Reader, error) { err := filler.Fill(buf, stat) if err != nil { buf.Reset() return rows, err } for { line, err := buf.ReadBytes('\n') if err != nil { buf.Reset() break } rows = append(rows, bytes.NewReader(line)) } return rows, err } if !rev { return base } return func(stat decor.Statistics, rows ...io.Reader) ([]io.Reader, error) { rows, err := base(stat, rows...) if err != nil { return rows, err } for left, right := 0, len(rows)-1; left < right; left, right = left+1, right-1 { rows[left], rows[right] = rows[right], rows[left] } return rows, err } } // BarFillerTrim removes leading and trailing space around the underlying BarFiller. func BarFillerTrim() BarOption { return func(s *bState) { s.trimSpace = true } } // BarNoPop disables bar pop out of container. Effective when // PopCompletedMode of container is enabled. func BarNoPop() BarOption { return func(s *bState) { s.noPop = true } } // BarOptional will return provided option only when cond is true. func BarOptional(option BarOption, cond bool) BarOption { if cond { return option } return nil } // BarOptOn will return provided option only when predicate evaluates to true. func BarOptOn(option BarOption, predicate func() bool) BarOption { if predicate() { return option } return nil } // BarFuncOptional will call option and return its value only when cond is true. func BarFuncOptional(option func() BarOption, cond bool) BarOption { if cond { return option() } return nil } // BarFuncOptOn will call option and return its value only when predicate evaluates to true. func BarFuncOptOn(option func() BarOption, predicate func() bool) BarOption { if predicate() { return option() } return nil } mpb-8.8.3/bar_test.go000066400000000000000000000156051466312757300144670ustar00rootroot00000000000000package mpb_test import ( "bytes" "context" "fmt" "io" "strings" "testing" "time" "unicode/utf8" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func TestBarCompleted(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) total := 80 bar := p.AddBar(int64(total)) if bar.Completed() { t.Error("expected bar not to complete") } bar.IncrBy(total) if !bar.Completed() { t.Error("expected bar to complete") } p.Wait() } func TestBarAborted(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) total := 80 bar := p.AddBar(int64(total)) if bar.Aborted() { t.Error("expected bar not to be aborted") } bar.Abort(false) if !bar.Aborted() { t.Error("expected bar to be aborted") } p.Wait() } func TestBarSetTotal(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) bar := p.AddBar(0) bar.SetTotal(0, false) if bar.Completed() { t.Error("expected bar not to complete") } bar.SetTotal(0, true) if !bar.Completed() { t.Error("expected bar to complete") } p.Wait() } func TestBarEnableTriggerCompleteZeroBar(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) bar := p.AddBar(0) // never complete bar if bar.Completed() { t.Error("expected bar not to complete") } // Calling bar.SetTotal(0, true) has same effect // but this one is more concise and intuitive bar.EnableTriggerComplete() if !bar.Completed() { t.Error("expected bar to complete") } p.Wait() } func TestBarEnableTriggerCompleteAndIncrementBefore(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) bar := p.AddBar(0) // never complete bar targetTotal := int64(80) for _, f := range []func(){ func() { bar.SetTotal(40, false) }, func() { bar.IncrBy(60) }, func() { bar.SetTotal(targetTotal, false) }, func() { bar.IncrBy(20) }, } { f() if bar.Completed() { t.Error("expected bar not to complete") } } bar.EnableTriggerComplete() if !bar.Completed() { t.Error("expected bar to complete") } if current := bar.Current(); current != targetTotal { t.Errorf("Expected current: %d, got: %d", targetTotal, current) } p.Wait() } func TestBarEnableTriggerCompleteAndIncrementAfter(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) bar := p.AddBar(0) // never complete bar targetTotal := int64(80) for _, f := range []func(){ func() { bar.SetTotal(40, false) }, func() { bar.IncrBy(60) }, func() { bar.SetTotal(targetTotal, false) }, func() { bar.EnableTriggerComplete() }, // disables any next SetTotal func() { bar.SetTotal(100, true) }, // nop } { f() if bar.Completed() { t.Error("expected bar not to complete") } } bar.IncrBy(20) if !bar.Completed() { t.Error("expected bar to complete") } if current := bar.Current(); current != targetTotal { t.Errorf("Expected current: %d, got: %d", targetTotal, current) } p.Wait() } func TestBarID(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) total := 100 wantID := 11 bar := p.AddBar(int64(total), mpb.BarID(wantID)) gotID := bar.ID() if gotID != wantID { t.Errorf("Expected bar id: %d, got %d", wantID, gotID) } bar.IncrBy(total) p.Wait() } func TestBarSetRefill(t *testing.T) { var buf bytes.Buffer p := mpb.New( mpb.WithWidth(100), mpb.WithOutput(&buf), mpb.WithAutoRefresh(), ) total := 100 till := 30 refiller := "+" bar := p.New(int64(total), mpb.BarStyle().Refiller(refiller), mpb.BarFillerTrim()) bar.IncrBy(till) bar.SetRefill(int64(till)) bar.IncrBy(total - till) p.Wait() wantBar := fmt.Sprintf("[%s%s]", strings.Repeat(refiller, till-1), strings.Repeat("=", total-till-1), ) got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0]) if !strings.Contains(got, wantBar) { t.Errorf("Want bar: %q, got bar: %q", wantBar, got) } } func TestBarHas100PercentWithBarRemoveOnComplete(t *testing.T) { var buf bytes.Buffer p := mpb.New( mpb.WithWidth(80), mpb.WithOutput(&buf), mpb.WithAutoRefresh(), ) total := 50 bar := p.AddBar(int64(total), mpb.BarRemoveOnComplete(), mpb.AppendDecorators(decor.Percentage()), ) bar.IncrBy(total) p.Wait() hundred := "100 %" if !bytes.Contains(buf.Bytes(), []byte(hundred)) { t.Errorf("Bar's buffer does not contain: %q", hundred) } } func TestBarStyle(t *testing.T) { var buf bytes.Buffer customFormat := "╢▌▌░╟" runes := []rune(customFormat) total := 80 p := mpb.New( mpb.WithWidth(80), mpb.WithOutput(&buf), mpb.WithAutoRefresh(), ) bs := mpb.BarStyle() bs = bs.Lbound(string(runes[0])) bs = bs.Filler(string(runes[1])) bs = bs.Tip(string(runes[2])) bs = bs.Padding(string(runes[3])) bs = bs.Rbound(string(runes[4])) bar := p.New(int64(total), bs, mpb.BarFillerTrim()) bar.IncrBy(total) p.Wait() wantBar := fmt.Sprintf("%s%s%s%s", string(runes[0]), strings.Repeat(string(runes[1]), total-3), string(runes[2]), string(runes[4]), ) got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0]) if !strings.Contains(got, wantBar) { t.Errorf("Want bar: %q:%d, got bar: %q:%d", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got)) } } func TestDecorStatisticsAvailableWidth(t *testing.T) { ch := make(chan int, 2) td1 := func(s decor.Statistics) string { ch <- s.AvailableWidth return strings.Repeat("0", 20) } td2 := func(s decor.Statistics) string { ch <- s.AvailableWidth return "" } ctx, cancel := context.WithCancel(context.Background()) refresh := make(chan interface{}) p := mpb.NewWithContext(ctx, mpb.WithWidth(100), mpb.WithManualRefresh(refresh), mpb.WithOutput(io.Discard), ) _ = p.AddBar(0, mpb.BarFillerTrim(), mpb.PrependDecorators( decor.Name(strings.Repeat("0", 20)), decor.Meta( decor.Any(td1), func(s string) string { return "\x1b[31;1m" + s + "\x1b[0m" }, ), ), mpb.AppendDecorators( decor.Name(strings.Repeat("0", 20)), decor.Any(td2), ), ) refresh <- time.Now() go func() { time.Sleep(10 * time.Millisecond) cancel() }() p.Wait() if availableWidth := <-ch; availableWidth != 80 { t.Errorf("expected AvailableWidth %d got %d", 80, availableWidth) } if availableWidth := <-ch; availableWidth != 40 { t.Errorf("expected AvailableWidth %d got %d", 40, availableWidth) } } func TestBarQueueAfterBar(t *testing.T) { shutdown := make(chan interface{}) ctx, cancel := context.WithCancel(context.Background()) p := mpb.NewWithContext(ctx, mpb.WithOutput(io.Discard), mpb.WithAutoRefresh(), mpb.WithShutdownNotifier(shutdown), ) a := p.AddBar(100) b := p.AddBar(100, mpb.BarQueueAfter(a)) identity := map[*mpb.Bar]string{ a: "a", b: "b", } a.IncrBy(100) a.Wait() cancel() bars := (<-shutdown).([]*mpb.Bar) if l := len(bars); l != 1 { t.Errorf("Expected len of bars: %d, got: %d", 1, l) } p.Wait() if bars[0] != b { t.Errorf("Expected bars[0] == b, got: %s", identity[bars[0]]) } } mpb-8.8.3/barbench_test.go000066400000000000000000000033311466312757300154600ustar00rootroot00000000000000package mpb_test import ( "io" "testing" "github.com/vbauerster/mpb/v8" ) const total = 1000 func BenchmarkNopStyleB1(b *testing.B) { bench(b, mpb.NopStyle(), false, 1) } func BenchmarkNopStyleWithAutoRefreshB1(b *testing.B) { bench(b, mpb.NopStyle(), true, 1) } func BenchmarkNopStylesB2(b *testing.B) { bench(b, mpb.NopStyle(), false, 2) } func BenchmarkNopStylesWithAutoRefreshB2(b *testing.B) { bench(b, mpb.NopStyle(), true, 2) } func BenchmarkNopStylesB3(b *testing.B) { bench(b, mpb.NopStyle(), false, 3) } func BenchmarkNopStylesWithAutoRefreshB3(b *testing.B) { bench(b, mpb.NopStyle(), true, 3) } func BenchmarkBarStyleB1(b *testing.B) { bench(b, mpb.BarStyle(), false, 1) } func BenchmarkBarStyleWithAutoRefreshB1(b *testing.B) { bench(b, mpb.BarStyle(), true, 1) } func BenchmarkBarStylesB2(b *testing.B) { bench(b, mpb.BarStyle(), false, 2) } func BenchmarkBarStylesWithAutoRefreshB2(b *testing.B) { bench(b, mpb.BarStyle(), true, 2) } func BenchmarkBarStylesB3(b *testing.B) { bench(b, mpb.BarStyle(), false, 3) } func BenchmarkBarStylesWithAutoRefreshB3(b *testing.B) { bench(b, mpb.BarStyle(), true, 3) } func bench(b *testing.B, builder mpb.BarFillerBuilder, autoRefresh bool, n int) { p := mpb.New( mpb.WithWidth(100), mpb.WithOutput(io.Discard), mpb.ContainerOptional(mpb.WithAutoRefresh(), autoRefresh), ) defer p.Wait() b.ResetTimer() for i := 0; i < b.N; i++ { var bars []*mpb.Bar for j := 0; j < n; j++ { bars = append(bars, p.New(total, builder)) switch j { case n - 1: complete(bars[j]) default: go complete(bars[j]) } } for _, bar := range bars { bar.Wait() } } } func complete(bar *mpb.Bar) { for i := 0; i < total; i++ { bar.Increment() } } mpb-8.8.3/container_option.go000066400000000000000000000072221466312757300162320ustar00rootroot00000000000000package mpb import ( "io" "sync" "time" ) // ContainerOption is a func option to alter default behavior of a bar // container. Container term refers to a Progress struct which can // hold one or more Bars. type ContainerOption func(*pState) // WithWaitGroup provides means to have a single joint point. If // *sync.WaitGroup is provided, you can safely call just p.Wait() // without calling Wait() on provided *sync.WaitGroup. Makes sense // when there are more than one bar to render. func WithWaitGroup(wg *sync.WaitGroup) ContainerOption { return func(s *pState) { s.uwg = wg } } // WithWidth sets container width. If not set it defaults to terminal // width. A bar added to the container will inherit its width, unless // overridden by `func BarWidth(int) BarOption`. func WithWidth(width int) ContainerOption { return func(s *pState) { s.reqWidth = width } } // WithRefreshRate overrides default 150ms refresh rate. func WithRefreshRate(d time.Duration) ContainerOption { return func(s *pState) { s.refreshRate = d } } // WithManualRefresh disables internal auto refresh time.Ticker. // Refresh will occur upon receive value from provided ch. func WithManualRefresh(ch <-chan interface{}) ContainerOption { return func(s *pState) { s.manualRC = ch } } // WithRenderDelay delays rendering. By default rendering starts as // soon as bar is added, with this option it's possible to delay // rendering process by keeping provided chan unclosed. In other words // rendering will start as soon as provided chan is closed. func WithRenderDelay(ch <-chan struct{}) ContainerOption { return func(s *pState) { s.delayRC = ch } } // WithShutdownNotifier value of type `[]*mpb.Bar` will be send into provided // channel upon container shutdown. func WithShutdownNotifier(ch chan<- interface{}) ContainerOption { return func(s *pState) { s.shutdownNotifier = ch } } // WithOutput overrides default os.Stdout output. If underlying io.Writer // is not a terminal then auto refresh is disabled unless WithAutoRefresh // option is set. func WithOutput(w io.Writer) ContainerOption { if w == nil { w = io.Discard } return func(s *pState) { s.output = w } } // WithDebugOutput sets debug output. func WithDebugOutput(w io.Writer) ContainerOption { if w == nil { w = io.Discard } return func(s *pState) { s.debugOut = w } } // WithAutoRefresh force auto refresh regardless of what output is set to. // Applicable only if not WithManualRefresh set. func WithAutoRefresh() ContainerOption { return func(s *pState) { s.autoRefresh = true } } // PopCompletedMode pop completed bars out of progress container. // In this mode completed bars get moved to the top and stop // participating in rendering cycle. func PopCompletedMode() ContainerOption { return func(s *pState) { s.popCompleted = true } } // ContainerOptional will return provided option only when cond is true. func ContainerOptional(option ContainerOption, cond bool) ContainerOption { if cond { return option } return nil } // ContainerOptOn will return provided option only when predicate evaluates to true. func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption { if predicate() { return option } return nil } // ContainerFuncOptional will call option and return its value only when cond is true. func ContainerFuncOptional(option func() ContainerOption, cond bool) ContainerOption { if cond { return option() } return nil } // ContainerFuncOptOn will call option and return its value only when predicate evaluates to true. func ContainerFuncOptOn(option func() ContainerOption, predicate func() bool) ContainerOption { if predicate() { return option() } return nil } mpb-8.8.3/cwriter/000077500000000000000000000000001466312757300140055ustar00rootroot00000000000000mpb-8.8.3/cwriter/cuuAndEd_construction_bench_test.go000066400000000000000000000015301466312757300230330ustar00rootroot00000000000000package cwriter import ( "bytes" "fmt" "io" "strconv" "testing" ) var ( out = io.Discard lines = 99 ) func BenchmarkWithFprintf(b *testing.B) { verb := fmt.Sprintf("%s%%d%s", escOpen, cuuAndEd) b.ResetTimer() for i := 0; i < b.N; i++ { fmt.Fprintf(out, verb, lines) } } func BenchmarkWithJoin(b *testing.B) { bCuuAndEd := [][]byte{[]byte(escOpen), []byte(cuuAndEd)} for i := 0; i < b.N; i++ { _, _ = out.Write(bytes.Join(bCuuAndEd, []byte(strconv.Itoa(lines)))) } } func BenchmarkWithAppend(b *testing.B) { escOpen := []byte(escOpen) cuuAndEd := []byte(cuuAndEd) for i := 0; i < b.N; i++ { _, _ = out.Write(append(strconv.AppendInt(escOpen, int64(lines), 10), cuuAndEd...)) } } func BenchmarkWithCurrentImpl(b *testing.B) { w := New(out) b.ResetTimer() for i := 0; i < b.N; i++ { _ = w.ew.ansiCuuAndEd(out, lines) } } mpb-8.8.3/cwriter/doc.go000066400000000000000000000001321466312757300150750ustar00rootroot00000000000000// Package cwriter is a console writer abstraction for the underlying OS. package cwriter mpb-8.8.3/cwriter/util_bsd.go000066400000000000000000000002301466312757300161340ustar00rootroot00000000000000//go:build darwin || dragonfly || freebsd || netbsd || openbsd package cwriter import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TIOCGETA mpb-8.8.3/cwriter/util_linux.go000066400000000000000000000001571466312757300165330ustar00rootroot00000000000000//go:build aix || linux package cwriter import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETS mpb-8.8.3/cwriter/util_solaris.go000066400000000000000000000001521466312757300170430ustar00rootroot00000000000000//go:build solaris package cwriter import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETA mpb-8.8.3/cwriter/util_zos.go000066400000000000000000000001461466312757300162050ustar00rootroot00000000000000//go:build zos package cwriter import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETS mpb-8.8.3/cwriter/writer.go000066400000000000000000000023011466312757300156440ustar00rootroot00000000000000package cwriter import ( "bytes" "errors" "io" "os" "strconv" ) // https://github.com/dylanaraps/pure-sh-bible#cursor-movement const ( escOpen = "\x1b[" cuuAndEd = "A\x1b[J" ) // ErrNotTTY not a TeleTYpewriter error. var ErrNotTTY = errors.New("not a terminal") // New returns a new Writer with defaults. func New(out io.Writer) *Writer { w := &Writer{ Buffer: new(bytes.Buffer), out: out, termSize: func(_ int) (int, int, error) { return -1, -1, ErrNotTTY }, } if f, ok := out.(*os.File); ok { w.fd = int(f.Fd()) if IsTerminal(w.fd) { w.terminal = true w.termSize = func(fd int) (int, int, error) { return GetSize(fd) } } } bb := make([]byte, 16) w.ew = escWriter(bb[:copy(bb, []byte(escOpen))]) return w } // IsTerminal tells whether underlying io.Writer is terminal. func (w *Writer) IsTerminal() bool { return w.terminal } // GetTermSize returns WxH of underlying terminal. func (w *Writer) GetTermSize() (width, height int, err error) { return w.termSize(w.fd) } type escWriter []byte func (b escWriter) ansiCuuAndEd(out io.Writer, n int) error { b = strconv.AppendInt(b, int64(n), 10) _, err := out.Write(append(b, []byte(cuuAndEd)...)) return err } mpb-8.8.3/cwriter/writer_posix.go000066400000000000000000000021671466312757300171000ustar00rootroot00000000000000//go:build !windows package cwriter import ( "bytes" "io" "golang.org/x/sys/unix" ) // Writer is a buffered terminal writer, which moves cursor N lines up // on each flush except the first one, where N is a number of lines of // a previous flush. type Writer struct { *bytes.Buffer out io.Writer ew escWriter fd int terminal bool termSize func(int) (int, int, error) } // Flush flushes the underlying buffer. // It's caller's responsibility to pass correct number of lines. func (w *Writer) Flush(lines int) error { _, err := w.WriteTo(w.out) // some terminals interpret 'cursor up 0' as 'cursor up 1' if err == nil && lines > 0 { err = w.ew.ansiCuuAndEd(w, lines) } return err } // GetSize returns the dimensions of the given terminal. func GetSize(fd int) (width, height int, err error) { ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) if err != nil { return -1, -1, err } return int(ws.Col), int(ws.Row), nil } // IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) return err == nil } mpb-8.8.3/cwriter/writer_windows.go000066400000000000000000000052501466312757300174240ustar00rootroot00000000000000//go:build windows package cwriter import ( "bytes" "io" "unsafe" "golang.org/x/sys/windows" ) var kernel32 = windows.NewLazySystemDLL("kernel32.dll") var ( procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") ) // Writer is a buffered terminal writer, which moves cursor N lines up // on each flush except the first one, where N is a number of lines of // a previous flush. type Writer struct { *bytes.Buffer out io.Writer ew escWriter lines int fd int terminal bool termSize func(int) (int, int, error) } // Flush flushes the underlying buffer. // It's caller's responsibility to pass correct number of lines. func (w *Writer) Flush(lines int) error { if w.lines > 0 { err := w.clearLines(w.lines) if err != nil { return err } } w.lines = lines _, err := w.WriteTo(w.out) return err } func (w *Writer) clearLines(n int) error { if !w.terminal { // hope it's cygwin or similar return w.ew.ansiCuuAndEd(w.out, n) } var info windows.ConsoleScreenBufferInfo if err := windows.GetConsoleScreenBufferInfo(windows.Handle(w.fd), &info); err != nil { return err } info.CursorPosition.Y -= int16(n) if info.CursorPosition.Y < 0 { info.CursorPosition.Y = 0 } _, _, _ = procSetConsoleCursorPosition.Call( uintptr(w.fd), uintptr(uint32(uint16(info.CursorPosition.Y))<<16|uint32(uint16(info.CursorPosition.X))), ) // clear the lines cursor := &windows.Coord{ X: info.Window.Left, Y: info.CursorPosition.Y, } count := uint32(info.Size.X) * uint32(n) _, _, _ = procFillConsoleOutputCharacter.Call( uintptr(w.fd), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(cursor)), uintptr(unsafe.Pointer(new(uint32))), ) return nil } // GetSize returns the visible dimensions of the given terminal. // These dimensions don't include any scrollback buffer height. func GetSize(fd int) (width, height int, err error) { var info windows.ConsoleScreenBufferInfo if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { return 0, 0, err } // terminal.GetSize from crypto/ssh adds "+ 1" to both width and height: // https://go.googlesource.com/crypto/+/refs/heads/release-branch.go1.14/ssh/terminal/util_windows.go#75 // but looks like this is a root cause of issue #66, so removing both "+ 1" have fixed it. return int(info.Window.Right - info.Window.Left), int(info.Window.Bottom - info.Window.Top), nil } // IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { var st uint32 err := windows.GetConsoleMode(windows.Handle(fd), &st) return err == nil } mpb-8.8.3/decor/000077500000000000000000000000001466312757300134225ustar00rootroot00000000000000mpb-8.8.3/decor/any.go000066400000000000000000000005371466312757300145450ustar00rootroot00000000000000package decor var _ Decorator = any{} // Any decorator. // Converts DecorFunc into Decorator. // // `fn` DecorFunc callback // `wcc` optional WC config func Any(fn DecorFunc, wcc ...WC) Decorator { return any{initWC(wcc...), fn} } type any struct { WC fn DecorFunc } func (d any) Decor(s Statistics) (string, int) { return d.Format(d.fn(s)) } mpb-8.8.3/decor/counters.go000066400000000000000000000153671466312757300156270ustar00rootroot00000000000000package decor import ( "fmt" ) // CountersNoUnit is a wrapper around Counters with no unit param. func CountersNoUnit(pairFmt string, wcc ...WC) Decorator { return Counters(0, pairFmt, wcc...) } // CountersKibiByte is a wrapper around Counters with predefined unit // as SizeB1024(0). func CountersKibiByte(pairFmt string, wcc ...WC) Decorator { return Counters(SizeB1024(0), pairFmt, wcc...) } // CountersKiloByte is a wrapper around Counters with predefined unit // as SizeB1000(0). func CountersKiloByte(pairFmt string, wcc ...WC) Decorator { return Counters(SizeB1000(0), pairFmt, wcc...) } // Counters decorator with dynamic unit measure adjustment. // // `unit` one of [0|SizeB1024(0)|SizeB1000(0)] // // `pairFmt` printf compatible verbs for current and total // // `wcc` optional WC config // // pairFmt example if unit=SizeB1000(0): // // pairFmt="%d / %d" output: "1MB / 12MB" // pairFmt="% d / % d" output: "1 MB / 12 MB" // pairFmt="%.1f / %.1f" output: "1.0MB / 12.0MB" // pairFmt="% .1f / % .1f" output: "1.0 MB / 12.0 MB" // pairFmt="%f / %f" output: "1.000000MB / 12.000000MB" // pairFmt="% f / % f" output: "1.000000 MB / 12.000000 MB" func Counters(unit interface{}, pairFmt string, wcc ...WC) Decorator { producer := func() DecorFunc { switch unit.(type) { case SizeB1024: if pairFmt == "" { pairFmt = "% d / % d" } return func(s Statistics) string { return fmt.Sprintf(pairFmt, SizeB1024(s.Current), SizeB1024(s.Total)) } case SizeB1000: if pairFmt == "" { pairFmt = "% d / % d" } return func(s Statistics) string { return fmt.Sprintf(pairFmt, SizeB1000(s.Current), SizeB1000(s.Total)) } default: if pairFmt == "" { pairFmt = "%d / %d" } return func(s Statistics) string { return fmt.Sprintf(pairFmt, s.Current, s.Total) } } } return Any(producer(), wcc...) } // TotalNoUnit is a wrapper around Total with no unit param. func TotalNoUnit(format string, wcc ...WC) Decorator { return Total(0, format, wcc...) } // TotalKibiByte is a wrapper around Total with predefined unit // as SizeB1024(0). func TotalKibiByte(format string, wcc ...WC) Decorator { return Total(SizeB1024(0), format, wcc...) } // TotalKiloByte is a wrapper around Total with predefined unit // as SizeB1000(0). func TotalKiloByte(format string, wcc ...WC) Decorator { return Total(SizeB1000(0), format, wcc...) } // Total decorator with dynamic unit measure adjustment. // // `unit` one of [0|SizeB1024(0)|SizeB1000(0)] // // `format` printf compatible verb for Total // // `wcc` optional WC config // // format example if unit=SizeB1024(0): // // format="%d" output: "12MiB" // format="% d" output: "12 MiB" // format="%.1f" output: "12.0MiB" // format="% .1f" output: "12.0 MiB" // format="%f" output: "12.000000MiB" // format="% f" output: "12.000000 MiB" func Total(unit interface{}, format string, wcc ...WC) Decorator { producer := func() DecorFunc { switch unit.(type) { case SizeB1024: if format == "" { format = "% d" } return func(s Statistics) string { return fmt.Sprintf(format, SizeB1024(s.Total)) } case SizeB1000: if format == "" { format = "% d" } return func(s Statistics) string { return fmt.Sprintf(format, SizeB1000(s.Total)) } default: if format == "" { format = "%d" } return func(s Statistics) string { return fmt.Sprintf(format, s.Total) } } } return Any(producer(), wcc...) } // CurrentNoUnit is a wrapper around Current with no unit param. func CurrentNoUnit(format string, wcc ...WC) Decorator { return Current(0, format, wcc...) } // CurrentKibiByte is a wrapper around Current with predefined unit // as SizeB1024(0). func CurrentKibiByte(format string, wcc ...WC) Decorator { return Current(SizeB1024(0), format, wcc...) } // CurrentKiloByte is a wrapper around Current with predefined unit // as SizeB1000(0). func CurrentKiloByte(format string, wcc ...WC) Decorator { return Current(SizeB1000(0), format, wcc...) } // Current decorator with dynamic unit measure adjustment. // // `unit` one of [0|SizeB1024(0)|SizeB1000(0)] // // `format` printf compatible verb for Current // // `wcc` optional WC config // // format example if unit=SizeB1024(0): // // format="%d" output: "12MiB" // format="% d" output: "12 MiB" // format="%.1f" output: "12.0MiB" // format="% .1f" output: "12.0 MiB" // format="%f" output: "12.000000MiB" // format="% f" output: "12.000000 MiB" func Current(unit interface{}, format string, wcc ...WC) Decorator { producer := func() DecorFunc { switch unit.(type) { case SizeB1024: if format == "" { format = "% d" } return func(s Statistics) string { return fmt.Sprintf(format, SizeB1024(s.Current)) } case SizeB1000: if format == "" { format = "% d" } return func(s Statistics) string { return fmt.Sprintf(format, SizeB1000(s.Current)) } default: if format == "" { format = "%d" } return func(s Statistics) string { return fmt.Sprintf(format, s.Current) } } } return Any(producer(), wcc...) } // InvertedCurrentNoUnit is a wrapper around InvertedCurrent with no unit param. func InvertedCurrentNoUnit(format string, wcc ...WC) Decorator { return InvertedCurrent(0, format, wcc...) } // InvertedCurrentKibiByte is a wrapper around InvertedCurrent with predefined unit // as SizeB1024(0). func InvertedCurrentKibiByte(format string, wcc ...WC) Decorator { return InvertedCurrent(SizeB1024(0), format, wcc...) } // InvertedCurrentKiloByte is a wrapper around InvertedCurrent with predefined unit // as SizeB1000(0). func InvertedCurrentKiloByte(format string, wcc ...WC) Decorator { return InvertedCurrent(SizeB1000(0), format, wcc...) } // InvertedCurrent decorator with dynamic unit measure adjustment. // // `unit` one of [0|SizeB1024(0)|SizeB1000(0)] // // `format` printf compatible verb for InvertedCurrent // // `wcc` optional WC config // // format example if unit=SizeB1024(0): // // format="%d" output: "12MiB" // format="% d" output: "12 MiB" // format="%.1f" output: "12.0MiB" // format="% .1f" output: "12.0 MiB" // format="%f" output: "12.000000MiB" // format="% f" output: "12.000000 MiB" func InvertedCurrent(unit interface{}, format string, wcc ...WC) Decorator { producer := func() DecorFunc { switch unit.(type) { case SizeB1024: if format == "" { format = "% d" } return func(s Statistics) string { return fmt.Sprintf(format, SizeB1024(s.Total-s.Current)) } case SizeB1000: if format == "" { format = "% d" } return func(s Statistics) string { return fmt.Sprintf(format, SizeB1000(s.Total-s.Current)) } default: if format == "" { format = "%d" } return func(s Statistics) string { return fmt.Sprintf(format, s.Total-s.Current) } } } return Any(producer(), wcc...) } mpb-8.8.3/decor/decorator.go000066400000000000000000000116201466312757300157330ustar00rootroot00000000000000package decor import ( "fmt" "time" "github.com/mattn/go-runewidth" ) const ( // DindentRight sets indentation from right to left. // // |foo |b | DindentRight is set // | foo| b| DindentRight is not set DindentRight = 1 << iota // DextraSpace bit adds extra indentation space. DextraSpace // DSyncWidth bit enables same column width synchronization. // Effective with multiple bars only. DSyncWidth // DSyncWidthR is shortcut for DSyncWidth|DindentRight DSyncWidthR = DSyncWidth | DindentRight // DSyncSpace is shortcut for DSyncWidth|DextraSpace DSyncSpace = DSyncWidth | DextraSpace // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DindentRight DSyncSpaceR = DSyncWidth | DextraSpace | DindentRight ) // TimeStyle enum. type TimeStyle int // TimeStyle kinds. const ( ET_STYLE_GO TimeStyle = iota ET_STYLE_HHMMSS ET_STYLE_HHMM ET_STYLE_MMSS ) // Statistics contains fields which are necessary for implementing // `decor.Decorator` and `mpb.BarFiller` interfaces. type Statistics struct { AvailableWidth int // calculated width initially equal to terminal width RequestedWidth int // width set by `mpb.WithWidth` ID int Total int64 Current int64 Refill int64 Completed bool Aborted bool } // Decorator interface. // Most of the time there is no need to implement this interface // manually, as decor package already provides a wide range of decorators // which implement this interface. If however built-in decorators don't // meet your needs, you're free to implement your own one by implementing // this particular interface. The easy way to go is to convert a // `DecorFunc` into a `Decorator` interface by using provided // `func Any(DecorFunc, ...WC) Decorator`. type Decorator interface { Synchronizer Formatter Decor(Statistics) (str string, viewWidth int) } // DecorFunc func type. // To be used with `func Any(DecorFunc, ...WC) Decorator`. type DecorFunc func(Statistics) string // Synchronizer interface. // All decorators implement this interface implicitly. Its Sync // method exposes width sync channel, if DSyncWidth bit is set. type Synchronizer interface { Sync() (chan int, bool) } // Formatter interface. // Format method needs to be called from within Decorator.Decor method // in order to format string according to decor.WC settings. // No need to implement manually as long as decor.WC is embedded. type Formatter interface { Format(string) (_ string, width int) } // Wrapper interface. // If you're implementing custom Decorator by wrapping a built-in one, // it is necessary to implement this interface to retain functionality // of built-in Decorator. type Wrapper interface { Unwrap() Decorator } // EwmaDecorator interface. // EWMA based decorators should implement this one. type EwmaDecorator interface { EwmaUpdate(int64, time.Duration) } // AverageDecorator interface. // Average decorators should implement this interface to provide start // time adjustment facility, for resume-able tasks. type AverageDecorator interface { AverageAdjust(time.Time) } // ShutdownListener interface. // If decorator needs to be notified once upon bar shutdown event, so // this is the right interface to implement. type ShutdownListener interface { OnShutdown() } // Global convenience instances of WC with sync width bit set. // To be used with multiple bars only, i.e. not effective for single bar usage. var ( WCSyncWidth = WC{C: DSyncWidth} WCSyncWidthR = WC{C: DSyncWidthR} WCSyncSpace = WC{C: DSyncSpace} WCSyncSpaceR = WC{C: DSyncSpaceR} ) // WC is a struct with two public fields W and C, both of int type. // W represents width and C represents bit set of width related config. // A decorator should embed WC, to enable width synchronization. type WC struct { W int C int fill func(s string, w int) string wsync chan int } // Format should be called by any Decorator implementation. // Returns formatted string and its view (visual) width. func (wc WC) Format(str string) (string, int) { width := runewidth.StringWidth(str) if wc.W > width { width = wc.W } else if (wc.C & DextraSpace) != 0 { width++ } if (wc.C & DSyncWidth) != 0 { wc.wsync <- width width = <-wc.wsync } return wc.fill(str, width), width } // Init initializes width related config. func (wc *WC) Init() WC { if (wc.C & DindentRight) != 0 { wc.fill = runewidth.FillRight } else { wc.fill = runewidth.FillLeft } if (wc.C & DSyncWidth) != 0 { // it's deliberate choice to override wsync on each Init() call, // this way globals like WCSyncSpace can be reused wc.wsync = make(chan int) } return *wc } // Sync is implementation of Synchronizer interface. func (wc WC) Sync() (chan int, bool) { if (wc.C&DSyncWidth) != 0 && wc.wsync == nil { panic(fmt.Sprintf("%T is not initialized", wc)) } return wc.wsync, (wc.C & DSyncWidth) != 0 } func initWC(wcc ...WC) WC { var wc WC for _, nwc := range wcc { wc = nwc } return wc.Init() } mpb-8.8.3/decor/doc.go000066400000000000000000000012251466312757300145160ustar00rootroot00000000000000// Package decor provides common decorators for "github.com/vbauerster/mpb/v8" module. // // Some decorators returned by this package might have a closure state. It is ok to use // decorators concurrently, unless you share the same decorator among multiple // *mpb.Bar instances. To avoid data races, create new decorator per *mpb.Bar instance. // // Don't: // // p := mpb.New() // name := decor.Name("bar") // p.AddBar(100, mpb.AppendDecorators(name)) // p.AddBar(100, mpb.AppendDecorators(name)) // // Do: // // p := mpb.New() // p.AddBar(100, mpb.AppendDecorators(decor.Name("bar1"))) // p.AddBar(100, mpb.AppendDecorators(decor.Name("bar2"))) package decor mpb-8.8.3/decor/elapsed.go000066400000000000000000000013661466312757300153740ustar00rootroot00000000000000package decor import ( "time" ) // Elapsed decorator. It's wrapper of NewElapsed. // // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] // // `wcc` optional WC config func Elapsed(style TimeStyle, wcc ...WC) Decorator { return NewElapsed(style, time.Now(), wcc...) } // NewElapsed returns elapsed time decorator. // // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] // // `start` start time // // `wcc` optional WC config func NewElapsed(style TimeStyle, start time.Time, wcc ...WC) Decorator { var msg string producer := chooseTimeProducer(style) fn := func(s Statistics) string { if !s.Completed && !s.Aborted { msg = producer(time.Since(start)) } return msg } return Any(fn, wcc...) } mpb-8.8.3/decor/eta.go000066400000000000000000000145221466312757300145260ustar00rootroot00000000000000package decor import ( "fmt" "math" "time" "github.com/VividCortex/ewma" ) var ( _ Decorator = (*movingAverageETA)(nil) _ EwmaDecorator = (*movingAverageETA)(nil) _ Decorator = (*averageETA)(nil) _ AverageDecorator = (*averageETA)(nil) ) // TimeNormalizer interface. Implementors could be passed into // MovingAverageETA, in order to affect i.e. normalize its output. type TimeNormalizer interface { Normalize(time.Duration) time.Duration } // TimeNormalizerFunc is function type adapter to convert function // into TimeNormalizer. type TimeNormalizerFunc func(time.Duration) time.Duration func (f TimeNormalizerFunc) Normalize(src time.Duration) time.Duration { return f(src) } // EwmaETA exponential-weighted-moving-average based ETA decorator. For this // decorator to work correctly you have to measure each iteration's duration // and pass it to one of the (*Bar).EwmaIncr... family methods. func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator { return EwmaNormalizedETA(style, age, nil, wcc...) } // EwmaNormalizedETA same as EwmaETA but with TimeNormalizer option. func EwmaNormalizedETA(style TimeStyle, age float64, normalizer TimeNormalizer, wcc ...WC) Decorator { var average ewma.MovingAverage if age == 0 { average = ewma.NewMovingAverage() } else { average = ewma.NewMovingAverage(age) } return MovingAverageETA(style, average, normalizer, wcc...) } // MovingAverageETA decorator relies on MovingAverage implementation to calculate its average. // // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] // // `average` implementation of MovingAverage interface // // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer] // // `wcc` optional WC config func MovingAverageETA(style TimeStyle, average ewma.MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator { if average == nil { average = NewMedian() } d := &movingAverageETA{ WC: initWC(wcc...), producer: chooseTimeProducer(style), average: average, normalizer: normalizer, } return d } type movingAverageETA struct { WC producer func(time.Duration) string average ewma.MovingAverage normalizer TimeNormalizer zDur time.Duration } func (d *movingAverageETA) Decor(s Statistics) (string, int) { v := math.Round(d.average.Value()) remaining := time.Duration((s.Total - s.Current) * int64(v)) if d.normalizer != nil { remaining = d.normalizer.Normalize(remaining) } return d.Format(d.producer(remaining)) } func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) { if n <= 0 { d.zDur += dur return } durPerItem := float64(d.zDur+dur) / float64(n) if math.IsInf(durPerItem, 0) || math.IsNaN(durPerItem) { d.zDur += dur return } d.zDur = 0 d.average.Add(durPerItem) } // AverageETA decorator. It's wrapper of NewAverageETA. // // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] // // `wcc` optional WC config func AverageETA(style TimeStyle, wcc ...WC) Decorator { return NewAverageETA(style, time.Now(), nil, wcc...) } // NewAverageETA decorator with user provided start time. // // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS] // // `start` start time // // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer] // // `wcc` optional WC config func NewAverageETA(style TimeStyle, start time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator { d := &averageETA{ WC: initWC(wcc...), start: start, normalizer: normalizer, producer: chooseTimeProducer(style), } return d } type averageETA struct { WC start time.Time normalizer TimeNormalizer producer func(time.Duration) string } func (d *averageETA) Decor(s Statistics) (string, int) { var remaining time.Duration if s.Current != 0 { durPerItem := float64(time.Since(d.start)) / float64(s.Current) durPerItem = math.Round(durPerItem) remaining = time.Duration((s.Total - s.Current) * int64(durPerItem)) if d.normalizer != nil { remaining = d.normalizer.Normalize(remaining) } } return d.Format(d.producer(remaining)) } func (d *averageETA) AverageAdjust(start time.Time) { d.start = start } // MaxTolerateTimeNormalizer returns implementation of TimeNormalizer. func MaxTolerateTimeNormalizer(maxTolerate time.Duration) TimeNormalizer { var normalized time.Duration var lastCall time.Time return TimeNormalizerFunc(func(remaining time.Duration) time.Duration { if diff := normalized - remaining; diff <= 0 || diff > maxTolerate || remaining < time.Minute { normalized = remaining lastCall = time.Now() return remaining } normalized -= time.Since(lastCall) lastCall = time.Now() if normalized > 0 { return normalized } return remaining }) } // FixedIntervalTimeNormalizer returns implementation of TimeNormalizer. func FixedIntervalTimeNormalizer(updInterval int) TimeNormalizer { var normalized time.Duration var lastCall time.Time var count int return TimeNormalizerFunc(func(remaining time.Duration) time.Duration { if count == 0 || remaining < time.Minute { count = updInterval normalized = remaining lastCall = time.Now() return remaining } count-- normalized -= time.Since(lastCall) lastCall = time.Now() if normalized > 0 { return normalized } return remaining }) } func chooseTimeProducer(style TimeStyle) func(time.Duration) string { switch style { case ET_STYLE_HHMMSS: return func(remaining time.Duration) string { hours := int64(remaining/time.Hour) % 60 minutes := int64(remaining/time.Minute) % 60 seconds := int64(remaining/time.Second) % 60 return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) } case ET_STYLE_HHMM: return func(remaining time.Duration) string { hours := int64(remaining/time.Hour) % 60 minutes := int64(remaining/time.Minute) % 60 return fmt.Sprintf("%02d:%02d", hours, minutes) } case ET_STYLE_MMSS: return func(remaining time.Duration) string { hours := int64(remaining/time.Hour) % 60 minutes := int64(remaining/time.Minute) % 60 seconds := int64(remaining/time.Second) % 60 if hours > 0 { return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) } return fmt.Sprintf("%02d:%02d", minutes, seconds) } default: return func(remaining time.Duration) string { return remaining.Truncate(time.Second).String() } } } mpb-8.8.3/decor/meta.go000066400000000000000000000013651466312757300147040ustar00rootroot00000000000000package decor var ( _ Decorator = metaWrapper{} _ Wrapper = metaWrapper{} ) // Meta wrap decorator. // Provided fn is supposed to wrap output of given decorator // with meta information like ANSI escape codes for example. // Primary usage intention is to set SGR display attributes. // // `decorator` Decorator to wrap // `fn` func to apply meta information func Meta(decorator Decorator, fn func(string) string) Decorator { if decorator == nil { return nil } return metaWrapper{decorator, fn} } type metaWrapper struct { Decorator fn func(string) string } func (d metaWrapper) Decor(s Statistics) (string, int) { str, width := d.Decorator.Decor(s) return d.fn(str), width } func (d metaWrapper) Unwrap() Decorator { return d.Decorator } mpb-8.8.3/decor/moving_average.go000066400000000000000000000031001466312757300167340ustar00rootroot00000000000000package decor import ( "sort" "sync" "github.com/VividCortex/ewma" ) var ( _ ewma.MovingAverage = (*threadSafeMovingAverage)(nil) _ ewma.MovingAverage = (*medianWindow)(nil) _ sort.Interface = (*medianWindow)(nil) ) type threadSafeMovingAverage struct { ewma.MovingAverage mu sync.Mutex } func (s *threadSafeMovingAverage) Add(value float64) { s.mu.Lock() s.MovingAverage.Add(value) s.mu.Unlock() } func (s *threadSafeMovingAverage) Value() float64 { s.mu.Lock() defer s.mu.Unlock() return s.MovingAverage.Value() } func (s *threadSafeMovingAverage) Set(value float64) { s.mu.Lock() s.MovingAverage.Set(value) s.mu.Unlock() } // NewThreadSafeMovingAverage converts provided ewma.MovingAverage // into thread safe ewma.MovingAverage. func NewThreadSafeMovingAverage(average ewma.MovingAverage) ewma.MovingAverage { if tsma, ok := average.(*threadSafeMovingAverage); ok { return tsma } return &threadSafeMovingAverage{MovingAverage: average} } type medianWindow [3]float64 func (s *medianWindow) Len() int { return len(s) } func (s *medianWindow) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s *medianWindow) Less(i, j int) bool { return s[i] < s[j] } func (s *medianWindow) Add(value float64) { s[0], s[1] = s[1], s[2] s[2] = value } func (s *medianWindow) Value() float64 { tmp := *s sort.Sort(&tmp) return tmp[1] } func (s *medianWindow) Set(value float64) { for i := 0; i < len(s); i++ { s[i] = value } } // NewMedian is fixed last 3 samples median MovingAverage. func NewMedian() ewma.MovingAverage { return new(medianWindow) } mpb-8.8.3/decor/name.go000066400000000000000000000004351466312757300146730ustar00rootroot00000000000000package decor // Name decorator displays text that is set once and can't be changed // during decorator's lifetime. // // `str` string to display // // `wcc` optional WC config func Name(str string, wcc ...WC) Decorator { return Any(func(Statistics) string { return str }, wcc...) } mpb-8.8.3/decor/on_abort.go000066400000000000000000000027601466312757300155610ustar00rootroot00000000000000package decor var ( _ Decorator = onAbortWrapper{} _ Wrapper = onAbortWrapper{} _ Decorator = onAbortMetaWrapper{} _ Wrapper = onAbortMetaWrapper{} ) // OnAbort wrap decorator. // Displays provided message on abort event. // Has no effect if bar.Abort(true) is called. // // `decorator` Decorator to wrap // `message` message to display func OnAbort(decorator Decorator, message string) Decorator { if decorator == nil { return nil } return onAbortWrapper{decorator, message} } type onAbortWrapper struct { Decorator msg string } func (d onAbortWrapper) Decor(s Statistics) (string, int) { if s.Aborted { return d.Format(d.msg) } return d.Decorator.Decor(s) } func (d onAbortWrapper) Unwrap() Decorator { return d.Decorator } // OnAbortMeta wrap decorator. // Provided fn is supposed to wrap output of given decorator // with meta information like ANSI escape codes for example. // Primary usage intention is to set SGR display attributes. // // `decorator` Decorator to wrap // `fn` func to apply meta information func OnAbortMeta(decorator Decorator, fn func(string) string) Decorator { if decorator == nil { return nil } return onAbortMetaWrapper{decorator, fn} } type onAbortMetaWrapper struct { Decorator fn func(string) string } func (d onAbortMetaWrapper) Decor(s Statistics) (string, int) { if s.Aborted { str, width := d.Decorator.Decor(s) return d.fn(str), width } return d.Decorator.Decor(s) } func (d onAbortMetaWrapper) Unwrap() Decorator { return d.Decorator } mpb-8.8.3/decor/on_compete_or_on_abort.go000066400000000000000000000014001466312757300204570ustar00rootroot00000000000000package decor // OnCompleteOrOnAbort wrap decorator. // Displays provided message on complete or on abort event. // // `decorator` Decorator to wrap // `message` message to display func OnCompleteOrOnAbort(decorator Decorator, message string) Decorator { return OnComplete(OnAbort(decorator, message), message) } // OnCompleteMetaOrOnAbortMeta wrap decorator. // Provided fn is supposed to wrap output of given decorator // with meta information like ANSI escape codes for example. // Primary usage intention is to set SGR display attributes. // // `decorator` Decorator to wrap // `fn` func to apply meta information func OnCompleteMetaOrOnAbortMeta(decorator Decorator, fn func(string) string) Decorator { return OnCompleteMeta(OnAbortMeta(decorator, fn), fn) } mpb-8.8.3/decor/on_complete.go000066400000000000000000000027701466312757300162630ustar00rootroot00000000000000package decor var ( _ Decorator = onCompleteWrapper{} _ Wrapper = onCompleteWrapper{} _ Decorator = onCompleteMetaWrapper{} _ Wrapper = onCompleteMetaWrapper{} ) // OnComplete wrap decorator. // Displays provided message on complete event. // // `decorator` Decorator to wrap // `message` message to display func OnComplete(decorator Decorator, message string) Decorator { if decorator == nil { return nil } return onCompleteWrapper{decorator, message} } type onCompleteWrapper struct { Decorator msg string } func (d onCompleteWrapper) Decor(s Statistics) (string, int) { if s.Completed { return d.Format(d.msg) } return d.Decorator.Decor(s) } func (d onCompleteWrapper) Unwrap() Decorator { return d.Decorator } // OnCompleteMeta wrap decorator. // Provided fn is supposed to wrap output of given decorator // with meta information like ANSI escape codes for example. // Primary usage intention is to set SGR display attributes. // // `decorator` Decorator to wrap // `fn` func to apply meta information func OnCompleteMeta(decorator Decorator, fn func(string) string) Decorator { if decorator == nil { return nil } return onCompleteMetaWrapper{decorator, fn} } type onCompleteMetaWrapper struct { Decorator fn func(string) string } func (d onCompleteMetaWrapper) Decor(s Statistics) (string, int) { if s.Completed { str, width := d.Decorator.Decor(s) return d.fn(str), width } return d.Decorator.Decor(s) } func (d onCompleteMetaWrapper) Unwrap() Decorator { return d.Decorator } mpb-8.8.3/decor/on_condition.go000066400000000000000000000017761466312757300164460ustar00rootroot00000000000000package decor // OnCondition applies decorator only if a condition is true. // // `decorator` Decorator // // `cond` bool func OnCondition(decorator Decorator, cond bool) Decorator { return Conditional(cond, decorator, nil) } // OnPredicate applies decorator only if a predicate evaluates to true. // // `decorator` Decorator // // `predicate` func() bool func OnPredicate(decorator Decorator, predicate func() bool) Decorator { return Predicative(predicate, decorator, nil) } // Conditional returns decorator `a` if condition is true, otherwise // decorator `b`. // // `cond` bool // // `a` Decorator // // `b` Decorator func Conditional(cond bool, a, b Decorator) Decorator { if cond { return a } else { return b } } // Predicative returns decorator `a` if predicate evaluates to true, // otherwise decorator `b`. // // `predicate` func() bool // // `a` Decorator // // `b` Decorator func Predicative(predicate func() bool, a, b Decorator) Decorator { if predicate() { return a } else { return b } } mpb-8.8.3/decor/percentage.go000066400000000000000000000026641466312757300160760ustar00rootroot00000000000000package decor import ( "fmt" "strconv" "github.com/vbauerster/mpb/v8/internal" ) var _ fmt.Formatter = percentageType(0) type percentageType float64 func (s percentageType) Format(st fmt.State, verb rune) { prec := -1 switch verb { case 'f', 'e', 'E': prec = 6 // default prec of fmt.Printf("%f|%e|%E") fallthrough case 'b', 'g', 'G', 'x', 'X': if p, ok := st.Precision(); ok { prec = p } default: verb, prec = 'f', 0 } b := strconv.AppendFloat(make([]byte, 0, 16), float64(s), byte(verb), prec, 64) if st.Flag(' ') { b = append(b, ' ', '%') } else { b = append(b, '%') } _, err := st.Write(b) if err != nil { panic(err) } } // Percentage returns percentage decorator. It's a wrapper of NewPercentage. func Percentage(wcc ...WC) Decorator { return NewPercentage("% d", wcc...) } // NewPercentage percentage decorator with custom format string. // // `format` printf compatible verb // // `wcc` optional WC config // // format examples: // // format="%d" output: "1%" // format="% d" output: "1 %" // format="%.1f" output: "1.0%" // format="% .1f" output: "1.0 %" // format="%f" output: "1.000000%" // format="% f" output: "1.000000 %" func NewPercentage(format string, wcc ...WC) Decorator { if format == "" { format = "% d" } f := func(s Statistics) string { p := internal.Percentage(uint(s.Total), uint(s.Current), 100) return fmt.Sprintf(format, percentageType(p)) } return Any(f, wcc...) } mpb-8.8.3/decor/percentage_test.go000066400000000000000000000051071466312757300171300ustar00rootroot00000000000000package decor import ( "fmt" "testing" ) func TestPercentageType(t *testing.T) { cases := map[string]struct { value float64 verb string expected string }{ "10 %d": {10, "%d", "10%"}, "10 %s": {10, "%s", "10%"}, "10 %f": {10, "%f", "10.000000%"}, "10 %.6f": {10, "%.6f", "10.000000%"}, "10 %.0f": {10, "%.0f", "10%"}, "10 %.1f": {10, "%.1f", "10.0%"}, "10 %.2f": {10, "%.2f", "10.00%"}, "10 %.3f": {10, "%.3f", "10.000%"}, "10 % d": {10, "% d", "10 %"}, "10 % s": {10, "% s", "10 %"}, "10 % f": {10, "% f", "10.000000 %"}, "10 % .6f": {10, "% .6f", "10.000000 %"}, "10 % .0f": {10, "% .0f", "10 %"}, "10 % .1f": {10, "% .1f", "10.0 %"}, "10 % .2f": {10, "% .2f", "10.00 %"}, "10 % .3f": {10, "% .3f", "10.000 %"}, "10.5 %d": {10.5, "%d", "10%"}, "10.5 %s": {10.5, "%s", "10%"}, "10.5 %f": {10.5, "%f", "10.500000%"}, "10.5 %.6f": {10.5, "%.6f", "10.500000%"}, "10.5 %.0f": {10.5, "%.0f", "10%"}, "10.5 %.1f": {10.5, "%.1f", "10.5%"}, "10.5 %.2f": {10.5, "%.2f", "10.50%"}, "10.5 %.3f": {10.5, "%.3f", "10.500%"}, "10.5 % d": {10.5, "% d", "10 %"}, "10.5 % s": {10.5, "% s", "10 %"}, "10.5 % f": {10.5, "% f", "10.500000 %"}, "10.5 % .6f": {10.5, "% .6f", "10.500000 %"}, "10.5 % .0f": {10.5, "% .0f", "10 %"}, "10.5 % .1f": {10.5, "% .1f", "10.5 %"}, "10.5 % .2f": {10.5, "% .2f", "10.50 %"}, "10.5 % .3f": {10.5, "% .3f", "10.500 %"}, } for name, tc := range cases { t.Run(name, func(t *testing.T) { got := fmt.Sprintf(tc.verb, percentageType(tc.value)) if got != tc.expected { t.Fatalf("expected: %q, got: %q\n", tc.expected, got) } }) } } func TestPercentageDecor(t *testing.T) { cases := []struct { name string fmt string current int64 total int64 expected string }{ { name: "tot:100 cur:0 fmt:none", fmt: "", current: 0, total: 100, expected: "0 %", }, { name: "tot:100 cur:10 fmt:none", fmt: "", current: 10, total: 100, expected: "10 %", }, { name: "tot:100 cur:10 fmt:%.2f", fmt: "%.2f", current: 10, total: 100, expected: "10.00%", }, { name: "tot:99 cur:10 fmt:%.2f", fmt: "%.2f", current: 11, total: 99, expected: "11.11%", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { decor := NewPercentage(tc.fmt) stat := Statistics{ Total: tc.total, Current: tc.current, } res, _ := decor.Decor(stat) if res != tc.expected { t.Fatalf("expected: %q, got: %q\n", tc.expected, res) } }) } } mpb-8.8.3/decor/size_type.go000066400000000000000000000045231466312757300157700ustar00rootroot00000000000000package decor import ( "fmt" "strconv" ) //go:generate stringer -type=SizeB1024 -trimprefix=_i //go:generate stringer -type=SizeB1000 -trimprefix=_ var ( _ fmt.Formatter = SizeB1024(0) _ fmt.Stringer = SizeB1024(0) _ fmt.Formatter = SizeB1000(0) _ fmt.Stringer = SizeB1000(0) ) const ( _ib SizeB1024 = iota + 1 _iKiB SizeB1024 = 1 << (iota * 10) _iMiB _iGiB _iTiB ) // SizeB1024 named type, which implements fmt.Formatter interface. It // adjusts its value according to byte size multiple by 1024 and appends // appropriate size marker (KiB, MiB, GiB, TiB). type SizeB1024 int64 func (self SizeB1024) Format(st fmt.State, verb rune) { prec := -1 switch verb { case 'f', 'e', 'E': prec = 6 // default prec of fmt.Printf("%f|%e|%E") fallthrough case 'b', 'g', 'G', 'x', 'X': if p, ok := st.Precision(); ok { prec = p } default: verb, prec = 'f', 0 } var unit SizeB1024 switch { case self < _iKiB: unit = _ib case self < _iMiB: unit = _iKiB case self < _iGiB: unit = _iMiB case self < _iTiB: unit = _iGiB default: unit = _iTiB } b := strconv.AppendFloat(make([]byte, 0, 24), float64(self)/float64(unit), byte(verb), prec, 64) if st.Flag(' ') { b = append(b, ' ') } b = append(b, []byte(unit.String())...) _, err := st.Write(b) if err != nil { panic(err) } } const ( _b SizeB1000 = 1 _KB SizeB1000 = _b * 1000 _MB SizeB1000 = _KB * 1000 _GB SizeB1000 = _MB * 1000 _TB SizeB1000 = _GB * 1000 ) // SizeB1000 named type, which implements fmt.Formatter interface. It // adjusts its value according to byte size multiple by 1000 and appends // appropriate size marker (KB, MB, GB, TB). type SizeB1000 int64 func (self SizeB1000) Format(st fmt.State, verb rune) { prec := -1 switch verb { case 'f', 'e', 'E': prec = 6 // default prec of fmt.Printf("%f|%e|%E") fallthrough case 'b', 'g', 'G', 'x', 'X': if p, ok := st.Precision(); ok { prec = p } default: verb, prec = 'f', 0 } var unit SizeB1000 switch { case self < _KB: unit = _b case self < _MB: unit = _KB case self < _GB: unit = _MB case self < _TB: unit = _GB default: unit = _TB } b := strconv.AppendFloat(make([]byte, 0, 24), float64(self)/float64(unit), byte(verb), prec, 64) if st.Flag(' ') { b = append(b, ' ') } b = append(b, []byte(unit.String())...) _, err := st.Write(b) if err != nil { panic(err) } } mpb-8.8.3/decor/size_type_test.go000066400000000000000000000144421466312757300170300ustar00rootroot00000000000000package decor import ( "fmt" "testing" ) func TestB1024(t *testing.T) { cases := map[string]struct { value int64 verb string expected string }{ "verb %d": {12345678, "%d", "12MiB"}, "verb %s": {12345678, "%s", "12MiB"}, "verb %f": {12345678, "%f", "11.773756MiB"}, "verb %.6f": {12345678, "%.6f", "11.773756MiB"}, "verb %.0f": {12345678, "%.0f", "12MiB"}, "verb %.1f": {12345678, "%.1f", "11.8MiB"}, "verb %.2f": {12345678, "%.2f", "11.77MiB"}, "verb %.3f": {12345678, "%.3f", "11.774MiB"}, "verb % d": {12345678, "% d", "12 MiB"}, "verb % s": {12345678, "% s", "12 MiB"}, "verb % f": {12345678, "% f", "11.773756 MiB"}, "verb % .6f": {12345678, "% .6f", "11.773756 MiB"}, "verb % .0f": {12345678, "% .0f", "12 MiB"}, "verb % .1f": {12345678, "% .1f", "11.8 MiB"}, "verb % .2f": {12345678, "% .2f", "11.77 MiB"}, "verb % .3f": {12345678, "% .3f", "11.774 MiB"}, "1000 %d": {1000, "%d", "1000b"}, "1000 %s": {1000, "%s", "1000b"}, "1000 %f": {1000, "%f", "1000.000000b"}, "1000 %.6f": {1000, "%.6f", "1000.000000b"}, "1000 %.0f": {1000, "%.0f", "1000b"}, "1000 %.1f": {1000, "%.1f", "1000.0b"}, "1000 %.2f": {1000, "%.2f", "1000.00b"}, "1000 %.3f": {1000, "%.3f", "1000.000b"}, "1024 %d": {1024, "%d", "1KiB"}, "1024 %s": {1024, "%s", "1KiB"}, "1024 %f": {1024, "%f", "1.000000KiB"}, "1024 %.6f": {1024, "%.6f", "1.000000KiB"}, "1024 %.0f": {1024, "%.0f", "1KiB"}, "1024 %.1f": {1024, "%.1f", "1.0KiB"}, "1024 %.2f": {1024, "%.2f", "1.00KiB"}, "1024 %.3f": {1024, "%.3f", "1.000KiB"}, "3*MiB+100KiB %d": {3*int64(_iMiB) + 100*int64(_iKiB), "%d", "3MiB"}, "3*MiB+100KiB %s": {3*int64(_iMiB) + 100*int64(_iKiB), "%s", "3MiB"}, "3*MiB+100KiB %f": {3*int64(_iMiB) + 100*int64(_iKiB), "%f", "3.097656MiB"}, "3*MiB+100KiB %.6f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.6f", "3.097656MiB"}, "3*MiB+100KiB %.0f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.0f", "3MiB"}, "3*MiB+100KiB %.1f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.1f", "3.1MiB"}, "3*MiB+100KiB %.2f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.2f", "3.10MiB"}, "3*MiB+100KiB %.3f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.3f", "3.098MiB"}, "2*GiB %d": {2 * int64(_iGiB), "%d", "2GiB"}, "2*GiB %s": {2 * int64(_iGiB), "%s", "2GiB"}, "2*GiB %f": {2 * int64(_iGiB), "%f", "2.000000GiB"}, "2*GiB %.6f": {2 * int64(_iGiB), "%.6f", "2.000000GiB"}, "2*GiB %.0f": {2 * int64(_iGiB), "%.0f", "2GiB"}, "2*GiB %.1f": {2 * int64(_iGiB), "%.1f", "2.0GiB"}, "2*GiB %.2f": {2 * int64(_iGiB), "%.2f", "2.00GiB"}, "2*GiB %.3f": {2 * int64(_iGiB), "%.3f", "2.000GiB"}, "4*TiB %d": {4 * int64(_iTiB), "%d", "4TiB"}, "4*TiB %s": {4 * int64(_iTiB), "%s", "4TiB"}, "4*TiB %f": {4 * int64(_iTiB), "%f", "4.000000TiB"}, "4*TiB %.6f": {4 * int64(_iTiB), "%.6f", "4.000000TiB"}, "4*TiB %.0f": {4 * int64(_iTiB), "%.0f", "4TiB"}, "4*TiB %.1f": {4 * int64(_iTiB), "%.1f", "4.0TiB"}, "4*TiB %.2f": {4 * int64(_iTiB), "%.2f", "4.00TiB"}, "4*TiB %.3f": {4 * int64(_iTiB), "%.3f", "4.000TiB"}, } for name, tc := range cases { t.Run(name, func(t *testing.T) { got := fmt.Sprintf(tc.verb, SizeB1024(tc.value)) if got != tc.expected { t.Fatalf("expected: %q, got: %q\n", tc.expected, got) } }) } } func TestB1000(t *testing.T) { cases := map[string]struct { value int64 verb string expected string }{ "verb %d": {12345678, "%d", "12MB"}, "verb %s": {12345678, "%s", "12MB"}, "verb %f": {12345678, "%f", "12.345678MB"}, "verb %.6f": {12345678, "%.6f", "12.345678MB"}, "verb %.0f": {12345678, "%.0f", "12MB"}, "verb %.1f": {12345678, "%.1f", "12.3MB"}, "verb %.2f": {12345678, "%.2f", "12.35MB"}, "verb %.3f": {12345678, "%.3f", "12.346MB"}, "verb % d": {12345678, "% d", "12 MB"}, "verb % s": {12345678, "% s", "12 MB"}, "verb % f": {12345678, "% f", "12.345678 MB"}, "verb % .6f": {12345678, "% .6f", "12.345678 MB"}, "verb % .0f": {12345678, "% .0f", "12 MB"}, "verb % .1f": {12345678, "% .1f", "12.3 MB"}, "verb % .2f": {12345678, "% .2f", "12.35 MB"}, "verb % .3f": {12345678, "% .3f", "12.346 MB"}, "1000 %d": {1000, "%d", "1KB"}, "1000 %s": {1000, "%s", "1KB"}, "1000 %f": {1000, "%f", "1.000000KB"}, "1000 %.6f": {1000, "%.6f", "1.000000KB"}, "1000 %.0f": {1000, "%.0f", "1KB"}, "1000 %.1f": {1000, "%.1f", "1.0KB"}, "1000 %.2f": {1000, "%.2f", "1.00KB"}, "1000 %.3f": {1000, "%.3f", "1.000KB"}, "1024 %d": {1024, "%d", "1KB"}, "1024 %s": {1024, "%s", "1KB"}, "1024 %f": {1024, "%f", "1.024000KB"}, "1024 %.6f": {1024, "%.6f", "1.024000KB"}, "1024 %.0f": {1024, "%.0f", "1KB"}, "1024 %.1f": {1024, "%.1f", "1.0KB"}, "1024 %.2f": {1024, "%.2f", "1.02KB"}, "1024 %.3f": {1024, "%.3f", "1.024KB"}, "3*MB+100*KB %d": {3*int64(_MB) + 100*int64(_KB), "%d", "3MB"}, "3*MB+100*KB %s": {3*int64(_MB) + 100*int64(_KB), "%s", "3MB"}, "3*MB+100*KB %f": {3*int64(_MB) + 100*int64(_KB), "%f", "3.100000MB"}, "3*MB+100*KB %.6f": {3*int64(_MB) + 100*int64(_KB), "%.6f", "3.100000MB"}, "3*MB+100*KB %.0f": {3*int64(_MB) + 100*int64(_KB), "%.0f", "3MB"}, "3*MB+100*KB %.1f": {3*int64(_MB) + 100*int64(_KB), "%.1f", "3.1MB"}, "3*MB+100*KB %.2f": {3*int64(_MB) + 100*int64(_KB), "%.2f", "3.10MB"}, "3*MB+100*KB %.3f": {3*int64(_MB) + 100*int64(_KB), "%.3f", "3.100MB"}, "2*GB %d": {2 * int64(_GB), "%d", "2GB"}, "2*GB %s": {2 * int64(_GB), "%s", "2GB"}, "2*GB %f": {2 * int64(_GB), "%f", "2.000000GB"}, "2*GB %.6f": {2 * int64(_GB), "%.6f", "2.000000GB"}, "2*GB %.0f": {2 * int64(_GB), "%.0f", "2GB"}, "2*GB %.1f": {2 * int64(_GB), "%.1f", "2.0GB"}, "2*GB %.2f": {2 * int64(_GB), "%.2f", "2.00GB"}, "2*GB %.3f": {2 * int64(_GB), "%.3f", "2.000GB"}, "4*TB %d": {4 * int64(_TB), "%d", "4TB"}, "4*TB %s": {4 * int64(_TB), "%s", "4TB"}, "4*TB %f": {4 * int64(_TB), "%f", "4.000000TB"}, "4*TB %.6f": {4 * int64(_TB), "%.6f", "4.000000TB"}, "4*TB %.0f": {4 * int64(_TB), "%.0f", "4TB"}, "4*TB %.1f": {4 * int64(_TB), "%.1f", "4.0TB"}, "4*TB %.2f": {4 * int64(_TB), "%.2f", "4.00TB"}, "4*TB %.3f": {4 * int64(_TB), "%.3f", "4.000TB"}, } for name, tc := range cases { t.Run(name, func(t *testing.T) { got := fmt.Sprintf(tc.verb, SizeB1000(tc.value)) if got != tc.expected { t.Fatalf("expected: %q, got: %q\n", tc.expected, got) } }) } } mpb-8.8.3/decor/sizeb1000_string.go000066400000000000000000000015721466312757300167610ustar00rootroot00000000000000// Code generated by "stringer -type=SizeB1000 -trimprefix=_"; DO NOT EDIT. package decor import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[_b-1] _ = x[_KB-1000] _ = x[_MB-1000000] _ = x[_GB-1000000000] _ = x[_TB-1000000000000] } const ( _SizeB1000_name_0 = "b" _SizeB1000_name_1 = "KB" _SizeB1000_name_2 = "MB" _SizeB1000_name_3 = "GB" _SizeB1000_name_4 = "TB" ) func (i SizeB1000) String() string { switch { case i == 1: return _SizeB1000_name_0 case i == 1000: return _SizeB1000_name_1 case i == 1000000: return _SizeB1000_name_2 case i == 1000000000: return _SizeB1000_name_3 case i == 1000000000000: return _SizeB1000_name_4 default: return "SizeB1000(" + strconv.FormatInt(int64(i), 10) + ")" } } mpb-8.8.3/decor/sizeb1024_string.go000066400000000000000000000016101466312757300167600ustar00rootroot00000000000000// Code generated by "stringer -type=SizeB1024 -trimprefix=_i"; DO NOT EDIT. package decor import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[_ib-1] _ = x[_iKiB-1024] _ = x[_iMiB-1048576] _ = x[_iGiB-1073741824] _ = x[_iTiB-1099511627776] } const ( _SizeB1024_name_0 = "b" _SizeB1024_name_1 = "KiB" _SizeB1024_name_2 = "MiB" _SizeB1024_name_3 = "GiB" _SizeB1024_name_4 = "TiB" ) func (i SizeB1024) String() string { switch { case i == 1: return _SizeB1024_name_0 case i == 1024: return _SizeB1024_name_1 case i == 1048576: return _SizeB1024_name_2 case i == 1073741824: return _SizeB1024_name_3 case i == 1099511627776: return _SizeB1024_name_4 default: return "SizeB1024(" + strconv.FormatInt(int64(i), 10) + ")" } } mpb-8.8.3/decor/speed.go000066400000000000000000000111241466312757300150500ustar00rootroot00000000000000package decor import ( "fmt" "io" "math" "time" "github.com/VividCortex/ewma" ) var ( _ Decorator = (*movingAverageSpeed)(nil) _ EwmaDecorator = (*movingAverageSpeed)(nil) _ Decorator = (*averageSpeed)(nil) _ AverageDecorator = (*averageSpeed)(nil) ) // FmtAsSpeed adds "/s" to the end of the input formatter. To be // used with SizeB1000 or SizeB1024 types, for example: // // fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048))) func FmtAsSpeed(input fmt.Formatter) fmt.Formatter { return &speedFormatter{input} } type speedFormatter struct { fmt.Formatter } func (s *speedFormatter) Format(st fmt.State, verb rune) { s.Formatter.Format(st, verb) _, err := io.WriteString(st, "/s") if err != nil { panic(err) } } // EwmaSpeed exponential-weighted-moving-average based speed decorator. // For this decorator to work correctly you have to measure each iteration's // duration and pass it to one of the (*Bar).EwmaIncr... family methods. func EwmaSpeed(unit interface{}, format string, age float64, wcc ...WC) Decorator { var average ewma.MovingAverage if age == 0 { average = ewma.NewMovingAverage() } else { average = ewma.NewMovingAverage(age) } return MovingAverageSpeed(unit, format, average, wcc...) } // MovingAverageSpeed decorator relies on MovingAverage implementation // to calculate its average. // // `unit` one of [0|SizeB1024(0)|SizeB1000(0)] // // `format` printf compatible verb for value, like "%f" or "%d" // // `average` MovingAverage implementation // // `wcc` optional WC config // // format examples: // // unit=SizeB1024(0), format="%.1f" output: "1.0MiB/s" // unit=SizeB1024(0), format="% .1f" output: "1.0 MiB/s" // unit=SizeB1000(0), format="%.1f" output: "1.0MB/s" // unit=SizeB1000(0), format="% .1f" output: "1.0 MB/s" func MovingAverageSpeed(unit interface{}, format string, average ewma.MovingAverage, wcc ...WC) Decorator { d := &movingAverageSpeed{ WC: initWC(wcc...), producer: chooseSpeedProducer(unit, format), average: average, } return d } type movingAverageSpeed struct { WC producer func(float64) string average ewma.MovingAverage zDur time.Duration } func (d *movingAverageSpeed) Decor(_ Statistics) (string, int) { var str string // ewma implementation may return 0 before accumulating certain number of samples if v := d.average.Value(); v != 0 { str = d.producer(1e9 / v) } else { str = d.producer(0) } return d.Format(str) } func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) { if n <= 0 { d.zDur += dur return } durPerByte := float64(d.zDur+dur) / float64(n) if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) { d.zDur += dur return } d.zDur = 0 d.average.Add(durPerByte) } // AverageSpeed decorator with dynamic unit measure adjustment. It's // a wrapper of NewAverageSpeed. func AverageSpeed(unit interface{}, format string, wcc ...WC) Decorator { return NewAverageSpeed(unit, format, time.Now(), wcc...) } // NewAverageSpeed decorator with dynamic unit measure adjustment and // user provided start time. // // `unit` one of [0|SizeB1024(0)|SizeB1000(0)] // // `format` printf compatible verb for value, like "%f" or "%d" // // `start` start time // // `wcc` optional WC config // // format examples: // // unit=SizeB1024(0), format="%.1f" output: "1.0MiB/s" // unit=SizeB1024(0), format="% .1f" output: "1.0 MiB/s" // unit=SizeB1000(0), format="%.1f" output: "1.0MB/s" // unit=SizeB1000(0), format="% .1f" output: "1.0 MB/s" func NewAverageSpeed(unit interface{}, format string, start time.Time, wcc ...WC) Decorator { d := &averageSpeed{ WC: initWC(wcc...), start: start, producer: chooseSpeedProducer(unit, format), } return d } type averageSpeed struct { WC start time.Time producer func(float64) string msg string } func (d *averageSpeed) Decor(s Statistics) (string, int) { if !s.Completed { speed := float64(s.Current) / float64(time.Since(d.start)) d.msg = d.producer(speed * 1e9) } return d.Format(d.msg) } func (d *averageSpeed) AverageAdjust(start time.Time) { d.start = start } func chooseSpeedProducer(unit interface{}, format string) func(float64) string { switch unit.(type) { case SizeB1024: if format == "" { format = "% d" } return func(speed float64) string { return fmt.Sprintf(format, FmtAsSpeed(SizeB1024(math.Round(speed)))) } case SizeB1000: if format == "" { format = "% d" } return func(speed float64) string { return fmt.Sprintf(format, FmtAsSpeed(SizeB1000(math.Round(speed)))) } default: if format == "" { format = "%f" } return func(speed float64) string { return fmt.Sprintf(format, speed) } } } mpb-8.8.3/decor/speed_test.go000066400000000000000000000132471466312757300161170ustar00rootroot00000000000000package decor import ( "testing" "time" ) func TestAverageSpeedSizeB1024(t *testing.T) { cases := []struct { name string fmt string unit interface{} current int64 elapsed time.Duration expected string }{ { name: "empty fmt", unit: SizeB1024(0), fmt: "", current: 0, elapsed: time.Second, expected: "0 b/s", }, { name: "SizeB1024(0):%d:0b", unit: SizeB1024(0), fmt: "%d", current: 0, elapsed: time.Second, expected: "0b/s", }, { name: "SizeB1024(0):%f:0b", unit: SizeB1024(0), fmt: "%f", current: 0, elapsed: time.Second, expected: "0.000000b/s", }, { name: "SizeB1024(0):% .2f:0b", unit: SizeB1024(0), fmt: "% .2f", current: 0, elapsed: time.Second, expected: "0.00 b/s", }, { name: "SizeB1024(0):%d:1b", unit: SizeB1024(0), fmt: "%d", current: 1, elapsed: time.Second, expected: "1b/s", }, { name: "SizeB1024(0):% .2f:1b", unit: SizeB1024(0), fmt: "% .2f", current: 1, elapsed: time.Second, expected: "1.00 b/s", }, { name: "SizeB1024(0):%d:KiB", unit: SizeB1024(0), fmt: "%d", current: 2 * int64(_iKiB), elapsed: 1 * time.Second, expected: "2KiB/s", }, { name: "SizeB1024(0):% .f:KiB", unit: SizeB1024(0), fmt: "% .2f", current: 2 * int64(_iKiB), elapsed: 1 * time.Second, expected: "2.00 KiB/s", }, { name: "SizeB1024(0):%d:MiB", unit: SizeB1024(0), fmt: "%d", current: 2 * int64(_iMiB), elapsed: 1 * time.Second, expected: "2MiB/s", }, { name: "SizeB1024(0):% .2f:MiB", unit: SizeB1024(0), fmt: "% .2f", current: 2 * int64(_iMiB), elapsed: 1 * time.Second, expected: "2.00 MiB/s", }, { name: "SizeB1024(0):%d:GiB", unit: SizeB1024(0), fmt: "%d", current: 2 * int64(_iGiB), elapsed: 1 * time.Second, expected: "2GiB/s", }, { name: "SizeB1024(0):% .2f:GiB", unit: SizeB1024(0), fmt: "% .2f", current: 2 * int64(_iGiB), elapsed: 1 * time.Second, expected: "2.00 GiB/s", }, { name: "SizeB1024(0):%d:TiB", unit: SizeB1024(0), fmt: "%d", current: 2 * int64(_iTiB), elapsed: 1 * time.Second, expected: "2TiB/s", }, { name: "SizeB1024(0):% .2f:TiB", unit: SizeB1024(0), fmt: "% .2f", current: 2 * int64(_iTiB), elapsed: 1 * time.Second, expected: "2.00 TiB/s", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { decor := NewAverageSpeed(tc.unit, tc.fmt, time.Now().Add(-tc.elapsed)) stat := Statistics{ Current: tc.current, } res, _ := decor.Decor(stat) if res != tc.expected { t.Fatalf("expected: %q, got: %q\n", tc.expected, res) } }) } } func TestAverageSpeedSizeB1000(t *testing.T) { cases := []struct { name string fmt string unit interface{} current int64 elapsed time.Duration expected string }{ { name: "empty fmt", unit: SizeB1000(0), fmt: "", current: 0, elapsed: time.Second, expected: "0 b/s", }, { name: "SizeB1000(0):%d:0b", unit: SizeB1000(0), fmt: "%d", current: 0, elapsed: time.Second, expected: "0b/s", }, { name: "SizeB1000(0):%f:0b", unit: SizeB1000(0), fmt: "%f", current: 0, elapsed: time.Second, expected: "0.000000b/s", }, { name: "SizeB1000(0):% .2f:0b", unit: SizeB1000(0), fmt: "% .2f", current: 0, elapsed: time.Second, expected: "0.00 b/s", }, { name: "SizeB1000(0):%d:1b", unit: SizeB1000(0), fmt: "%d", current: 1, elapsed: time.Second, expected: "1b/s", }, { name: "SizeB1000(0):% .2f:1b", unit: SizeB1000(0), fmt: "% .2f", current: 1, elapsed: time.Second, expected: "1.00 b/s", }, { name: "SizeB1000(0):%d:KB", unit: SizeB1000(0), fmt: "%d", current: 2 * int64(_KB), elapsed: 1 * time.Second, expected: "2KB/s", }, { name: "SizeB1000(0):% .f:KB", unit: SizeB1000(0), fmt: "% .2f", current: 2 * int64(_KB), elapsed: 1 * time.Second, expected: "2.00 KB/s", }, { name: "SizeB1000(0):%d:MB", unit: SizeB1000(0), fmt: "%d", current: 2 * int64(_MB), elapsed: 1 * time.Second, expected: "2MB/s", }, { name: "SizeB1000(0):% .2f:MB", unit: SizeB1000(0), fmt: "% .2f", current: 2 * int64(_MB), elapsed: 1 * time.Second, expected: "2.00 MB/s", }, { name: "SizeB1000(0):%d:GB", unit: SizeB1000(0), fmt: "%d", current: 2 * int64(_GB), elapsed: 1 * time.Second, expected: "2GB/s", }, { name: "SizeB1000(0):% .2f:GB", unit: SizeB1000(0), fmt: "% .2f", current: 2 * int64(_GB), elapsed: 1 * time.Second, expected: "2.00 GB/s", }, { name: "SizeB1000(0):%d:TB", unit: SizeB1000(0), fmt: "%d", current: 2 * int64(_TB), elapsed: 1 * time.Second, expected: "2TB/s", }, { name: "SizeB1000(0):% .2f:TB", unit: SizeB1000(0), fmt: "% .2f", current: 2 * int64(_TB), elapsed: 1 * time.Second, expected: "2.00 TB/s", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { decor := NewAverageSpeed(tc.unit, tc.fmt, time.Now().Add(-tc.elapsed)) stat := Statistics{ Current: tc.current, } res, _ := decor.Decor(stat) if res != tc.expected { t.Fatalf("expected: %q, got: %q\n", tc.expected, res) } }) } } mpb-8.8.3/decor/spinner.go000066400000000000000000000010051466312757300154230ustar00rootroot00000000000000package decor var defaultSpinnerStyle = [...]string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} // Spinner returns spinner decorator. // // `frames` spinner frames, if nil or len==0, default is used // // `wcc` optional WC config func Spinner(frames []string, wcc ...WC) Decorator { if len(frames) == 0 { frames = defaultSpinnerStyle[:] } var count uint f := func(s Statistics) string { frame := frames[count%uint(len(frames))] count++ return frame } return Any(f, wcc...) } mpb-8.8.3/decorators_test.go000066400000000000000000000077731466312757300160770ustar00rootroot00000000000000package mpb_test import ( "testing" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func TestNameDecorator(t *testing.T) { tests := []struct { decorator decor.Decorator want string }{ { decorator: decor.Name("Test"), want: "Test", }, { decorator: decor.Name("Test", decor.WC{W: len("Test")}), want: "Test", }, { decorator: decor.Name("Test", decor.WC{W: 10}), want: " Test", }, { decorator: decor.Name("Test", decor.WC{W: 10, C: decor.DindentRight}), want: "Test ", }, } for _, test := range tests { got, _ := test.decorator.Decor(decor.Statistics{}) if got != test.want { t.Errorf("Want: %q, Got: %q\n", test.want, got) } } } type step struct { stat decor.Statistics decorator decor.Decorator want string } func TestPercentageDwidthSync(t *testing.T) { testCases := [][]step{ { { decor.Statistics{Total: 100, Current: 8}, decor.Percentage(decor.WCSyncWidth), "8 %", }, { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncWidth), "9 %", }, }, { { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncWidth), " 9 %", }, { decor.Statistics{Total: 100, Current: 10}, decor.Percentage(decor.WCSyncWidth), "10 %", }, }, { { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncWidth), " 9 %", }, { decor.Statistics{Total: 100, Current: 100}, decor.Percentage(decor.WCSyncWidth), "100 %", }, }, } testDecoratorConcurrently(t, testCases) } func TestPercentageDwidthSyncDindentRight(t *testing.T) { testCases := [][]step{ { { decor.Statistics{Total: 100, Current: 8}, decor.Percentage(decor.WCSyncWidthR), "8 %", }, { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncWidthR), "9 %", }, }, { { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncWidthR), "9 % ", }, { decor.Statistics{Total: 100, Current: 10}, decor.Percentage(decor.WCSyncWidthR), "10 %", }, }, { { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncWidthR), "9 % ", }, { decor.Statistics{Total: 100, Current: 100}, decor.Percentage(decor.WCSyncWidthR), "100 %", }, }, } testDecoratorConcurrently(t, testCases) } func TestPercentageDSyncSpace(t *testing.T) { testCases := [][]step{ { { decor.Statistics{Total: 100, Current: 8}, decor.Percentage(decor.WCSyncSpace), " 8 %", }, { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncSpace), " 9 %", }, }, { { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncSpace), " 9 %", }, { decor.Statistics{Total: 100, Current: 10}, decor.Percentage(decor.WCSyncSpace), " 10 %", }, }, { { decor.Statistics{Total: 100, Current: 9}, decor.Percentage(decor.WCSyncSpace), " 9 %", }, { decor.Statistics{Total: 100, Current: 100}, decor.Percentage(decor.WCSyncSpace), " 100 %", }, }, } testDecoratorConcurrently(t, testCases) } func testDecoratorConcurrently(t *testing.T, testCases [][]step) { if len(testCases) == 0 { t.Fail() } for _, columnCase := range testCases { mpb.SyncWidth(toSyncMatrix(columnCase), nil) var results []chan string for _, step := range columnCase { step := step ch := make(chan string) go func() { str, _ := step.decorator.Decor(step.stat) ch <- str }() results = append(results, ch) } for i, ch := range results { res := <-ch want := columnCase[i].want if res != want { t.Errorf("Want: %q, Got: %q\n", want, res) } } } } func toSyncMatrix(ss []step) map[int][]chan int { var column []chan int for _, s := range ss { if ch, ok := s.decorator.Sync(); ok { column = append(column, ch) } } return map[int][]chan int{0: column} } mpb-8.8.3/doc.go000066400000000000000000000001361466312757300134220ustar00rootroot00000000000000// Package mpb is a library for rendering progress bars in terminal applications. package mpb mpb-8.8.3/draw_test.go000066400000000000000000001105521466312757300146550ustar00rootroot00000000000000package mpb import ( "bytes" "testing" "unicode/utf8" ) func TestDrawDefault(t *testing.T) { // key is termWidth testSuite := map[int][]struct { style BarStyleComposer name string total int64 current int64 refill int64 barWidth int trim bool want string }{ 0: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: "", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "", }, }, 1: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: "", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "", }, }, 2: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: " ", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "[]", }, }, 3: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: " ", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "[-]", }, { style: BarStyle(), name: "t,c{60,59}", total: 60, current: 59, want: " ", }, { style: BarStyle(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[>]", }, { style: BarStyle(), name: "t,c{60,60}", total: 60, current: 60, want: " ", }, { style: BarStyle(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[=]", }, }, 4: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: " [] ", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "[>-]", }, { style: BarStyle(), name: "t,c{60,59}", total: 60, current: 59, want: " [] ", }, { style: BarStyle(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[=>]", }, { style: BarStyle(), name: "t,c{60,60}", total: 60, current: 60, want: " [] ", }, { style: BarStyle(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[==]", }, }, 5: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: " [-] ", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "[>--]", }, { style: BarStyle(), name: "t,c{60,59}", total: 60, current: 59, want: " [>] ", }, { style: BarStyle(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[==>]", }, { style: BarStyle(), name: "t,c{60,60}", total: 60, current: 60, want: " [=] ", }, { style: BarStyle(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[===]", }, }, 6: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: " [>-] ", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "[>---]", }, { style: BarStyle(), name: "t,c{60,59}", total: 60, current: 59, want: " [=>] ", }, { style: BarStyle(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[===>]", }, { style: BarStyle(), name: "t,c{60,60}", total: 60, current: 60, want: " [==] ", }, { style: BarStyle(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[====]", }, }, 7: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: " [>--] ", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "[=>---]", }, { style: BarStyle(), name: "t,c{60,59}", total: 60, current: 59, want: " [==>] ", }, { style: BarStyle(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[====>]", }, { style: BarStyle(), name: "t,c{60,60}", total: 60, current: 60, want: " [===] ", }, { style: BarStyle(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[=====]", }, }, 8: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: " [>---] ", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "[=>----]", }, { style: BarStyle(), name: "t,c{60,59}", total: 60, current: 59, want: " [===>] ", }, { style: BarStyle(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[=====>]", }, { style: BarStyle(), name: "t,c{60,60}", total: 60, current: 60, want: " [====] ", }, { style: BarStyle(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[======]", }, }, 80: { { style: BarStyle(), name: "t,c{60,20}", total: 60, current: 20, want: " [========================>---------------------------------------------------] ", }, { style: BarStyle(), name: "t,c{60,20}trim", total: 60, current: 20, trim: true, want: "[=========================>----------------------------------------------------]", }, { style: BarStyle(), name: "t,c,bw{60,20,60}", total: 60, current: 20, barWidth: 60, want: " [==================>---------------------------------------] ", }, { style: BarStyle(), name: "t,c,bw{60,20,60}trim", total: 60, current: 20, barWidth: 60, trim: true, want: "[==================>---------------------------------------]", }, { style: BarStyle(), name: "t,c{60,59}", total: 60, current: 59, want: " [==========================================================================>-] ", }, { style: BarStyle(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[============================================================================>-]", }, { style: BarStyle(), name: "t,c,bw{60,59,60}", total: 60, current: 59, barWidth: 60, want: " [========================================================>-] ", }, { style: BarStyle(), name: "t,c,bw{60,59,60}trim", total: 60, current: 59, barWidth: 60, trim: true, want: "[========================================================>-]", }, { style: BarStyle(), name: "t,c{60,60}", total: 60, current: 60, want: " [============================================================================] ", }, { style: BarStyle(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[==============================================================================]", }, { style: BarStyle(), name: "t,c,bw{60,60,60}", total: 60, current: 60, barWidth: 60, want: " [==========================================================] ", }, { style: BarStyle(), name: "t,c,bw{60,60,60}trim", total: 60, current: 60, barWidth: 60, trim: true, want: "[==========================================================]", }, }, 99: { { style: BarStyle(), name: "t,c{100,1}", total: 100, current: 1, want: " [>----------------------------------------------------------------------------------------------] ", }, { style: BarStyle(), name: "t,c{100,1}trim", total: 100, current: 1, trim: true, want: "[>------------------------------------------------------------------------------------------------]", }, { style: BarStyle(), name: "t,c,r{100,40,33}", total: 100, current: 40, refill: 33, want: " [+++++++++++++++++++++++++++++++======>---------------------------------------------------------] ", }, { style: BarStyle(), name: "t,c,r{100,40,33}trim", total: 100, current: 40, refill: 33, trim: true, want: "[++++++++++++++++++++++++++++++++======>----------------------------------------------------------]", }, { style: BarStyle().Tip("<").Reverse(), name: "t,c,r{100,40,33},rev", total: 100, current: 40, refill: 33, want: " [---------------------------------------------------------<======+++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Tip("<").Reverse(), name: "t,c,r{100,40,33}trim,rev", total: 100, current: 40, refill: 33, trim: true, want: "[----------------------------------------------------------<======++++++++++++++++++++++++++++++++]", }, { style: BarStyle(), name: "t,c{100,99}", total: 100, current: 99, want: " [=============================================================================================>-] ", }, { style: BarStyle(), name: "t,c{100,99}trim", total: 100, current: 99, trim: true, want: "[===============================================================================================>-]", }, { style: BarStyle(), name: "t,c{100,100}", total: 100, current: 100, want: " [===============================================================================================] ", }, { style: BarStyle(), name: "t,c{100,100}trim", total: 100, current: 100, trim: true, want: "[=================================================================================================]", }, { style: BarStyle(), name: "t,c,r{100,100,99}", total: 100, current: 100, refill: 99, want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=] ", }, { style: BarStyle(), name: "t,c,r{100,100,99}trim", total: 100, current: 100, refill: 99, trim: true, want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=]", }, { style: BarStyle().Reverse(), name: "t,c,r{100,100,99}rev", total: 100, current: 100, refill: 99, want: " [=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Reverse(), name: "t,c,r{100,100,99}trim,rev", total: 100, current: 100, refill: 99, trim: true, want: "[=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", }, { style: BarStyle(), name: "t,c,r{100,100,100}", total: 100, current: 100, refill: 100, want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", }, { style: BarStyle(), name: "t,c,r{100,100,100}trim", total: 100, current: 100, refill: 100, trim: true, want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", }, { style: BarStyle().Reverse(), name: "t,c,r{100,100,100}rev", total: 100, current: 100, refill: 100, want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Reverse(), name: "t,c,r{100,100,100}trim", total: 100, current: 100, refill: 100, trim: true, want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", }, }, 100: { { style: BarStyle(), name: "t,c{100,0}", total: 100, current: 0, want: " [------------------------------------------------------------------------------------------------] ", }, { style: BarStyle(), name: "t,c{100,0}trim", total: 100, current: 0, trim: true, want: "[--------------------------------------------------------------------------------------------------]", }, { style: BarStyle(), name: "t,c{100,1}", total: 100, current: 1, want: " [>-----------------------------------------------------------------------------------------------] ", }, { style: BarStyle().Tip(""), name: "t,c{100,1}empty_tip", total: 100, current: 1, want: " [=-----------------------------------------------------------------------------------------------] ", }, { style: BarStyle(), name: "t,c{100,1}trim", total: 100, current: 1, trim: true, want: "[>-------------------------------------------------------------------------------------------------]", }, { style: BarStyle(), name: "t,c{100,99}", total: 100, current: 99, want: " [==============================================================================================>-] ", }, { style: BarStyle().Tip(""), name: "t,c{100,99}empty_tip", total: 100, current: 99, want: " [===============================================================================================-] ", }, { style: BarStyle(), name: "t,c{100,99}trim", total: 100, current: 99, trim: true, want: "[================================================================================================>-]", }, { style: BarStyle(), name: "t,c{100,100}", total: 100, current: 100, want: " [================================================================================================] ", }, { style: BarStyle(), name: "t,c{100,100}trim", total: 100, current: 100, trim: true, want: "[==================================================================================================]", }, { style: BarStyle(), name: "t,c,r{100,100,99}", total: 100, current: 100, refill: 99, want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=] ", }, { style: BarStyle(), name: "t,c,r{100,100,99}trim", total: 100, current: 100, refill: 99, trim: true, want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=]", }, { style: BarStyle(), name: "t,c,r{100,100,100}", total: 100, current: 100, refill: 100, want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", }, { style: BarStyle(), name: "t,c,r{100,100,100}trim", total: 100, current: 100, refill: 100, trim: true, want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", }, { style: BarStyle().Reverse(), name: "t,c,r{100,100,99}rev", total: 100, current: 100, refill: 99, want: " [=+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Reverse(), name: "t,c,r{100,100,99}trim,rev", total: 100, current: 100, refill: 99, trim: true, want: "[=+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", }, { style: BarStyle().Reverse(), name: "t,c,r{100,100,100}rev", total: 100, current: 100, refill: 100, want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Reverse(), name: "t,c,r{100,100,100}trim", total: 100, current: 100, refill: 100, trim: true, want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", }, { style: BarStyle(), name: "t,c,r{100,40,33}", total: 100, current: 40, refill: 33, want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ", }, { style: BarStyle(), name: "t,c,r{100,40,33}trim", total: 100, current: 40, refill: 33, trim: true, want: "[++++++++++++++++++++++++++++++++======>-----------------------------------------------------------]", }, { style: BarStyle().Tip("<").Reverse(), name: "t,c,r{100,40,33},rev", total: 100, current: 40, refill: 33, want: " [----------------------------------------------------------<=====++++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Tip("<").Reverse(), name: "t,c,r{100,40,33}trim,rev", total: 100, current: 40, refill: 33, trim: true, want: "[-----------------------------------------------------------<======++++++++++++++++++++++++++++++++]", }, }, } var tmpBuf bytes.Buffer for tw, cases := range testSuite { for _, tc := range cases { ps := pState{reqWidth: tc.barWidth} s := ps.makeBarState(tc.total, tc.style.Build()) s.current = tc.current s.trimSpace = tc.trim s.refill = tc.refill r, err := s.draw(s.newStatistics(tw)) if err != nil { t.Fatalf("tw: %d case %q draw error: %s", tw, tc.name, err.Error()) } tmpBuf.Reset() _, err = tmpBuf.ReadFrom(r) if err != nil { t.FailNow() } by := tmpBuf.Bytes() got := string(by[:len(by)-1]) if !utf8.ValidString(got) { t.Fail() } if got != tc.want { t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", tw, tc.name, tc.want, utf8.RuneCountInString(tc.want), got, utf8.RuneCountInString(got)) } } } } func TestDrawTipOnComplete(t *testing.T) { // key is termWidth testSuite := map[int][]struct { style BarStyleComposer name string total int64 current int64 refill int64 barWidth int trim bool want string }{ 3: { { style: BarStyle().TipOnComplete(), name: "t,c{60,60}", total: 60, current: 60, want: " ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[>]", }, }, 4: { { style: BarStyle().TipOnComplete(), name: "t,c{60,59}", total: 60, current: 59, want: " [] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[=>]", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}", total: 60, current: 60, want: " [] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[=>]", }, }, 5: { { style: BarStyle().TipOnComplete(), name: "t,c{60,59}", total: 60, current: 59, want: " [>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[==>]", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}", total: 60, current: 60, want: " [>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[==>]", }, }, 6: { { style: BarStyle().TipOnComplete(), name: "t,c{60,59}", total: 60, current: 59, want: " [=>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[===>]", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}", total: 60, current: 60, want: " [=>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[===>]", }, }, 7: { { style: BarStyle().TipOnComplete(), name: "t,c{60,59}", total: 60, current: 59, want: " [==>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[====>]", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}", total: 60, current: 60, want: " [==>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[====>]", }, }, 8: { { style: BarStyle().TipOnComplete(), name: "t,c{60,59}", total: 60, current: 59, want: " [===>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[=====>]", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}", total: 60, current: 60, want: " [===>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[=====>]", }, }, 80: { { style: BarStyle().TipOnComplete(), name: "t,c{60,59}", total: 60, current: 59, want: " [==========================================================================>-] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,59}trim", total: 60, current: 59, trim: true, want: "[============================================================================>-]", }, { style: BarStyle().TipOnComplete(), name: "t,c,bw{60,59,60}", total: 60, current: 59, barWidth: 60, want: " [========================================================>-] ", }, { style: BarStyle().TipOnComplete(), name: "t,c,bw{60,59,60}trim", total: 60, current: 59, barWidth: 60, trim: true, want: "[========================================================>-]", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}", total: 60, current: 60, want: " [===========================================================================>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{60,60}trim", total: 60, current: 60, trim: true, want: "[=============================================================================>]", }, { style: BarStyle().TipOnComplete(), name: "t,c,bw{60,60,60}", total: 60, current: 60, barWidth: 60, want: " [=========================================================>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c,bw{60,60,60}trim", total: 60, current: 60, barWidth: 60, trim: true, want: "[=========================================================>]", }, }, 99: { { style: BarStyle().TipOnComplete(), name: "t,c{100,99}", total: 100, current: 99, want: " [=============================================================================================>-] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{100,99}trim", total: 100, current: 99, trim: true, want: "[===============================================================================================>-]", }, { style: BarStyle().TipOnComplete(), name: "t,c{100,100}", total: 100, current: 100, want: " [==============================================================================================>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{100,100}trim", total: 100, current: 100, trim: true, want: "[================================================================================================>]", }, { style: BarStyle().TipOnComplete(), name: "t,c,r{100,100,99}", total: 100, current: 100, refill: 99, want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c,r{100,100,99}trim", total: 100, current: 100, refill: 99, trim: true, want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", }, { style: BarStyle().Tip("<").TipOnComplete().Reverse(), name: `t,c,r{100,100,99}.Tip("<").TipOnComplete().Reverse()`, total: 100, current: 100, refill: 99, want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Tip("<").TipOnComplete().Reverse(), name: `t,c,r{100,100,99}.Tip("<").TipOnComplete().Reverse()trim`, total: 100, current: 100, refill: 99, trim: true, want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", }, { style: BarStyle().TipOnComplete(), name: "t,c,r{100,100,100}", total: 100, current: 100, refill: 100, want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c,r{100,100,100}trim", total: 100, current: 100, refill: 100, trim: true, want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", }, { style: BarStyle().Tip("<").TipOnComplete().Reverse(), name: `t,c,r{100,100,100}.Tip("<").TipOnComplete().Reverse()`, total: 100, current: 100, refill: 100, want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Tip("<").TipOnComplete().Reverse(), name: `t,c,r{100,100,100}.Tip("<").TipOnComplete().Reverse()trim`, total: 100, current: 100, refill: 100, trim: true, want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", }, }, 100: { { style: BarStyle().TipOnComplete(), name: "t,c{100,99}", total: 100, current: 99, want: " [==============================================================================================>-] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{100,99}trim", total: 100, current: 99, trim: true, want: "[================================================================================================>-]", }, { style: BarStyle().TipOnComplete(), name: "t,c{100,100}", total: 100, current: 100, want: " [===============================================================================================>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c{100,100}trim", total: 100, current: 100, trim: true, want: "[=================================================================================================>]", }, { style: BarStyle().TipOnComplete(), name: "t,c,r{100,100,99}", total: 100, current: 100, refill: 99, want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c,r{100,100,99}trim", total: 100, current: 100, refill: 99, trim: true, want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", }, { style: BarStyle().TipOnComplete(), name: "t,c,r{100,100,100}", total: 100, current: 100, refill: 100, want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ", }, { style: BarStyle().TipOnComplete(), name: "t,c,r{100,100,100}trim", total: 100, current: 100, refill: 100, trim: true, want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", }, }, } var tmpBuf bytes.Buffer for tw, cases := range testSuite { for _, tc := range cases { ps := pState{reqWidth: tc.barWidth} s := ps.makeBarState(tc.total, tc.style.Build()) s.current = tc.current s.trimSpace = tc.trim s.refill = tc.refill r, err := s.draw(s.newStatistics(tw)) if err != nil { t.Fatalf("tw: %d case %q draw error: %s", tw, tc.name, err.Error()) } tmpBuf.Reset() _, err = tmpBuf.ReadFrom(r) if err != nil { t.FailNow() } by := tmpBuf.Bytes() got := string(by[:len(by)-1]) if !utf8.ValidString(got) { t.Fail() } if got != tc.want { t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", tw, tc.name, tc.want, utf8.RuneCountInString(tc.want), got, utf8.RuneCountInString(got)) } } } } func TestDrawDoubleWidth(t *testing.T) { // key is termWidth testSuite := map[int][]struct { style BarStyleComposer name string total int64 current int64 refill int64 barWidth int trim bool want string }{ 99: { { style: BarStyle().Lbound("の").Rbound("の"), name: `t,c{100,1}.Lbound("の").Rbound("の")`, total: 100, current: 1, want: " の>--------------------------------------------------------------------------------------------の ", }, { style: BarStyle().Lbound("の").Rbound("の"), name: `t,c{100,1}.Lbound("の").Rbound("の")`, total: 100, current: 2, want: " の=>-------------------------------------------------------------------------------------------の ", }, { style: BarStyle().Tip("だ"), name: `t,c{100,1}Tip("だ")`, total: 100, current: 1, want: " [だ---------------------------------------------------------------------------------------------] ", }, { style: BarStyle().Tip("だ"), name: `t,c{100,2}Tip("だ")`, total: 100, current: 2, want: " [だ---------------------------------------------------------------------------------------------] ", }, { style: BarStyle().Tip("だ"), name: `t,c{100,3}Tip("だ")`, total: 100, current: 3, want: " [=だ--------------------------------------------------------------------------------------------] ", }, { style: BarStyle().Tip("だ"), name: `t,c{100,99}Tip("だ")`, total: 100, current: 99, want: " [============================================================================================だ-] ", }, { style: BarStyle().Tip("だ"), name: `t,c{100,100}Tip("だ")`, total: 100, current: 100, want: " [===============================================================================================] ", }, { style: BarStyle().Tip("だ").TipOnComplete(), name: `t,c{100,100}.Tip("だ").TipOnComplete()`, total: 100, current: 100, want: " [=============================================================================================だ] ", }, { style: BarStyle().Filler("の").Tip("だ").Padding("つ"), name: `t,c{100,1}Filler("の").Tip("だ").Padding("つ")`, total: 100, current: 1, want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", }, { style: BarStyle().Filler("の").Tip("だ").Padding("つ"), name: `t,c{100,2}Filler("の").Tip("だ").Padding("つ")`, total: 100, current: 2, want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", }, { style: BarStyle().Filler("の").Tip("だ").Padding("つ"), name: `t,c{100,99}Filler("の").Tip("だ").Padding("つ")`, total: 100, current: 99, want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ", }, { style: BarStyle().Filler("の").Tip("だ").Padding("つ"), name: `t,c{100,100}.Filler("の").Tip("だ").Padding("つ")`, total: 100, current: 100, want: " [ののののののののののののののののののののののののののののののののののののののののののののののの…] ", }, { style: BarStyle().Filler("の").Tip("だ").Padding("つ").Reverse(), name: `t,c{100,100}Filler("の").Tip("だ").Padding("つ").Reverse()`, total: 100, current: 100, want: " […ののののののののののののののののののののののののののののののののののののののののののののののの] ", }, { style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ"), name: `t,c{100,99}Filler("の").Tip("だ").TipOnComplete().Padding("つ")`, total: 100, current: 99, want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ", }, { style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ"), name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete().Padding("つ")`, total: 100, current: 100, want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ", }, { style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ").Reverse(), name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete().Padding("つ").Reverse()`, total: 100, current: 100, want: " […だのののののののののののののののののののののののののののののののののののののののののののののの] ", }, { style: BarStyle().Refiller("の"), name: `t,c,r{100,100,99}Refiller("の")`, total: 100, current: 100, refill: 99, want: " [ののののののののののののののののののののののののののののののののののののののののののののののの=] ", }, { style: BarStyle().Refiller("の"), name: `t,c,r{100,100,99}Refiller("の")trim`, total: 100, current: 100, refill: 99, trim: true, want: "[のののののののののののののののののののののののののののののののののののののののののののののののの=]", }, }, } var tmpBuf bytes.Buffer for tw, cases := range testSuite { for _, tc := range cases { ps := pState{reqWidth: tc.barWidth} s := ps.makeBarState(tc.total, tc.style.Build()) s.current = tc.current s.trimSpace = tc.trim s.refill = tc.refill r, err := s.draw(s.newStatistics(tw)) if err != nil { t.Fatalf("tw: %d case %q draw error: %s", tw, tc.name, err.Error()) } tmpBuf.Reset() _, err = tmpBuf.ReadFrom(r) if err != nil { t.FailNow() } by := tmpBuf.Bytes() got := string(by[:len(by)-1]) if !utf8.ValidString(got) { t.Fail() } if got != tc.want { t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", tw, tc.name, tc.want, utf8.RuneCountInString(tc.want), got, utf8.RuneCountInString(got)) } } } } mpb-8.8.3/example_test.go000066400000000000000000000033001466312757300153430ustar00rootroot00000000000000package mpb_test import ( crand "crypto/rand" "io" "math/rand" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func Example() { // initialize progress container, with custom width p := mpb.New(mpb.WithWidth(64)) total := 100 name := "Single Bar:" // create a single bar, which will inherit container's width bar := p.New(int64(total), // BarFillerBuilder with custom style mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"), mpb.PrependDecorators( // display our name with one space on the right decor.Name(name, decor.WC{C: decor.DindentRight | decor.DextraSpace}), // replace ETA decorator with "done" message, OnComplete event decor.OnComplete(decor.AverageETA(decor.ET_STYLE_GO), "done"), ), mpb.AppendDecorators(decor.Percentage()), ) // simulating some work max := 100 * time.Millisecond for i := 0; i < total; i++ { time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } // wait for our bar to complete and flush p.Wait() } func ExampleBar_Completed() { p := mpb.New() bar := p.AddBar(100) max := 100 * time.Millisecond for !bar.Completed() { time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) bar.Increment() } p.Wait() } func ExampleBar_ProxyReader() { // import crand "crypto/rand" var total int64 = 1024 * 1024 * 500 reader := io.LimitReader(crand.Reader, total) p := mpb.New() bar := p.AddBar(total, mpb.AppendDecorators( decor.CountersKibiByte("% .2f / % .2f"), ), ) // create proxy reader proxyReader := bar.ProxyReader(reader) defer proxyReader.Close() // and copy from reader, ignoring errors _, _ = io.Copy(io.Discard, proxyReader) p.Wait() } mpb-8.8.3/export_test.go000066400000000000000000000001611466312757300152330ustar00rootroot00000000000000package mpb // make syncWidth func public in test var SyncWidth = syncWidth type PriorityQueue = priorityQueue mpb-8.8.3/go.mod000066400000000000000000000004241466312757300134340ustar00rootroot00000000000000module github.com/vbauerster/mpb/v8 require ( github.com/VividCortex/ewma v1.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/mattn/go-runewidth v0.0.16 golang.org/x/sys v0.24.0 ) require github.com/rivo/uniseg v0.4.7 // indirect go 1.17 mpb-8.8.3/go.sum000066400000000000000000000017321466312757300134640ustar00rootroot00000000000000github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= mpb-8.8.3/heap_manager.go000066400000000000000000000063101466312757300152640ustar00rootroot00000000000000package mpb import "container/heap" type heapManager chan heapRequest type heapCmd int const ( h_sync heapCmd = iota h_push h_iter h_drain h_fix h_state h_end ) type heapRequest struct { cmd heapCmd data interface{} } type iterData struct { iter chan<- *Bar drop <-chan struct{} } type pushData struct { bar *Bar sync bool } type fixData struct { bar *Bar priority int lazy bool } func (m heapManager) run() { var bHeap priorityQueue var pMatrix, aMatrix map[int][]chan int var l int var sync bool for req := range m { switch req.cmd { case h_push: data := req.data.(pushData) heap.Push(&bHeap, data.bar) if !sync { sync = data.sync } case h_sync: if sync || l != bHeap.Len() { pMatrix = make(map[int][]chan int) aMatrix = make(map[int][]chan int) for _, b := range bHeap { table := b.wSyncTable() for i, ch := range table[0] { pMatrix[i] = append(pMatrix[i], ch) } for i, ch := range table[1] { aMatrix[i] = append(aMatrix[i], ch) } } sync = false l = bHeap.Len() } drop := req.data.(<-chan struct{}) syncWidth(pMatrix, drop) syncWidth(aMatrix, drop) case h_iter: data := req.data.(iterData) drop_iter: for _, b := range bHeap { select { case data.iter <- b: case <-data.drop: break drop_iter } } close(data.iter) case h_drain: data := req.data.(iterData) drop_drain: for bHeap.Len() != 0 { select { case data.iter <- heap.Pop(&bHeap).(*Bar): case <-data.drop: break drop_drain } } close(data.iter) case h_fix: data := req.data.(fixData) if data.bar.index < 0 { break } data.bar.priority = data.priority if !data.lazy { heap.Fix(&bHeap, data.bar.index) } case h_state: ch := req.data.(chan<- bool) ch <- sync || l != bHeap.Len() case h_end: ch := req.data.(chan<- interface{}) if ch != nil { go func() { ch <- []*Bar(bHeap) }() } close(m) } } } func (m heapManager) sync(drop <-chan struct{}) { m <- heapRequest{cmd: h_sync, data: drop} } func (m heapManager) push(b *Bar, sync bool) { data := pushData{b, sync} m <- heapRequest{cmd: h_push, data: data} } func (m heapManager) iter(iter chan<- *Bar, drop <-chan struct{}) { data := iterData{iter, drop} m <- heapRequest{cmd: h_iter, data: data} } func (m heapManager) drain(iter chan<- *Bar, drop <-chan struct{}) { data := iterData{iter, drop} m <- heapRequest{cmd: h_drain, data: data} } func (m heapManager) fix(b *Bar, priority int, lazy bool) { data := fixData{b, priority, lazy} m <- heapRequest{cmd: h_fix, data: data} } func (m heapManager) state(ch chan<- bool) { m <- heapRequest{cmd: h_state, data: ch} } func (m heapManager) end(ch chan<- interface{}) { m <- heapRequest{cmd: h_end, data: ch} } func syncWidth(matrix map[int][]chan int, drop <-chan struct{}) { for _, column := range matrix { go maxWidthDistributor(column, drop) } } func maxWidthDistributor(column []chan int, drop <-chan struct{}) { var maxWidth int for _, ch := range column { select { case w := <-ch: if w > maxWidth { maxWidth = w } case <-drop: return } } for _, ch := range column { ch <- maxWidth } } mpb-8.8.3/internal/000077500000000000000000000000001466312757300141425ustar00rootroot00000000000000mpb-8.8.3/internal/percentage.go000066400000000000000000000010051466312757300166020ustar00rootroot00000000000000package internal import "math" // Percentage is a helper function, to calculate percentage. func Percentage(total, current, width uint) float64 { if total == 0 { return 0 } if current >= total { return float64(width) } return float64(width*current) / float64(total) } // PercentageRound same as Percentage but with math.Round. func PercentageRound(total, current int64, width uint) float64 { if total < 0 || current < 0 { return 0 } return math.Round(Percentage(uint(total), uint(current), width)) } mpb-8.8.3/internal/percentage_test.go000066400000000000000000000041131466312757300176440ustar00rootroot00000000000000package internal import "testing" func TestPercentage(t *testing.T) { // key is barWidth testSuite := map[uint][]struct { name string total int64 current int64 expected int64 }{ 100: { {"t,c,e{-1,-1,0}", -1, -1, 0}, {"t,c,e{0,-1,0}", 0, -1, 0}, {"t,c,e{0,0,0}", 0, 0, 0}, {"t,c,e{0,1,0}", 0, 1, 0}, {"t,c,e{100,0,0}", 100, 0, 0}, {"t,c,e{100,10,10}", 100, 10, 10}, {"t,c,e{100,15,15}", 100, 15, 15}, {"t,c,e{100,50,50}", 100, 50, 50}, {"t,c,e{100,99,99}", 100, 99, 99}, {"t,c,e{100,100,100}", 100, 100, 100}, {"t,c,e{100,101,101}", 100, 101, 100}, {"t,c,e{120,0,0}", 120, 0, 0}, {"t,c,e{120,10,8}", 120, 10, 8}, {"t,c,e{120,15,13}", 120, 15, 13}, {"t,c,e{120,50,42}", 120, 50, 42}, {"t,c,e{120,60,50}", 120, 60, 50}, {"t,c,e{120,99,83}", 120, 99, 83}, {"t,c,e{120,101,84}", 120, 101, 84}, {"t,c,e{120,118,98}", 120, 118, 98}, {"t,c,e{120,119,99}", 120, 119, 99}, {"t,c,e{120,120,100}", 120, 120, 100}, {"t,c,e{120,121,101}", 120, 121, 100}, }, 80: { {"t,c,e{-1,-1,0}", -1, -1, 0}, {"t,c,e{0,-1,0}", 0, -1, 0}, {"t,c,e{0,0,0}", 0, 0, 0}, {"t,c,e{0,1,0}", 0, 1, 0}, {"t,c,e{100,0,0}", 100, 0, 0}, {"t,c,e{100,10,8}", 100, 10, 8}, {"t,c,e{100,15,12}", 100, 15, 12}, {"t,c,e{100,50,40}", 100, 50, 40}, {"t,c,e{100,99,79}", 100, 99, 79}, {"t,c,e{100,100,80}", 100, 100, 80}, {"t,c,e{100,101,81}", 100, 101, 80}, {"t,c,e{120,0,0}", 120, 0, 0}, {"t,c,e{120,10,7}", 120, 10, 7}, {"t,c,e{120,15,10}", 120, 15, 10}, {"t,c,e{120,50,33}", 120, 50, 33}, {"t,c,e{120,60,40}", 120, 60, 40}, {"t,c,e{120,99,66}", 120, 99, 66}, {"t,c,e{120,101,67}", 120, 101, 67}, {"t,c,e{120,118,79}", 120, 118, 79}, {"t,c,e{120,119,79}", 120, 119, 79}, {"t,c,e{120,120,80}", 120, 120, 80}, {"t,c,e{120,121,81}", 120, 121, 80}, }, } for width, cases := range testSuite { for _, tc := range cases { got := int64(PercentageRound(tc.total, tc.current, width)) if got != tc.expected { t.Errorf("width %d; %s: Expected: %d, got: %d\n", width, tc.name, tc.expected, got) } } } } mpb-8.8.3/internal/width.go000066400000000000000000000003711466312757300156110ustar00rootroot00000000000000package internal // CheckRequestedWidth checks that requested width doesn't overflow // available width func CheckRequestedWidth(requested, available int) int { if requested < 1 || requested > available { return available } return requested } mpb-8.8.3/priority_queue.go000066400000000000000000000012751466312757300157470ustar00rootroot00000000000000package mpb import "container/heap" var _ heap.Interface = (*priorityQueue)(nil) type priorityQueue []*Bar func (pq priorityQueue) Len() int { return len(pq) } func (pq priorityQueue) Less(i, j int) bool { // greater priority pops first return pq[i].priority > pq[j].priority } func (pq priorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] pq[i].index = i pq[j].index = j } func (pq *priorityQueue) Push(x interface{}) { n := len(*pq) bar := x.(*Bar) bar.index = n *pq = append(*pq, bar) } func (pq *priorityQueue) Pop() interface{} { old := *pq n := len(old) bar := old[n-1] old[n-1] = nil // avoid memory leak bar.index = -1 // for safety *pq = old[:n-1] return bar } mpb-8.8.3/progress.go000066400000000000000000000252111466312757300145220ustar00rootroot00000000000000package mpb import ( "bytes" "context" "fmt" "io" "math" "os" "sync" "time" "github.com/vbauerster/mpb/v8/cwriter" "github.com/vbauerster/mpb/v8/decor" ) const defaultRefreshRate = 150 * time.Millisecond // DoneError represents use after `(*Progress).Wait()` error. var DoneError = fmt.Errorf("%T instance can't be reused after %[1]T.Wait()", (*Progress)(nil)) // Progress represents a container that renders one or more progress bars. type Progress struct { uwg *sync.WaitGroup pwg, bwg sync.WaitGroup operateState chan func(*pState) interceptIO chan func(io.Writer) done <-chan struct{} cancel func() } // pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine. type pState struct { ctx context.Context hm heapManager dropS, dropD chan struct{} renderReq chan time.Time idCount int popPriority int // following are provided/overrided by user refreshRate time.Duration reqWidth int popCompleted bool autoRefresh bool delayRC <-chan struct{} manualRC <-chan interface{} shutdownNotifier chan<- interface{} queueBars map[*Bar]*Bar output io.Writer debugOut io.Writer uwg *sync.WaitGroup } // New creates new Progress container instance. It's not possible to // reuse instance after `(*Progress).Wait` method has been called. func New(options ...ContainerOption) *Progress { return NewWithContext(context.Background(), options...) } // NewWithContext creates new Progress container instance with provided // context. It's not possible to reuse instance after `(*Progress).Wait` // method has been called. func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress { if ctx == nil { ctx = context.Background() } ctx, cancel := context.WithCancel(ctx) s := &pState{ ctx: ctx, hm: make(heapManager), dropS: make(chan struct{}), dropD: make(chan struct{}), renderReq: make(chan time.Time), popPriority: math.MinInt32, refreshRate: defaultRefreshRate, queueBars: make(map[*Bar]*Bar), output: os.Stdout, debugOut: io.Discard, } for _, opt := range options { if opt != nil { opt(s) } } p := &Progress{ uwg: s.uwg, operateState: make(chan func(*pState)), interceptIO: make(chan func(io.Writer)), cancel: cancel, } cw := cwriter.New(s.output) if s.manualRC != nil { done := make(chan struct{}) p.done = done s.autoRefresh = false go s.manualRefreshListener(done) } else if cw.IsTerminal() || s.autoRefresh { done := make(chan struct{}) p.done = done s.autoRefresh = true go s.autoRefreshListener(done) } else { p.done = ctx.Done() s.autoRefresh = false } p.pwg.Add(1) go p.serve(s, cw) go s.hm.run() return p } // AddBar creates a bar with default bar filler. func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { return p.New(total, BarStyle(), options...) } // AddSpinner creates a bar with default spinner filler. func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar { return p.New(total, SpinnerStyle(), options...) } // New creates a bar by calling `Build` method on provided `BarFillerBuilder`. func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar { if builder == nil { return p.MustAdd(total, nil, options...) } return p.MustAdd(total, builder.Build(), options...) } // MustAdd creates a bar which renders itself by provided BarFiller. // If `total <= 0` triggering complete event by increment methods is // disabled. Panics if called after `(*Progress).Wait()`. func (p *Progress) MustAdd(total int64, filler BarFiller, options ...BarOption) *Bar { bar, err := p.Add(total, filler, options...) if err != nil { panic(err) } return bar } // Add creates a bar which renders itself by provided BarFiller. // If `total <= 0` triggering complete event by increment methods // is disabled. If called after `(*Progress).Wait()` then // `(nil, DoneError)` is returned. func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) (*Bar, error) { if filler == nil { filler = NopStyle().Build() } else if f, ok := filler.(BarFillerFunc); ok && f == nil { filler = NopStyle().Build() } ch := make(chan *Bar) select { case p.operateState <- func(ps *pState) { bs := ps.makeBarState(total, filler, options...) bar := newBar(ps.ctx, p, bs) if bs.waitBar != nil { ps.queueBars[bs.waitBar] = bar } else { ps.hm.push(bar, true) } ps.idCount++ ch <- bar }: return <-ch, nil case <-p.done: return nil, DoneError } } func (p *Progress) traverseBars(cb func(b *Bar) bool) { iter, drop := make(chan *Bar), make(chan struct{}) select { case p.operateState <- func(s *pState) { s.hm.iter(iter, drop) }: for b := range iter { if !cb(b) { close(drop) break } } case <-p.done: } } // UpdateBarPriority either immediately or lazy. // With lazy flag order is updated after the next refresh cycle. // If you don't care about laziness just use `(*Bar).SetPriority(int)`. func (p *Progress) UpdateBarPriority(b *Bar, priority int, lazy bool) { if b == nil { return } select { case p.operateState <- func(s *pState) { s.hm.fix(b, priority, lazy) }: case <-p.done: } } // Write is implementation of io.Writer. // Writing to `*Progress` will print lines above a running bar. // Writes aren't flushed immediately, but at next refresh cycle. // If called after `(*Progress).Wait()` then `(0, DoneError)` // is returned. func (p *Progress) Write(b []byte) (int, error) { type result struct { n int err error } ch := make(chan result) select { case p.interceptIO <- func(w io.Writer) { n, err := w.Write(b) ch <- result{n, err} }: res := <-ch return res.n, res.err case <-p.done: return 0, DoneError } } // Wait waits for all bars to complete and finally shutdowns container. After // this method has been called, there is no way to reuse `*Progress` instance. func (p *Progress) Wait() { p.bwg.Wait() p.Shutdown() // wait for user wg, if any if p.uwg != nil { p.uwg.Wait() } } // Shutdown cancels any running bar immediately and then shutdowns `*Progress` // instance. Normally this method shouldn't be called unless you know what you // are doing. Proper way to shutdown is to call `(*Progress).Wait()` instead. func (p *Progress) Shutdown() { p.cancel() p.pwg.Wait() } func (p *Progress) serve(s *pState, cw *cwriter.Writer) { defer p.pwg.Done() var err error var w *cwriter.Writer renderReq := s.renderReq operateState := p.operateState interceptIO := p.interceptIO if s.delayRC != nil { w = cwriter.New(io.Discard) } else { w, cw = cw, nil } for { select { case <-s.delayRC: w, cw = cw, nil s.delayRC = nil case op := <-operateState: op(s) case fn := <-interceptIO: fn(w) case <-renderReq: err = s.render(w) if err != nil { // (*pState).(autoRefreshListener|manualRefreshListener) may block // if not launching following short lived goroutine go func() { for { select { case <-s.renderReq: case <-p.done: return } } }() p.cancel() // cancel all bars renderReq = nil operateState = nil interceptIO = nil } case <-p.done: if err != nil { _, _ = fmt.Fprintln(s.debugOut, err.Error()) } else if s.autoRefresh { update := make(chan bool) for i := 0; i == 0 || <-update; i++ { if err := s.render(w); err != nil { _, _ = fmt.Fprintln(s.debugOut, err.Error()) break } s.hm.state(update) } } s.hm.end(s.shutdownNotifier) return } } } func (s *pState) autoRefreshListener(done chan struct{}) { ticker := time.NewTicker(s.refreshRate) defer ticker.Stop() for { select { case t := <-ticker.C: s.renderReq <- t case <-s.ctx.Done(): close(done) return } } } func (s *pState) manualRefreshListener(done chan struct{}) { for { select { case x := <-s.manualRC: if t, ok := x.(time.Time); ok { s.renderReq <- t } else { s.renderReq <- time.Now() } case <-s.ctx.Done(): close(done) return } } } func (s *pState) render(cw *cwriter.Writer) (err error) { s.hm.sync(s.dropS) iter := make(chan *Bar) go s.hm.iter(iter, s.dropS) var width, height int if cw.IsTerminal() { width, height, err = cw.GetTermSize() if err != nil { close(s.dropS) return err } } else { if s.reqWidth > 0 { width = s.reqWidth } else { width = 80 } height = width } for b := range iter { go b.render(width) } return s.flush(cw, height) } func (s *pState) flush(cw *cwriter.Writer, height int) error { var wg sync.WaitGroup defer wg.Wait() // waiting for all s.push to complete var popCount int var rows []io.Reader iter := make(chan *Bar) s.hm.drain(iter, s.dropD) for b := range iter { frame := <-b.frameCh if frame.err != nil { close(s.dropD) b.cancel() return frame.err // b.frameCh is buffered it's ok to return here } var usedRows int for i := len(frame.rows) - 1; i >= 0; i-- { if row := frame.rows[i]; len(rows) < height { rows = append(rows, row) usedRows++ } else { _, _ = io.Copy(io.Discard, row) } } switch frame.shutdown { case 1: b.cancel() if qb, ok := s.queueBars[b]; ok { delete(s.queueBars, b) qb.priority = b.priority wg.Add(1) go s.push(&wg, qb, true) } else if s.popCompleted && !frame.noPop { b.priority = s.popPriority s.popPriority++ wg.Add(1) go s.push(&wg, b, false) } else if !frame.rmOnComplete { wg.Add(1) go s.push(&wg, b, false) } case 2: if s.popCompleted && !frame.noPop { popCount += usedRows continue } fallthrough default: wg.Add(1) go s.push(&wg, b, false) } } for i := len(rows) - 1; i >= 0; i-- { _, err := cw.ReadFrom(rows[i]) if err != nil { return err } } return cw.Flush(len(rows) - popCount) } func (s *pState) push(wg *sync.WaitGroup, b *Bar, sync bool) { s.hm.push(b, sync) wg.Done() } func (s pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState { bs := &bState{ id: s.idCount, priority: s.idCount, reqWidth: s.reqWidth, total: total, filler: filler, renderReq: s.renderReq, autoRefresh: s.autoRefresh, extender: func(_ decor.Statistics, rows ...io.Reader) ([]io.Reader, error) { return rows, nil }, } if total > 0 { bs.triggerComplete = true } for _, opt := range options { if opt != nil { opt(bs) } } bs.buffers[0] = bytes.NewBuffer(make([]byte, 0, 128)) // prepend bs.buffers[1] = bytes.NewBuffer(make([]byte, 0, 128)) // append bs.buffers[2] = bytes.NewBuffer(make([]byte, 0, 256)) // filler return bs } mpb-8.8.3/progress_test.go000066400000000000000000000141401466312757300155600ustar00rootroot00000000000000package mpb_test import ( "bytes" "container/heap" "context" "errors" "io" "strings" "testing" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) const ( timeout = 300 * time.Millisecond ) func TestWithContext(t *testing.T) { shutdown := make(chan interface{}) ctx, cancel := context.WithCancel(context.Background()) p := mpb.NewWithContext(ctx, mpb.WithOutput(io.Discard), mpb.WithShutdownNotifier(shutdown), ) _ = p.AddBar(0) // never complete bar _ = p.AddBar(0) // never complete bar go func() { time.Sleep(10 * time.Millisecond) cancel() }() p.Wait() select { case v := <-shutdown: if l := len(v.([]*mpb.Bar)); l != 2 { t.Errorf("Expected len of bars: %d, got: %d", 2, l) } case <-time.After(timeout): t.Errorf("Progress didn't shutdown after %v", timeout) } } func TestShutdownsWithErrFiller(t *testing.T) { var debug bytes.Buffer shutdown := make(chan interface{}) p := mpb.New( mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(io.Discard), mpb.WithDebugOutput(&debug), mpb.WithAutoRefresh(), ) var errReturnCount int testError := errors.New("test error") bar := p.AddBar(100, mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller { return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error { if st.Current >= 22 { errReturnCount++ return testError } return base.Fill(w, st) }) }), ) go func() { for bar.IsRunning() { bar.Increment() time.Sleep(10 * time.Millisecond) } }() p.Wait() if errReturnCount != 1 { t.Errorf("Expected errReturnCount: %d, got: %d\n", 1, errReturnCount) } select { case v := <-shutdown: if l := len(v.([]*mpb.Bar)); l != 0 { t.Errorf("Expected len of bars: %d, got: %d\n", 0, l) } if err := strings.TrimSpace(debug.String()); err != testError.Error() { t.Errorf("Expected err: %q, got %q\n", testError.Error(), err) } case <-time.After(timeout): t.Errorf("Progress didn't shutdown after %v", timeout) } } func TestShutdownAfterBarAbortWithDrop(t *testing.T) { shutdown := make(chan interface{}) p := mpb.New( mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(io.Discard), mpb.WithAutoRefresh(), ) b := p.AddBar(100) var count int for i := 0; !b.Aborted(); i++ { if i >= 10 { count++ b.Abort(true) } else { b.Increment() time.Sleep(10 * time.Millisecond) } } p.Wait() if count != 1 { t.Errorf("Expected count: %d, got: %d", 1, count) } select { case v := <-shutdown: if l := len(v.([]*mpb.Bar)); l != 0 { t.Errorf("Expected len of bars: %d, got: %d", 0, l) } case <-time.After(timeout): t.Errorf("Progress didn't shutdown after %v", timeout) } } func TestShutdownAfterBarAbortWithNoDrop(t *testing.T) { shutdown := make(chan interface{}) p := mpb.New( mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(io.Discard), mpb.WithAutoRefresh(), ) b := p.AddBar(100) var count int for i := 0; !b.Aborted(); i++ { if i >= 10 { count++ b.Abort(false) } else { b.Increment() time.Sleep(10 * time.Millisecond) } } p.Wait() if count != 1 { t.Errorf("Expected count: %d, got: %d", 1, count) } select { case v := <-shutdown: if l := len(v.([]*mpb.Bar)); l != 1 { t.Errorf("Expected len of bars: %d, got: %d", 1, l) } case <-time.After(timeout): t.Errorf("Progress didn't shutdown after %v", timeout) } } func TestBarPristinePopOrder(t *testing.T) { shutdown := make(chan interface{}) ctx, cancel := context.WithCancel(context.Background()) p := mpb.NewWithContext(ctx, mpb.WithOutput(io.Discard), // auto refresh is disabled mpb.WithShutdownNotifier(shutdown), ) a := p.AddBar(100, mpb.BarPriority(1), mpb.BarID(1)) b := p.AddBar(100, mpb.BarPriority(2), mpb.BarID(2)) c := p.AddBar(100, mpb.BarPriority(3), mpb.BarID(3)) pristineOrder := []*mpb.Bar{c, b, a} go cancel() bars := (<-shutdown).([]*mpb.Bar) if l := len(bars); l != 3 { t.Fatalf("Expected len of bars: %d, got: %d", 3, l) } p.Wait() pq := mpb.PriorityQueue(bars) for _, b := range pristineOrder { // higher priority pops first if bar := heap.Pop(&pq).(*mpb.Bar); bar.ID() != b.ID() { t.Errorf("Expected bar id: %d, got bar id: %d", b.ID(), bar.ID()) } } } func makeUpdateBarPriorityTest(refresh, lazy bool) func(*testing.T) { return func(t *testing.T) { shutdown := make(chan interface{}) refreshCh := make(chan interface{}) ctx, cancel := context.WithCancel(context.Background()) p := mpb.NewWithContext(ctx, mpb.WithOutput(io.Discard), mpb.WithManualRefresh(refreshCh), mpb.WithShutdownNotifier(shutdown), ) a := p.AddBar(100, mpb.BarPriority(1), mpb.BarID(1)) b := p.AddBar(100, mpb.BarPriority(2), mpb.BarID(2)) c := p.AddBar(100, mpb.BarPriority(3), mpb.BarID(3)) p.UpdateBarPriority(c, 2, lazy) p.UpdateBarPriority(b, 3, lazy) checkOrder := []*mpb.Bar{b, c, a} // updated order if refresh { refreshCh <- time.Now() } else if lazy { checkOrder = []*mpb.Bar{c, b, a} // pristine order } go cancel() bars := (<-shutdown).([]*mpb.Bar) if l := len(bars); l != 3 { t.Fatalf("Expected len of bars: %d, got: %d", 3, l) } p.Wait() pq := mpb.PriorityQueue(bars) for _, b := range checkOrder { // higher priority pops first if bar := heap.Pop(&pq).(*mpb.Bar); bar.ID() != b.ID() { t.Errorf("Expected bar id: %d, got bar id: %d", b.ID(), bar.ID()) } } } } func TestUpdateBarPriority(t *testing.T) { makeUpdateBarPriorityTest(false, false)(t) makeUpdateBarPriorityTest(true, false)(t) } func TestUpdateBarPriorityLazy(t *testing.T) { makeUpdateBarPriorityTest(false, true)(t) makeUpdateBarPriorityTest(true, true)(t) } func TestNoOutput(t *testing.T) { var buf bytes.Buffer p := mpb.New(mpb.WithOutput(&buf)) bar := p.AddBar(100) go func() { for !bar.Completed() { bar.Increment() } }() p.Wait() if buf.Len() != 0 { t.Errorf("Expected buf.Len == 0, got: %d\n", buf.Len()) } } func TestAddAfterDone(t *testing.T) { p := mpb.New(mpb.WithOutput(io.Discard)) bar := p.AddBar(100) bar.IncrBy(100) p.Wait() _, err := p.Add(100, nil) if err != mpb.DoneError { t.Errorf("Expected %q, got: %q\n", mpb.DoneError, err) } } mpb-8.8.3/proxyreader.go000066400000000000000000000024671466312757300152320ustar00rootroot00000000000000package mpb import ( "io" "time" ) type proxyReader struct { io.ReadCloser bar *Bar } func (x proxyReader) Read(p []byte) (int, error) { n, err := x.ReadCloser.Read(p) x.bar.IncrBy(n) return n, err } type proxyWriterTo struct { proxyReader } func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) { n, err := x.ReadCloser.(io.WriterTo).WriteTo(w) x.bar.IncrInt64(n) return n, err } type ewmaProxyReader struct { io.ReadCloser bar *Bar } func (x ewmaProxyReader) Read(p []byte) (int, error) { start := time.Now() n, err := x.ReadCloser.Read(p) x.bar.EwmaIncrBy(n, time.Since(start)) return n, err } type ewmaProxyWriterTo struct { ewmaProxyReader } func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) { start := time.Now() n, err := x.ReadCloser.(io.WriterTo).WriteTo(w) x.bar.EwmaIncrInt64(n, time.Since(start)) return n, err } func newProxyReader(r io.Reader, b *Bar, hasEwma bool) io.ReadCloser { rc := toReadCloser(r) if hasEwma { epr := ewmaProxyReader{rc, b} if _, ok := r.(io.WriterTo); ok { return ewmaProxyWriterTo{epr} } return epr } pr := proxyReader{rc, b} if _, ok := r.(io.WriterTo); ok { return proxyWriterTo{pr} } return pr } func toReadCloser(r io.Reader) io.ReadCloser { if rc, ok := r.(io.ReadCloser); ok { return rc } return io.NopCloser(r) } mpb-8.8.3/proxyreader_test.go000066400000000000000000000046151466312757300162660ustar00rootroot00000000000000package mpb_test import ( "bytes" "io" "strings" "testing" "github.com/vbauerster/mpb/v8" ) const content = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.` type testReader struct { io.Reader called bool } func (r *testReader) Read(p []byte) (n int, err error) { r.called = true return r.Reader.Read(p) } func TestProxyReader(t *testing.T) { p := mpb.New(mpb.WithOutput(io.Discard)) tr := &testReader{strings.NewReader(content), false} bar := p.New(int64(len(content)), mpb.NopStyle()) var buf bytes.Buffer _, err := io.Copy(&buf, bar.ProxyReader(tr)) if err != nil { t.Errorf("io.Copy: %s\n", err.Error()) } p.Wait() if !tr.called { t.Error("Read not called") } if got := buf.String(); got != content { t.Errorf("Expected content: %s, got: %s\n", content, got) } } type testReadCloser struct { io.Reader called bool } func (r *testReadCloser) Close() error { r.called = true return nil } func TestProxyReadCloser(t *testing.T) { p := mpb.New(mpb.WithOutput(io.Discard)) tr := &testReadCloser{strings.NewReader(content), false} bar := p.New(int64(len(content)), mpb.NopStyle()) rc := bar.ProxyReader(tr) _, err := io.Copy(io.Discard, rc) if err != nil { t.Errorf("io.Copy: %s\n", err.Error()) } _ = rc.Close() p.Wait() if !tr.called { t.Error("Close not called") } } type testReaderWriterTo struct { io.Reader called bool } func (r *testReaderWriterTo) WriteTo(w io.Writer) (n int64, err error) { r.called = true return r.Reader.(io.WriterTo).WriteTo(w) } func TestProxyReaderWriterTo(t *testing.T) { p := mpb.New(mpb.WithOutput(io.Discard)) tr := &testReaderWriterTo{strings.NewReader(content), false} bar := p.New(int64(len(content)), mpb.NopStyle()) var buf bytes.Buffer _, err := io.Copy(&buf, bar.ProxyReader(tr)) if err != nil { t.Errorf("io.Copy: %s\n", err.Error()) } p.Wait() if !tr.called { t.Error("WriteTo not called") } if got := buf.String(); got != content { t.Errorf("Expected content: %s, got: %s\n", content, got) } } mpb-8.8.3/proxywriter.go000066400000000000000000000035131466312757300152750ustar00rootroot00000000000000package mpb import ( "io" "time" ) type proxyWriter struct { io.WriteCloser bar *Bar } func (x proxyWriter) Write(p []byte) (int, error) { n, err := x.WriteCloser.Write(p) x.bar.IncrBy(n) return n, err } type proxyReaderFrom struct { proxyWriter } func (x proxyReaderFrom) ReadFrom(r io.Reader) (int64, error) { n, err := x.WriteCloser.(io.ReaderFrom).ReadFrom(r) x.bar.IncrInt64(n) return n, err } type ewmaProxyWriter struct { io.WriteCloser bar *Bar } func (x ewmaProxyWriter) Write(p []byte) (int, error) { start := time.Now() n, err := x.WriteCloser.Write(p) x.bar.EwmaIncrBy(n, time.Since(start)) return n, err } type ewmaProxyReaderFrom struct { ewmaProxyWriter } func (x ewmaProxyReaderFrom) ReadFrom(r io.Reader) (int64, error) { start := time.Now() n, err := x.WriteCloser.(io.ReaderFrom).ReadFrom(r) x.bar.EwmaIncrInt64(n, time.Since(start)) return n, err } func newProxyWriter(w io.Writer, b *Bar, hasEwma bool) io.WriteCloser { wc := toWriteCloser(w) if hasEwma { epw := ewmaProxyWriter{wc, b} if _, ok := w.(io.ReaderFrom); ok { return ewmaProxyReaderFrom{epw} } return epw } pw := proxyWriter{wc, b} if _, ok := w.(io.ReaderFrom); ok { return proxyReaderFrom{pw} } return pw } func toWriteCloser(w io.Writer) io.WriteCloser { if wc, ok := w.(io.WriteCloser); ok { return wc } return toNopWriteCloser(w) } func toNopWriteCloser(w io.Writer) io.WriteCloser { if _, ok := w.(io.ReaderFrom); ok { return nopWriteCloserReaderFrom{w} } return nopWriteCloser{w} } type nopWriteCloser struct { io.Writer } func (nopWriteCloser) Close() error { return nil } type nopWriteCloserReaderFrom struct { io.Writer } func (nopWriteCloserReaderFrom) Close() error { return nil } func (c nopWriteCloserReaderFrom) ReadFrom(r io.Reader) (int64, error) { return c.Writer.(io.ReaderFrom).ReadFrom(r) } mpb-8.8.3/proxywriter_test.go000066400000000000000000000042341466312757300163350ustar00rootroot00000000000000package mpb_test import ( "bytes" "io" "strings" "testing" "github.com/vbauerster/mpb/v8" ) type testWriter struct { io.Writer called bool } func (w *testWriter) Write(p []byte) (n int, err error) { w.called = true return w.Writer.Write(p) } func TestProxyWriter(t *testing.T) { p := mpb.New(mpb.WithOutput(io.Discard)) var buf bytes.Buffer tw := &testWriter{&buf, false} bar := p.New(int64(len(content)), mpb.NopStyle()) _, err := io.Copy(bar.ProxyWriter(tw), strings.NewReader(content)) if err != nil { t.Errorf("io.Copy: %s\n", err.Error()) } p.Wait() if !tw.called { t.Error("Read not called") } if got := buf.String(); got != content { t.Errorf("Expected content: %s, got: %s\n", content, got) } } type testWriteCloser struct { io.Writer called bool } func (w *testWriteCloser) Close() error { w.called = true return nil } func TestProxyWriteCloser(t *testing.T) { p := mpb.New(mpb.WithOutput(io.Discard)) var buf bytes.Buffer tw := &testWriteCloser{&buf, false} bar := p.New(int64(len(content)), mpb.NopStyle()) wc := bar.ProxyWriter(tw) _, err := io.Copy(wc, strings.NewReader(content)) if err != nil { t.Errorf("io.Copy: %s\n", err.Error()) } _ = wc.Close() p.Wait() if !tw.called { t.Error("Close not called") } } type testWriterReadFrom struct { io.Writer called bool } func (w *testWriterReadFrom) ReadFrom(r io.Reader) (n int64, err error) { w.called = true return w.Writer.(io.ReaderFrom).ReadFrom(r) } type dumbReader struct { r io.Reader } func (r dumbReader) Read(p []byte) (int, error) { return r.r.Read(p) } func TestProxyWriterReadFrom(t *testing.T) { p := mpb.New(mpb.WithOutput(io.Discard)) var buf bytes.Buffer tw := &testWriterReadFrom{&buf, false} bar := p.New(int64(len(content)), mpb.NopStyle()) // To trigger ReadFrom, WriteTo needs to be hidden, hence a dumb wrapper dr := dumbReader{strings.NewReader(content)} _, err := io.Copy(bar.ProxyWriter(tw), dr) if err != nil { t.Errorf("io.Copy: %s\n", err.Error()) } p.Wait() if !tw.called { t.Error("ReadFrom not called") } if got := buf.String(); got != content { t.Errorf("Expected content: %s, got: %s\n", content, got) } }