pax_global_header00006660000000000000000000000064144731251300014512gustar00rootroot0000000000000052 comment=9fd271cd98a7a2024bfd5f277fe00a03fc1a74e5 mpb-8.6.1/000077500000000000000000000000001447312513000123045ustar00rootroot00000000000000mpb-8.6.1/.github/000077500000000000000000000000001447312513000136445ustar00rootroot00000000000000mpb-8.6.1/.github/workflows/000077500000000000000000000000001447312513000157015ustar00rootroot00000000000000mpb-8.6.1/.github/workflows/golangci-lint.yml000066400000000000000000000021501447312513000211510ustar00rootroot00000000000000name: 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@v3 - uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} cache: false - uses: golangci/golangci-lint-action@v3 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: working directory, useful for monorepos # working-directory: somedir # 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.6.1/.github/workflows/test.yml000066400000000000000000000007541447312513000174110ustar00rootroot00000000000000name: test on: push: tags: - v* branches: - master - v* 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@v3 - uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} - run: go test -race ./... mpb-8.6.1/.gitignore000066400000000000000000000001731447312513000142750ustar00rootroot00000000000000# Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out mpb-8.6.1/CONTRIBUTING000066400000000000000000000012011447312513000141300ustar00rootroot00000000000000When 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.6.1/README.md000066400000000000000000000101561447312513000135660ustar00rootroot00000000000000# 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{W: len(name) + 1, C: decor.DidentRight}), // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "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.6.1/UNLICENSE000066400000000000000000000022731447312513000135600ustar00rootroot00000000000000This 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.6.1/_examples/000077500000000000000000000000001447312513000142615ustar00rootroot00000000000000mpb-8.6.1/_examples/.gitignore000066400000000000000000000000071447312513000162460ustar00rootroot00000000000000go.sum mpb-8.6.1/_examples/barExtender/000077500000000000000000000000001447312513000165245ustar00rootroot00000000000000mpb-8.6.1/_examples/barExtender/go.mod000066400000000000000000000005741447312513000176400ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/barExtender go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/barExtender/main.go000066400000000000000000000031241447312513000177770ustar00rootroot00000000000000package 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.6.1/_examples/barExtenderRev/000077500000000000000000000000001447312513000172015ustar00rootroot00000000000000mpb-8.6.1/_examples/barExtenderRev/go.mod000066400000000000000000000005771447312513000203200ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/barExtenderRev go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/barExtenderRev/main.go000066400000000000000000000052151447312513000204570ustar00rootroot00000000000000package 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.6.1/_examples/cancel/000077500000000000000000000000001447312513000155065ustar00rootroot00000000000000mpb-8.6.1/_examples/cancel/go.mod000066400000000000000000000005671447312513000166240ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/cancel go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/cancel/main.go000066400000000000000000000025261447312513000167660ustar00rootroot00000000000000package 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.6.1/_examples/complex/000077500000000000000000000000001447312513000157305ustar00rootroot00000000000000mpb-8.6.1/_examples/complex/go.mod000066400000000000000000000010001447312513000170250ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/complex go 1.17 require ( github.com/fatih/color v1.15.0 github.com/vbauerster/mpb/v8 v8.6.1 ) 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.19 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/complex/main.go000066400000000000000000000040001447312513000171750ustar00rootroot00000000000000package 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{W: len(task) + 1, C: decor.DidentRight}), 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{W: len(task) + 1, C: decor.DidentRight}), 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.6.1/_examples/decoratorsOnTop/000077500000000000000000000000001447312513000174065ustar00rootroot00000000000000mpb-8.6.1/_examples/decoratorsOnTop/go.mod000066400000000000000000000006001447312513000205100ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/decoratorsOnTop go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/decoratorsOnTop/main.go000066400000000000000000000020401447312513000206550ustar00rootroot00000000000000package 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.6.1/_examples/differentWidth/000077500000000000000000000000001447312513000172275ustar00rootroot00000000000000mpb-8.6.1/_examples/differentWidth/go.mod000066400000000000000000000005771447312513000203460ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/differentWidth go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/differentWidth/main.go000066400000000000000000000027341447312513000205100ustar00rootroot00000000000000package 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.6.1/_examples/dynTotal/000077500000000000000000000000001447312513000160575ustar00rootroot00000000000000mpb-8.6.1/_examples/dynTotal/go.mod000066400000000000000000000005711447312513000171700ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/dynTotal go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/dynTotal/main.go000066400000000000000000000020571447312513000173360ustar00rootroot00000000000000package 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.6.1/_examples/gomodtidyall.sh000077500000000000000000000002051447312513000173050ustar00rootroot00000000000000#!/bin/sh for d in *; do [ ! -d "$d" ] && continue pushd "$d" >/dev/null 2>&1 go mod tidy popd >/dev/null 2>&1 done mpb-8.6.1/_examples/io/000077500000000000000000000000001447312513000146705ustar00rootroot00000000000000mpb-8.6.1/_examples/io/go.mod000066400000000000000000000005631447312513000160020ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/io go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/io/main.go000066400000000000000000000014411447312513000161430ustar00rootroot00000000000000package main import ( "crypto/rand" "io" "io/ioutil" "time" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { var total int64 = 1024 * 1024 * 500 reader := io.LimitReader(rand.Reader, total) 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(reader) defer proxyReader.Close() // copy from proxyReader, ignoring errors _, _ = io.Copy(ioutil.Discard, proxyReader) p.Wait() } mpb-8.6.1/_examples/mexicanBar/000077500000000000000000000000001447312513000163325ustar00rootroot00000000000000mpb-8.6.1/_examples/mexicanBar/go.mod000066400000000000000000000005731447312513000174450ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/mexicanBar go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/mexicanBar/main.go000066400000000000000000000022031447312513000176020ustar00rootroot00000000000000package 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.6.1/_examples/multiBars/000077500000000000000000000000001447312513000162235ustar00rootroot00000000000000mpb-8.6.1/_examples/multiBars/go.mod000066400000000000000000000005721447312513000173350ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/multiBars go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/multiBars/main.go000066400000000000000000000026011447312513000174750ustar00rootroot00000000000000package 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.6.1/_examples/poplog/000077500000000000000000000000001447312513000155615ustar00rootroot00000000000000mpb-8.6.1/_examples/poplog/go.mod000066400000000000000000000005671447312513000166770ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/poplog go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/poplog/main.go000066400000000000000000000021621447312513000170350ustar00rootroot00000000000000package 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, 60), ""), ), ) // 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.6.1/_examples/progressAsWriter/000077500000000000000000000000001447312513000176065ustar00rootroot00000000000000mpb-8.6.1/_examples/progressAsWriter/go.mod000066400000000000000000000006011447312513000207110ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/progressAsWriter go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/progressAsWriter/main.go000066400000000000000000000031021447312513000210550ustar00rootroot00000000000000package 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 wg sync.WaitGroup wg.Add(numBars) done := make(chan interface{}) p := mpb.New( mpb.WithWidth(64), mpb.WithWaitGroup(&wg), mpb.WithShutdownNotifier(done), ) log.SetOutput(p) 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 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)) } log.Println(name, "done") }() } var qwg sync.WaitGroup qwg.Add(1) go func() { quit: for { select { case <-done: // after done, underlying io.Writer returns mpb.DoneError // so following isn't printed log.Println("all done") break quit default: log.Println("waiting for done") time.Sleep(100 * time.Millisecond) } } qwg.Done() }() p.Wait() qwg.Wait() } mpb-8.6.1/_examples/quietMode/000077500000000000000000000000001447312513000162155ustar00rootroot00000000000000mpb-8.6.1/_examples/quietMode/go.mod000066400000000000000000000005721447312513000173270ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/quietMode go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/quietMode/main.go000066400000000000000000000030761447312513000174760ustar00rootroot00000000000000package 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.6.1/_examples/remove/000077500000000000000000000000001447312513000155565ustar00rootroot00000000000000mpb-8.6.1/_examples/remove/go.mod000066400000000000000000000005671447312513000166740ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/remove go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/remove/main.go000066400000000000000000000025211447312513000170310ustar00rootroot00000000000000package 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.6.1/_examples/reverseBar/000077500000000000000000000000001447312513000163615ustar00rootroot00000000000000mpb-8.6.1/_examples/reverseBar/go.mod000066400000000000000000000005731447312513000174740ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/reverseBar go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/reverseBar/main.go000066400000000000000000000030651447312513000176400ustar00rootroot00000000000000package 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.6.1/_examples/singleBar/000077500000000000000000000000001447312513000161675ustar00rootroot00000000000000mpb-8.6.1/_examples/singleBar/go.mod000066400000000000000000000005721447312513000173010ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/singleBar go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/singleBar/main.go000066400000000000000000000020271447312513000174430ustar00rootroot00000000000000package 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{W: len(name) + 1, C: decor.DidentRight}), // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "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.6.1/_examples/spinTipBar/000077500000000000000000000000001447312513000163345ustar00rootroot00000000000000mpb-8.6.1/_examples/spinTipBar/go.mod000066400000000000000000000005731447312513000174470ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/spinTipBar go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/spinTipBar/main.go000066400000000000000000000013131447312513000176050ustar00rootroot00000000000000package 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.6.1/_examples/spinnerBar/000077500000000000000000000000001447312513000163645ustar00rootroot00000000000000mpb-8.6.1/_examples/spinnerBar/go.mod000066400000000000000000000005731447312513000174770ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/spinnerBar go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/spinnerBar/main.go000066400000000000000000000032351447312513000176420ustar00rootroot00000000000000package 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.6.1/_examples/spinnerDecorator/000077500000000000000000000000001447312513000176025ustar00rootroot00000000000000mpb-8.6.1/_examples/spinnerDecorator/go.mod000066400000000000000000000006011447312513000207050ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/spinnerDecorator go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/spinnerDecorator/main.go000066400000000000000000000021351447312513000210560ustar00rootroot00000000000000package 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.6.1/_examples/stress/000077500000000000000000000000001447312513000156045ustar00rootroot00000000000000mpb-8.6.1/_examples/stress/go.mod000066400000000000000000000010211447312513000167040ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/stress go 1.17 require ( github.com/pkg/profile v1.7.0 github.com/vbauerster/mpb/v8 v8.6.1 ) 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.3 // indirect github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/stress/main.go000066400000000000000000000025661447312513000170700ustar00rootroot00000000000000package 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 = 32 ) 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.Elapsed(decor.ET_STYLE_GO, decor.WCSyncWidth), ), mpb.AppendDecorators( 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.Completed() { 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.6.1/_examples/suppressBar/000077500000000000000000000000001447312513000165725ustar00rootroot00000000000000mpb-8.6.1/_examples/suppressBar/go.mod000066400000000000000000000005651447312513000177060ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/suppressBar go 1.17 require ( github.com/mattn/go-runewidth v0.0.15 github.com/vbauerster/mpb/v8 v8.6.1 ) require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/suppressBar/main.go000066400000000000000000000045071447312513000200530ustar00rootroot00000000000000package main import ( "errors" "fmt" "io" "math/rand" "sync" "time" "github.com/mattn/go-runewidth" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" ) func main() { p := mpb.New() total := 100 msgCh := make(chan string) resumeCh := make(chan struct{}) nextCh := make(chan struct{}, 1) ew := &errorWrapper{} timer := time.AfterFunc(2*time.Second, func() { ew.reset(errors.New("timeout")) }) defer timer.Stop() bar := p.AddBar(int64(total), mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller { var msg *string var times int return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error { if msg == nil { select { case m := <-msgCh: msg = &m times = 10 nextCh <- struct{}{} default: } return base.Fill(w, st) } switch { case times == 0, st.Completed, st.Aborted: defer func() { msg = nil }() resumeCh <- struct{}{} default: times-- } _, err := io.WriteString(w, runewidth.Truncate(*msg, st.AvailableWidth, "…")) nextCh <- struct{}{} return err }) }), mpb.PrependDecorators(decor.Name("my bar:")), mpb.AppendDecorators(newCustomPercentage(nextCh)), ) // simulating some work go func() { 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) if ew.isErr() { msgCh <- fmt.Sprintf("%s at %d, retrying...", ew.Error(), i) i-- bar.SetRefill(int64(i)) ew.reset(nil) <-resumeCh continue } bar.Increment() } }() p.Wait() } type errorWrapper struct { sync.RWMutex err error } func (ew *errorWrapper) Error() string { ew.RLock() defer ew.RUnlock() return ew.err.Error() } func (ew *errorWrapper) isErr() bool { ew.RLock() defer ew.RUnlock() return ew.err != nil } func (ew *errorWrapper) reset(err error) { ew.Lock() ew.err = err ew.Unlock() } type percentage struct { decor.Decorator suspend <-chan struct{} } func (d percentage) Decor(s decor.Statistics) (string, int) { select { case <-d.suspend: return d.Format("") default: return d.Decorator.Decor(s) } } func newCustomPercentage(nextCh <-chan struct{}) decor.Decorator { return percentage{ Decorator: decor.Percentage(), suspend: nextCh, } } mpb-8.6.1/_examples/tipOnComplete/000077500000000000000000000000001447312513000170435ustar00rootroot00000000000000mpb-8.6.1/_examples/tipOnComplete/go.mod000066400000000000000000000005761447312513000201610ustar00rootroot00000000000000module github.com/vbauerster/mpb/_examples/tipOnComplete go 1.17 require github.com/vbauerster/mpb/v8 v8.6.1 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.15 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/sys v0.11.0 // indirect ) mpb-8.6.1/_examples/tipOnComplete/main.go000066400000000000000000000011621447312513000203160ustar00rootroot00000000000000package 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.6.1/_svg/000077500000000000000000000000001447312513000132425ustar00rootroot00000000000000mpb-8.6.1/_svg/godEMrCZmJkHYH1X9dN4Nm0U7.svg000066400000000000000000002025171447312513000200560ustar00rootroot00000000000000~/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.6.1/_svg/hIpTa3A5rQz65ssiVuRJu87X6.svg000066400000000000000000005242741447312513000201770ustar00rootroot00000000000000~/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.6.1/_svg/wHzf1M7sd7B3zVa2scBMnjqRf.svg000066400000000000000000005756551447312513000203310ustar00rootroot00000000000000~/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.6.1/bar.go000066400000000000000000000357401447312513000134100ustar00rootroot00000000000000package 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) done chan struct{} container *Progress bs *bState cancel func() } type syncTable [2][]chan int type extenderFunc func([]io.Reader, decor.Statistics) ([]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 completed bool aborted bool triggerComplete bool rmOnComplete bool noPop bool autoRefresh bool aDecorators []decor.Decorator pDecorators []decor.Decorator averageDecorators []decor.AverageDecorator ewmaDecorators []decor.EwmaDecorator shutdownListeners []decor.ShutdownListener buffers [3]*bytes.Buffer 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 done 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)), done: make(chan struct{}), container: container, cancel: cancel, } container.bwg.Add(1) go bar.serve(ctx, 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) method after (io.Reader).Read 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 bool) select { case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }: return newProxyReader(r, b, <-result) case <-b.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 bool) select { case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }: return newProxyWriter(w, b, <-result) case <-b.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.done: 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.done: 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.done: } } // TraverseDecorators traverses all available decorators and calls cb func on each. func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) { iter := make(chan decor.Decorator) select { case b.operateState <- func(s *bState) { for _, decorators := range [][]decor.Decorator{ s.pDecorators, s.aDecorators, } { for _, d := range decorators { iter <- d } } close(iter) }: for d := range iter { cb(unwrap(d)) } case <-b.done: } } // EnableTriggerComplete enables triggering complete event. It's // effective only for bars which were constructed with `total <= 0` and // after total has been set with (*Bar).SetTotal(int64, false). If bar // has been incremented to the total, complete event is triggered right // away. func (b *Bar) EnableTriggerComplete() { select { case b.operateState <- func(s *bState) { if s.triggerComplete || s.total <= 0 { return } if s.current >= s.total { s.current = s.total s.completed = true b.triggerCompletion(s) } else { s.triggerComplete = true } }: case <-b.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 triggerCompletion is true, total value is set to current and // complete event is triggered right away. func (b *Bar) SetTotal(total int64, triggerCompletion bool) { select { case b.operateState <- func(s *bState) { if s.triggerComplete { return } if total < 0 { s.total = s.current } else { s.total = total } if triggerCompletion { s.current = s.total s.completed = true b.triggerCompletion(s) } }: case <-b.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.completed = true b.triggerCompletion(s) } }: case <-b.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) { if n := current - s.current; n > 0 { s.decoratorEwmaUpdate(n, iterDur) } s.current = current if s.triggerComplete && s.current >= s.total { s.current = s.total s.completed = true b.triggerCompletion(s) } }: case <-b.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) { if n <= 0 { return } select { case b.operateState <- func(s *bState) { s.current += n if s.triggerComplete && s.current >= s.total { s.current = s.total s.completed = true b.triggerCompletion(s) } }: case <-b.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) { if n <= 0 { return } select { case b.operateState <- func(s *bState) { s.decoratorEwmaUpdate(n, iterDur) s.current += n if s.triggerComplete && s.current >= s.total { s.current = s.total s.completed = true b.triggerCompletion(s) } }: case <-b.done: } } // DecoratorAverageAdjust adjusts all average based decorators. Call // if you need to adjust start time of all average based decorators // or after progress resume. func (b *Bar) DecoratorAverageAdjust(start time.Time) { select { case b.operateState <- func(s *bState) { s.decoratorAverageAdjust(start) }: case <-b.done: } } // 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.completed || s.aborted { return } s.aborted = true s.rmOnComplete = drop b.triggerCompletion(s) }: case <-b.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.done: 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.done: return b.bs.completed } } // IsRunning reports whether the bar is running, i.e. not yet completed // and not yet aborted. func (b *Bar) IsRunning() bool { result := make(chan bool) select { case b.operateState <- func(s *bState) { result <- !s.completed && !s.aborted }: return <-result case <-b.done: return false } } // Wait blocks until bar is completed or aborted. func (b *Bar) Wait() { <-b.done } func (b *Bar) serve(ctx context.Context, bs *bState) { defer b.container.bwg.Done() for { select { case op := <-b.operateState: op(bs) case <-ctx.Done(): bs.aborted = !bs.completed bs.decoratorShutdownNotify() b.bs = bs close(b.done) return } } } func (b *Bar) render(tw int) { var done bool fn := func(s *bState) { var rows []io.Reader stat := newStatistics(tw, s) r, err := s.draw(stat) if err != nil { b.frameCh <- &renderFrame{err: err} return } rows = append(rows, r) if s.extender != nil { rows, err = s.extender(rows, stat) if err != nil { b.frameCh <- &renderFrame{err: err} return } } frame := &renderFrame{ rows: rows, shutdown: s.shutdown, rmOnComplete: s.rmOnComplete, noPop: s.noPop, done: done, } if s.completed || s.aborted { // post increment makes sure OnComplete decorators are rendered s.shutdown++ } b.frameCh <- frame } select { case b.operateState <- fn: case <-b.done: done = true fn(b.bs) } } func (b *Bar) triggerCompletion(s *bState) { 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 (b *Bar) tryEarlyRefresh(renderReq chan<- time.Time) { var anyOtherRunning bool b.container.traverseBars(func(bar *Bar) bool { anyOtherRunning = b != bar && bar.IsRunning() return anyOtherRunning }) if !anyOtherRunning { for { select { case renderReq <- time.Now(): case <-b.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.done: return b.bs.wSyncTable() } } func (s *bState) draw(stat decor.Statistics) (io.Reader, error) { r, err := s.drawImpl(stat) if err != nil { for _, b := range s.buffers { b.Reset() } return nil, err } return io.MultiReader(r, strings.NewReader("\n")), nil } func (s *bState) drawImpl(stat decor.Statistics) (io.Reader, 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 } bufP, bufB, bufA := s.buffers[0], s.buffers[1], s.buffers[2] err := eitherError(decorFiller(bufP, s.pDecorators), decorFiller(bufA, s.aDecorators)) if err != nil { return nil, err } if !s.trimSpace && stat.AvailableWidth >= 2 { stat.AvailableWidth -= 2 writeFiller := func(buf *bytes.Buffer) error { return s.filler.Fill(buf, stat) } for _, fn := range []func(*bytes.Buffer) error{ writeSpace, writeFiller, writeSpace, } { if err := fn(bufB); err != nil { return nil, err } } } else { err := s.filler.Fill(bufB, stat) if err != nil { return nil, err } } return io.MultiReader(bufP, bufB, bufA), nil } func (s *bState) wSyncTable() (table syncTable) { var count int var row []chan int for i, decorators := range [][]decor.Decorator{ s.pDecorators, s.aDecorators, } { 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) decoratorEwmaUpdate(n int64, dur time.Duration) { var wg sync.WaitGroup for i := 0; i < len(s.ewmaDecorators); i++ { switch d := s.ewmaDecorators[i]; i { case len(s.ewmaDecorators) - 1: d.EwmaUpdate(n, dur) default: wg.Add(1) go func() { d.EwmaUpdate(n, dur) wg.Done() }() } } wg.Wait() } func (s bState) decoratorAverageAdjust(start time.Time) { var wg sync.WaitGroup for i := 0; i < len(s.averageDecorators); i++ { switch d := s.averageDecorators[i]; i { case len(s.averageDecorators) - 1: d.AverageAdjust(start) default: wg.Add(1) go func() { d.AverageAdjust(start) wg.Done() }() } } wg.Wait() } func (s bState) decoratorShutdownNotify() { var wg sync.WaitGroup for i := 0; i < len(s.shutdownListeners); i++ { switch d := s.shutdownListeners[i]; i { case len(s.shutdownListeners) - 1: d.OnShutdown() default: wg.Add(1) go func() { d.OnShutdown() wg.Done() }() } } wg.Wait() } func newStatistics(tw int, s *bState) 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 } func writeSpace(buf *bytes.Buffer) error { return buf.WriteByte(' ') } func eitherError(errors ...error) error { for _, err := range errors { if err != nil { return err } } return nil } mpb-8.6.1/bar_filler.go000066400000000000000000000012331447312513000147330ustar00rootroot00000000000000package 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.6.1/bar_filler_bar.go000066400000000000000000000156231447312513000155670ustar00rootroot00000000000000package 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 tipOnComplete bool tip struct { frames []component count uint } } 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, tipOnComplete: s.tipOnComplete, } 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.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.tipOnComplete { tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))] s.tip.count++ fillCount += tip.width } if stat.Refill != 0 { refWidth := int(internal.PercentageRound(stat.Total, stat.Refill, uint(width))) curWidth -= refWidth refWidth += curWidth 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...) } } else { for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w { filling = append(filling, s.components[iFiller].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.6.1/bar_filler_nop.go000066400000000000000000000010261447312513000156070ustar00rootroot00000000000000package 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.6.1/bar_filler_spinner.go000066400000000000000000000044501447312513000164750ustar00rootroot00000000000000package 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.6.1/bar_option.go000066400000000000000000000115251447312513000147730ustar00rootroot00000000000000package 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 } // 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.aDecorators = decorators } } // 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.pDecorators = 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 } fn := makeExtenderFunc(filler, rev) return func(s *bState) { s.extender = fn } } func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc { buf := new(bytes.Buffer) base := func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) { err := filler.Fill(buf, stat) if err != nil { buf.Reset() return rows, err } for { b, err := buf.ReadBytes('\n') if err != nil { break } rows = append(rows, bytes.NewReader(b)) } buf.Reset() return rows, err } if !rev { return base } return func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) { rows, err := base(rows, stat) 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.6.1/bar_test.go000066400000000000000000000136411447312513000144430ustar00rootroot00000000000000package 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.Fail() } bar.IncrBy(total) if !bar.Completed() { t.Error("bar isn't completed after increment") } 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.Fail() } bar.Abort(false) if !bar.Aborted() { t.Error("bar isn't aborted after abort call") } 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 TestBarEnableTriggerCompleteAndIncrementBefore(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) bar := p.AddBar(0) // never complete bar for _, f := range []func(){ func() { bar.SetTotal(40, false) }, func() { bar.IncrBy(60) }, func() { bar.SetTotal(80, false) }, func() { bar.IncrBy(20) }, } { f() if bar.Completed() { t.Fail() } } bar.EnableTriggerComplete() if !bar.Completed() { t.Fail() } p.Wait() } func TestBarEnableTriggerCompleteAndIncrementAfter(t *testing.T) { p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard)) bar := p.AddBar(0) // never complete bar for _, f := range []func(){ func() { bar.SetTotal(40, false) }, func() { bar.IncrBy(60) }, func() { bar.SetTotal(80, false) }, func() { bar.EnableTriggerComplete() }, } { f() if bar.Completed() { t.Fail() } } bar.IncrBy(20) if !bar.Completed() { t.Fail() } 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.6.1/barbench_test.go000066400000000000000000000034161447312513000154420ustar00rootroot00000000000000package mpb_test import ( "io" "sync" "testing" "github.com/vbauerster/mpb/v8" ) const total = 1000 func BenchmarkNopStyle1Bar(b *testing.B) { bench(b, mpb.NopStyle(), false, 1) } func BenchmarkNopStyle1BarWithAutoRefresh(b *testing.B) { bench(b, mpb.NopStyle(), true, 1) } func BenchmarkNopStyle2Bars(b *testing.B) { bench(b, mpb.NopStyle(), false, 2) } func BenchmarkNopStyle2BarsWithAutoRefresh(b *testing.B) { bench(b, mpb.NopStyle(), true, 2) } func BenchmarkNopStyle3Bars(b *testing.B) { bench(b, mpb.NopStyle(), false, 3) } func BenchmarkNopStyle3BarsWithAutoRefresh(b *testing.B) { bench(b, mpb.NopStyle(), true, 3) } func BenchmarkBarStyle1Bar(b *testing.B) { bench(b, mpb.BarStyle(), false, 1) } func BenchmarkBarStyle1BarWithAutoRefresh(b *testing.B) { bench(b, mpb.BarStyle(), true, 1) } func BenchmarkBarStyle2Bars(b *testing.B) { bench(b, mpb.BarStyle(), false, 2) } func BenchmarkBarStyle2BarsWithAutoRefresh(b *testing.B) { bench(b, mpb.BarStyle(), true, 2) } func BenchmarkBarStyle3Bars(b *testing.B) { bench(b, mpb.BarStyle(), false, 3) } func BenchmarkBarStyle3BarsWithAutoRefresh(b *testing.B) { bench(b, mpb.BarStyle(), true, 3) } func bench(b *testing.B, builder mpb.BarFillerBuilder, autoRefresh bool, n int) { var wg sync.WaitGroup p := mpb.New( mpb.WithWidth(100), mpb.WithOutput(io.Discard), mpb.ContainerOptional(mpb.WithAutoRefresh(), autoRefresh), ) b.ResetTimer() for i := 0; i < b.N; i++ { for j := 0; j < n; j++ { bar := p.New(total, builder) switch j { case n - 1: complete(b, bar) default: wg.Add(1) go func() { complete(b, bar) wg.Done() }() } } wg.Wait() } p.Wait() } func complete(b *testing.B, bar *mpb.Bar) { for i := 0; i < total; i++ { bar.Increment() } bar.Wait() } mpb-8.6.1/container_option.go000066400000000000000000000072161447312513000162130ustar00rootroot00000000000000package 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 will pop completed bars to the top. // To stop rendering bar after it has been popped, use // mpb.BarRemoveOnComplete() option on that bar. 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.6.1/cwriter/000077500000000000000000000000001447312513000137635ustar00rootroot00000000000000mpb-8.6.1/cwriter/cuuAndEd_construction_bench_test.go000066400000000000000000000015301447312513000230110ustar00rootroot00000000000000package 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.6.1/cwriter/doc.go000066400000000000000000000001321447312513000150530ustar00rootroot00000000000000// Package cwriter is a console writer abstraction for the underlying OS. package cwriter mpb-8.6.1/cwriter/util_bsd.go000066400000000000000000000002301447312513000161120ustar00rootroot00000000000000//go:build darwin || dragonfly || freebsd || netbsd || openbsd package cwriter import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TIOCGETA mpb-8.6.1/cwriter/util_linux.go000066400000000000000000000001571447312513000165110ustar00rootroot00000000000000//go:build aix || linux package cwriter import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETS mpb-8.6.1/cwriter/util_solaris.go000066400000000000000000000001521447312513000170210ustar00rootroot00000000000000//go:build solaris package cwriter import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETA mpb-8.6.1/cwriter/util_zos.go000066400000000000000000000001461447312513000161630ustar00rootroot00000000000000//go:build zos package cwriter import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETS mpb-8.6.1/cwriter/writer.go000066400000000000000000000023011447312513000156220ustar00rootroot00000000000000package 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.6.1/cwriter/writer_posix.go000066400000000000000000000021671447312513000170560ustar00rootroot00000000000000//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.6.1/cwriter/writer_windows.go000066400000000000000000000052501447312513000174020ustar00rootroot00000000000000//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.6.1/decor/000077500000000000000000000000001447312513000134005ustar00rootroot00000000000000mpb-8.6.1/decor/any.go000066400000000000000000000005371447312513000145230ustar00rootroot00000000000000package 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.6.1/decor/counters.go000066400000000000000000000153671447312513000156050ustar00rootroot00000000000000package 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.6.1/decor/decorator.go000066400000000000000000000120331447312513000157100ustar00rootroot00000000000000package decor import ( "fmt" "time" "github.com/mattn/go-runewidth" ) const ( // DidentRight bit specifies identation direction. // // |foo |b | With DidentRight // | foo| b| Without DidentRight DidentRight = 1 << iota // DextraSpace bit adds extra space, makes sense with DSyncWidth only. // When DidentRight bit set, the space will be added to the right, // otherwise to the left. DextraSpace // DSyncWidth bit enables same column width synchronization. // Effective with multiple bars only. DSyncWidth // DSyncWidthR is shortcut for DSyncWidth|DidentRight DSyncWidthR = DSyncWidth | DidentRight // DSyncSpace is shortcut for DSyncWidth|DextraSpace DSyncSpace = DSyncWidth | DextraSpace // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DidentRight DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight ) // 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) (str string, viewWidth 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) { viewWidth := runewidth.StringWidth(str) if wc.W > viewWidth { viewWidth = wc.W } if (wc.C & DSyncWidth) != 0 { if (wc.C & DextraSpace) != 0 { viewWidth++ } wc.wsync <- viewWidth viewWidth = <-wc.wsync } return wc.fill(str, viewWidth), viewWidth } // Init initializes width related config. func (wc *WC) Init() WC { if (wc.C & DidentRight) != 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.6.1/decor/doc.go000066400000000000000000000012251447312513000144740ustar00rootroot00000000000000// 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.6.1/decor/elapsed.go000066400000000000000000000013641447312513000153500ustar00rootroot00000000000000package 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] // // `startTime` start time // // `wcc` optional WC config func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator { var msg string producer := chooseTimeProducer(style) fn := func(s Statistics) string { if !s.Completed { msg = producer(time.Since(startTime)) } return msg } return Any(fn, wcc...) } mpb-8.6.1/decor/eta.go000066400000000000000000000136571447312513000145140ustar00rootroot00000000000000package 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 { var average ewma.MovingAverage if age == 0 { average = ewma.NewMovingAverage() } else { average = ewma.NewMovingAverage(age) } return MovingAverageETA(style, NewThreadSafeMovingAverage(average), nil, 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 { d := &movingAverageETA{ WC: initWC(wcc...), average: average, normalizer: normalizer, producer: chooseTimeProducer(style), } return d } type movingAverageETA struct { WC average ewma.MovingAverage normalizer TimeNormalizer producer func(time.Duration) string } 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) { durPerItem := float64(dur) / float64(n) if math.IsInf(durPerItem, 0) || math.IsNaN(durPerItem) { return } 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] // // `startTime` start time // // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer] // // `wcc` optional WC config func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator { d := &averageETA{ WC: initWC(wcc...), startTime: startTime, normalizer: normalizer, producer: chooseTimeProducer(style), } return d } type averageETA struct { WC startTime 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.startTime)) / 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(startTime time.Time) { d.startTime = startTime } // 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() return normalized }) } // 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() return normalized }) } 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.6.1/decor/meta.go000066400000000000000000000013651447312513000146620ustar00rootroot00000000000000package 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.6.1/decor/moving_average.go000066400000000000000000000031341447312513000167210ustar00rootroot00000000000000package 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 NewThreadSafeMovingAverage(new(medianWindow)) } mpb-8.6.1/decor/name.go000066400000000000000000000004351447312513000146510ustar00rootroot00000000000000package 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.6.1/decor/on_abort.go000066400000000000000000000027621447312513000155410ustar00rootroot00000000000000package 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.Completed { 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.6.1/decor/on_compete_or_on_abort.go000066400000000000000000000014001447312513000204350ustar00rootroot00000000000000package 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.6.1/decor/on_complete.go000066400000000000000000000027701447312513000162410ustar00rootroot00000000000000package 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.6.1/decor/on_condition.go000066400000000000000000000017761447312513000164240ustar00rootroot00000000000000package 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.6.1/decor/percentage.go000066400000000000000000000026501447312513000160470ustar00rootroot00000000000000package 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(s.Total, s.Current, 100) return fmt.Sprintf(format, percentageType(p)) } return Any(f, wcc...) } mpb-8.6.1/decor/percentage_test.go000066400000000000000000000032071447312513000171050ustar00rootroot00000000000000package 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) } }) } } mpb-8.6.1/decor/size_type.go000066400000000000000000000045231447312513000157460ustar00rootroot00000000000000package 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.6.1/decor/size_type_test.go000066400000000000000000000144421447312513000170060ustar00rootroot00000000000000package 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.6.1/decor/sizeb1000_string.go000066400000000000000000000015721447312513000167370ustar00rootroot00000000000000// 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.6.1/decor/sizeb1024_string.go000066400000000000000000000016101447312513000167360ustar00rootroot00000000000000// 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.6.1/decor/speed.go000066400000000000000000000110041447312513000150230ustar00rootroot00000000000000package 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 (self speedFormatter) Format(st fmt.State, verb rune) { self.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, NewThreadSafeMovingAverage(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...), average: average, producer: chooseSpeedProducer(unit, format), } return d } type movingAverageSpeed struct { WC producer func(float64) string average ewma.MovingAverage msg string } func (d *movingAverageSpeed) Decor(s Statistics) (string, int) { if !s.Completed { var speed float64 if v := d.average.Value(); v > 0 { speed = 1 / v } d.msg = d.producer(speed * 1e9) } return d.Format(d.msg) } func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) { durPerByte := float64(dur) / float64(n) if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) { return } 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" // // `startTime` 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, startTime time.Time, wcc ...WC) Decorator { d := &averageSpeed{ WC: initWC(wcc...), startTime: startTime, producer: chooseSpeedProducer(unit, format), } return d } type averageSpeed struct { WC startTime 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.startTime)) d.msg = d.producer(speed * 1e9) } return d.Format(d.msg) } func (d *averageSpeed) AverageAdjust(startTime time.Time) { d.startTime = startTime } 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.6.1/decor/speed_test.go000066400000000000000000000132471447312513000160750ustar00rootroot00000000000000package 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.6.1/decor/spinner.go000066400000000000000000000010051447312513000154010ustar00rootroot00000000000000package 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.6.1/decorators_test.go000066400000000000000000000077711447312513000160530ustar00rootroot00000000000000package 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.DidentRight}), 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 TestPercentageDwidthSyncDidentRight(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.6.1/doc.go000066400000000000000000000001361447312513000134000ustar00rootroot00000000000000// Package mpb is a library for rendering progress bars in terminal applications. package mpb mpb-8.6.1/draw_test.go000066400000000000000000001101341447312513000146270ustar00rootroot00000000000000package 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(), 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(), 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 { s := newTestState(tc.style.Build()) s.reqWidth = tc.barWidth s.total = tc.total s.current = tc.current s.trimSpace = tc.trim s.refill = tc.refill s.completed = tc.total > 0 && tc.current >= tc.total tmpBuf.Reset() r, err := s.draw(newStatistics(tw, s)) if err != nil { t.FailNow() } _, 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 { s := newTestState(tc.style.Build()) s.reqWidth = tc.barWidth s.total = tc.total s.current = tc.current s.trimSpace = tc.trim s.refill = tc.refill s.completed = tc.total > 0 && tc.current >= tc.total tmpBuf.Reset() r, err := s.draw(newStatistics(tw, s)) if err != nil { t.FailNow() } _, 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 { s := newTestState(tc.style.Build()) s.reqWidth = tc.barWidth s.total = tc.total s.current = tc.current s.trimSpace = tc.trim s.refill = tc.refill s.completed = tc.total > 0 && tc.current >= tc.total tmpBuf.Reset() r, err := s.draw(newStatistics(tw, s)) if err != nil { t.FailNow() } _, 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 newTestState(filler BarFiller) *bState { bs := &bState{ filler: filler, } for i := 0; i < len(bs.buffers); i++ { bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512)) } return bs } mpb-8.6.1/example_test.go000066400000000000000000000033271447312513000153320ustar00rootroot00000000000000package 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{W: len(name) + 1, C: decor.DidentRight}), // replace ETA decorator with "done" message, OnComplete event decor.OnComplete( decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "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.6.1/export_test.go000066400000000000000000000001611447312513000152110ustar00rootroot00000000000000package mpb // make syncWidth func public in test var SyncWidth = syncWidth type PriorityQueue = priorityQueue mpb-8.6.1/go.mod000066400000000000000000000004241447312513000134120ustar00rootroot00000000000000module 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.15 golang.org/x/sys v0.11.0 ) require github.com/rivo/uniseg v0.4.4 // indirect go 1.17 mpb-8.6.1/go.sum000066400000000000000000000017321447312513000134420ustar00rootroot00000000000000github.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.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= mpb-8.6.1/heap_manager.go000066400000000000000000000063101447312513000152420ustar00rootroot00000000000000package 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.6.1/internal/000077500000000000000000000000001447312513000141205ustar00rootroot00000000000000mpb-8.6.1/internal/percentage.go000066400000000000000000000007311447312513000165650ustar00rootroot00000000000000package internal import "math" // Percentage is a helper function, to calculate percentage. func Percentage(total, current int64, width uint) float64 { if total <= 0 { return 0 } if current >= total { return float64(width) } return float64(int64(width)*current) / float64(total) } // PercentageRound same as Percentage but with math.Round. func PercentageRound(total, current int64, width uint) float64 { return math.Round(Percentage(total, current, width)) } mpb-8.6.1/internal/percentage_test.go000066400000000000000000000041131447312513000176220ustar00rootroot00000000000000package 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.6.1/internal/width.go000066400000000000000000000003721447312513000155700ustar00rootroot00000000000000package 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.6.1/priority_queue.go000066400000000000000000000012751447312513000157250ustar00rootroot00000000000000package 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.6.1/progress.go000066400000000000000000000245071447312513000145070ustar00rootroot00000000000000package 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 an error when `*mpb.Progress` is done but its functionality is requested. var DoneError = fmt.Errorf("%T instance can't be reused after it's done", (*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), refreshRate: defaultRefreshRate, popPriority: math.MinInt32, 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 { 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 *Progress instance is done, i.e. 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 *Progress instance is done, i.e. called after // (*Progress).Wait(), return error == DoneError. func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) (*Bar, error) { if filler == nil { filler = NopStyle().Build() } type result struct { bar *Bar bs *bState } ch := make(chan result) 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 <- result{bar, bs} }: res := <-ch bar, bs := res.bar, res.bs bar.TraverseDecorators(func(d decor.Decorator) { if d, ok := d.(decor.AverageDecorator); ok { bs.averageDecorators = append(bs.averageDecorators, d) } if d, ok := d.(decor.EwmaDecorator); ok { bs.ewmaDecorators = append(bs.ewmaDecorators, d) } if d, ok := d.(decor.ShutdownListener); ok { bs.shutdownListeners = append(bs.shutdownListeners, d) } }) return bar, 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 `*mpb.Progress` will print lines above a running bar. // Writes aren't flushed immediately, but at next refresh cycle. // If Write is called after `*mpb.Progress` is done, `mpb.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() { // wait for user wg, if any if p.uwg != nil { p.uwg.Wait() } p.bwg.Wait() p.Shutdown() } // 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() render := func() error { return s.render(cw) } var err error for { select { case op := <-p.operateState: op(s) case fn := <-p.interceptIO: fn(cw) case <-s.renderReq: e := render() if e != nil { p.cancel() // cancel all bars render = func() error { return nil } err = e } case <-p.done: update := make(chan bool) for s.autoRefresh && err == nil { s.hm.state(update) if <-update { err = render() } else { break } } if err != nil { _, _ = fmt.Fprintln(s.debugOut, err.Error()) } s.hm.end(s.shutdownNotifier) return } } } func (s pState) autoRefreshListener(done chan struct{}) { if s.delayRC != nil { <-s.delayRC } 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 = 100 } height = 100 } 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.hm.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) } } if frame.shutdown != 0 && !frame.done { if qb, ok := s.queueBars[b]; ok { b.cancel() delete(s.queueBars, b) qb.priority = b.priority wg.Add(1) go func(b *Bar) { s.hm.push(b, true) wg.Done() }(qb) continue } if s.popCompleted && !frame.noPop { switch frame.shutdown { case 1: b.priority = s.popPriority s.popPriority++ default: b.cancel() popCount += usedRows continue } } else if frame.rmOnComplete { b.cancel() continue } else { b.cancel() } } wg.Add(1) go func(b *Bar) { s.hm.push(b, false) wg.Done() }(b) } 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) 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, } if total > 0 { bs.triggerComplete = true } for _, opt := range options { if opt != nil { opt(bs) } } for i := 0; i < len(bs.buffers); i++ { bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512)) } return bs } mpb-8.6.1/progress_test.go000066400000000000000000000141401447312513000155360ustar00rootroot00000000000000package 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.6.1/proxyreader.go000066400000000000000000000034141447312513000152010ustar00rootroot00000000000000package 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 toNopReadCloser(r) } func toNopReadCloser(r io.Reader) io.ReadCloser { if _, ok := r.(io.WriterTo); ok { return nopReadCloserWriterTo{r} } return nopReadCloser{r} } type nopReadCloser struct { io.Reader } func (nopReadCloser) Close() error { return nil } type nopReadCloserWriterTo struct { io.Reader } func (nopReadCloserWriterTo) Close() error { return nil } func (c nopReadCloserWriterTo) WriteTo(w io.Writer) (int64, error) { return c.Reader.(io.WriterTo).WriteTo(w) } mpb-8.6.1/proxyreader_test.go000066400000000000000000000046151447312513000162440ustar00rootroot00000000000000package 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.6.1/proxywriter.go000066400000000000000000000035131447312513000152530ustar00rootroot00000000000000package 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.6.1/proxywriter_test.go000066400000000000000000000042341447312513000163130ustar00rootroot00000000000000package 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) } }