New upstream version 7.3.2
Andreas Tille
4 years ago
| 0 | name: Test | |
| 1 | ||
| 2 | on: [push, pull_request] | |
| 3 | ||
| 4 | jobs: | |
| 5 | test: | |
| 6 | strategy: | |
| 7 | matrix: | |
| 8 | go-version: [1.16, 1.17] | |
| 9 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 10 | runs-on: ${{ matrix.os }} | |
| 11 | steps: | |
| 12 | - name: Setup Go | |
| 13 | uses: actions/setup-go@v2 | |
| 14 | with: | |
| 15 | go-version: ${{ matrix.go-version }} | |
| 16 | - name: Checkout code | |
| 17 | uses: actions/checkout@v2 | |
| 18 | - uses: actions/cache@v2 | |
| 19 | with: | |
| 20 | # In order: | |
| 21 | # * Module download cache | |
| 22 | # * Build cache (Linux) | |
| 23 | # * Build cache (Mac) | |
| 24 | # * Build cache (Windows) | |
| 25 | path: | | |
| 26 | ~/go/pkg/mod | |
| 27 | ~/.cache/go-build | |
| 28 | ~/Library/Caches/go-build | |
| 29 | %LocalAppData%\go-build | |
| 30 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | |
| 31 | restore-keys: | | |
| 32 | ${{ runner.os }}-go- | |
| 33 | - name: Test | |
| 34 | run: go test -race ./... |
| 0 | language: go | |
| 1 | arch: | |
| 2 | - amd64 | |
| 3 | - ppc64le | |
| 4 | ||
| 5 | go: | |
| 6 | - 1.14.x | |
| 7 | ||
| 8 | script: | |
| 9 | - go test -race ./... | |
| 10 | - for i in _examples/*/; do go build $i/*.go || exit 1; done |
| 0 | 0 | # Multi Progress Bar |
| 1 | 1 | |
| 2 | [](https://pkg.go.dev/github.com/vbauerster/mpb/v6) | |
| 3 | [](https://travis-ci.org/vbauerster/mpb) | |
| 4 | [](https://goreportcard.com/report/github.com/vbauerster/mpb) | |
| 2 | [](https://pkg.go.dev/github.com/vbauerster/mpb/v7) | |
| 3 | [](https://github.com/vbauerster/mpb/actions/workflows/test.yml) | |
| 4 | [](https://www.paypal.me/vbauerster) | |
| 5 | 5 | |
| 6 | 6 | **mpb** is a Go lib for rendering progress bars in terminal applications. |
| 7 | 7 | |
| 25 | 25 | "math/rand" |
| 26 | 26 | "time" |
| 27 | 27 | |
| 28 | "github.com/vbauerster/mpb/v6" | |
| 29 | "github.com/vbauerster/mpb/v6/decor" | |
| 28 | "github.com/vbauerster/mpb/v7" | |
| 29 | "github.com/vbauerster/mpb/v7/decor" | |
| 30 | 30 | ) |
| 31 | 31 | |
| 32 | 32 | func main() { |
| 35 | 35 | |
| 36 | 36 | total := 100 |
| 37 | 37 | name := "Single Bar:" |
| 38 | // adding a single bar, which will inherit container's width | |
| 39 | bar := p.Add(int64(total), | |
| 40 | // progress bar filler with customized style | |
| 41 | mpb.NewBarFiller("╢▌▌░╟"), | |
| 38 | // create a single bar, which will inherit container's width | |
| 39 | bar := p.New(int64(total), | |
| 40 | // BarFillerBuilder with custom style | |
| 41 | mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"), | |
| 42 | 42 | mpb.PrependDecorators( |
| 43 | 43 | // display our name with one space on the right |
| 44 | 44 | decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), |
| 64 | 64 | |
| 65 | 65 | ```go |
| 66 | 66 | var wg sync.WaitGroup |
| 67 | // pass &wg (optional), so p will wait for it eventually | |
| 67 | // passed wg will be accounted at p.Wait() call | |
| 68 | 68 | p := mpb.New(mpb.WithWaitGroup(&wg)) |
| 69 | 69 | total, numBars := 100, 3 |
| 70 | 70 | wg.Add(numBars) |
| 82 | 82 | // replace ETA decorator with "done" message, OnComplete event |
| 83 | 83 | decor.OnComplete( |
| 84 | 84 | // ETA decorator with ewma age of 60 |
| 85 | decor.EwmaETA(decor.ET_STYLE_GO, 60), "done", | |
| 85 | decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncWidth), "done", | |
| 86 | 86 | ), |
| 87 | 87 | ), |
| 88 | 88 | ) |
| 102 | 102 | } |
| 103 | 103 | }() |
| 104 | 104 | } |
| 105 | // Waiting for passed &wg and for all bars to complete and flush | |
| 105 | // wait for passed wg and for all bars to complete and flush | |
| 106 | 106 | p.Wait() |
| 107 | 107 | ``` |
| 108 | 108 | |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 6 | 6 | "sync" |
| 7 | 7 | "time" |
| 8 | 8 | |
| 9 | "github.com/vbauerster/mpb/v6" | |
| 10 | "github.com/vbauerster/mpb/v6/decor" | |
| 9 | "github.com/vbauerster/mpb/v7" | |
| 10 | "github.com/vbauerster/mpb/v7/decor" | |
| 11 | 11 | ) |
| 12 | 12 | |
| 13 | 13 | func main() { |
| 14 | 14 | var wg sync.WaitGroup |
| 15 | // passed wg will be accounted at p.Wait() call | |
| 15 | 16 | p := mpb.New(mpb.WithWaitGroup(&wg)) |
| 16 | 17 | total, numBars := 100, 3 |
| 17 | 18 | wg.Add(numBars) |
| 54 | 55 | } |
| 55 | 56 | }() |
| 56 | 57 | } |
| 57 | // wait for all bars to complete and flush | |
| 58 | // wait for passed wg and for all bars to complete and flush | |
| 58 | 59 | p.Wait() |
| 59 | 60 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 6 | 6 | "sync" |
| 7 | 7 | "time" |
| 8 | 8 | |
| 9 | "github.com/vbauerster/mpb/v6" | |
| 10 | "github.com/vbauerster/mpb/v6/decor" | |
| 9 | "github.com/vbauerster/mpb/v7" | |
| 10 | "github.com/vbauerster/mpb/v7/decor" | |
| 11 | 11 | ) |
| 12 | 12 | |
| 13 | 13 | func main() { |
| 15 | 15 | defer cancel() |
| 16 | 16 | |
| 17 | 17 | var wg sync.WaitGroup |
| 18 | // passed wg will be accounted at p.Wait() call | |
| 18 | 19 | p := mpb.NewWithContext(ctx, mpb.WithWaitGroup(&wg)) |
| 19 | 20 | total := 300 |
| 20 | 21 | numBars := 3 |
| 48 | 49 | } |
| 49 | 50 | }() |
| 50 | 51 | } |
| 51 | ||
| 52 | // wait for passed wg and for all bars to complete and flush | |
| 52 | 53 | p.Wait() |
| 53 | 54 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func init() { |
| 15 | 15 | |
| 16 | 16 | func main() { |
| 17 | 17 | doneWg := new(sync.WaitGroup) |
| 18 | // passed doneWg will be accounted at p.Wait() call | |
| 18 | 19 | p := mpb.New(mpb.WithWaitGroup(doneWg)) |
| 19 | 20 | numBars := 4 |
| 20 | 21 | |
| 63 | 64 | go newTask(doneWg, b, numBars-i) |
| 64 | 65 | }() |
| 65 | 66 | } |
| 66 | ||
| 67 | // wait for passed doneWg and for all bars to complete and flush | |
| 67 | 68 | p.Wait() |
| 68 | 69 | } |
| 69 | 70 | |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 4 | 4 | "math/rand" |
| 5 | 5 | "time" |
| 6 | 6 | |
| 7 | "github.com/vbauerster/mpb/v6" | |
| 8 | "github.com/vbauerster/mpb/v6/decor" | |
| 7 | "github.com/vbauerster/mpb/v7" | |
| 8 | "github.com/vbauerster/mpb/v7/decor" | |
| 9 | 9 | ) |
| 10 | 10 | |
| 11 | 11 | func main() { |
| 12 | 12 | p := mpb.New() |
| 13 | 13 | |
| 14 | 14 | total := 100 |
| 15 | bar := p.Add(int64(total), nil, | |
| 15 | bar := p.New(int64(total), | |
| 16 | mpb.NopStyle(), // make main bar style nop, so there are just decorators | |
| 17 | mpb.BarExtender(extended(mpb.BarStyle())), // extend wtih normal bar on the next line | |
| 16 | 18 | mpb.PrependDecorators( |
| 17 | 19 | decor.Name("Percentage: "), |
| 18 | 20 | decor.NewPercentage("%d"), |
| 23 | 25 | decor.AverageETA(decor.ET_STYLE_GO), "done", |
| 24 | 26 | ), |
| 25 | 27 | ), |
| 26 | mpb.BarExtender(nlBarFiller(mpb.NewBarFiller("╢▌▌░╟"))), | |
| 27 | 28 | ) |
| 28 | 29 | // simulating some work |
| 29 | 30 | max := 100 * time.Millisecond |
| 35 | 36 | p.Wait() |
| 36 | 37 | } |
| 37 | 38 | |
| 38 | func nlBarFiller(filler mpb.BarFiller) mpb.BarFiller { | |
| 39 | func extended(builder mpb.BarFillerBuilder) mpb.BarFiller { | |
| 40 | filler := builder.Build() | |
| 39 | 41 | return mpb.BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) { |
| 40 | 42 | filler.Fill(w, reqWidth, st) |
| 41 | 43 | w.Write([]byte("\n")) |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func main() { |
| 13 | 13 | var wg sync.WaitGroup |
| 14 | // passed wg will be accounted at p.Wait() call | |
| 14 | 15 | p := mpb.New( |
| 15 | 16 | mpb.WithWaitGroup(&wg), |
| 16 | // container's width. | |
| 17 | 17 | mpb.WithWidth(60), |
| 18 | 18 | ) |
| 19 | 19 | total, numBars := 100, 3 |
| 54 | 54 | } |
| 55 | 55 | }() |
| 56 | 56 | } |
| 57 | // wait for all bars to complete and flush | |
| 57 | // wait for passed wg and for all bars to complete and flush | |
| 58 | 58 | p.Wait() |
| 59 | 59 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 4 | 4 | "math/rand" |
| 5 | 5 | "time" |
| 6 | 6 | |
| 7 | "github.com/vbauerster/mpb/v6" | |
| 8 | "github.com/vbauerster/mpb/v6/decor" | |
| 7 | "github.com/vbauerster/mpb/v7" | |
| 8 | "github.com/vbauerster/mpb/v7/decor" | |
| 9 | 9 | ) |
| 10 | 10 | |
| 11 | 11 | func init() { |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "io/ioutil" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func main() { |
| 18 | 18 | mpb.WithRefreshRate(180*time.Millisecond), |
| 19 | 19 | ) |
| 20 | 20 | |
| 21 | bar := p.Add(total, | |
| 22 | mpb.NewBarFiller("[=>-|"), | |
| 21 | bar := p.New(total, | |
| 22 | mpb.BarStyle().Rbound("|"), | |
| 23 | 23 | mpb.PrependDecorators( |
| 24 | 24 | decor.CountersKibiByte("% .2f / % .2f"), |
| 25 | 25 | ), |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func main() { |
| 13 | 13 | var wg sync.WaitGroup |
| 14 | // pass &wg (optional), so p will wait for it eventually | |
| 14 | // passed wg will be accounted at p.Wait() call | |
| 15 | 15 | p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(60)) |
| 16 | 16 | total, numBars := 100, 3 |
| 17 | 17 | wg.Add(numBars) |
| 58 | 58 | } |
| 59 | 59 | }() |
| 60 | 60 | } |
| 61 | // Waiting for passed &wg and for all bars to complete and flush | |
| 61 | // wait for passed wg and for all bars to complete and flush | |
| 62 | 62 | p.Wait() |
| 63 | 63 | } |
| 64 | 64 | |
| 0 | module github.com/vbauerster/mpb/_examples/mexicanBar | |
| 1 | ||
| 2 | go 1.14 | |
| 3 | ||
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "math/rand" | |
| 4 | "time" | |
| 5 | ||
| 6 | "github.com/vbauerster/mpb/v7" | |
| 7 | "github.com/vbauerster/mpb/v7/decor" | |
| 8 | ) | |
| 9 | ||
| 10 | func main() { | |
| 11 | // initialize progress container, with custom width | |
| 12 | p := mpb.New(mpb.WithWidth(80)) | |
| 13 | ||
| 14 | total := 100 | |
| 15 | name := "Complex Filler:" | |
| 16 | bs := mpb.BarStyle() | |
| 17 | bs.Lbound("[\u001b[36;1m") | |
| 18 | bs.Filler("_") | |
| 19 | bs.Tip("\u001b[0m⛵\u001b[36;1m") | |
| 20 | bs.Padding("_") | |
| 21 | bs.Rbound("\u001b[0m]") | |
| 22 | bar := p.New(int64(total), bs, | |
| 23 | mpb.PrependDecorators(decor.Name(name)), | |
| 24 | mpb.AppendDecorators(decor.Percentage()), | |
| 25 | ) | |
| 26 | // simulating some work | |
| 27 | max := 100 * time.Millisecond | |
| 28 | for i := 0; i < total; i++ { | |
| 29 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) | |
| 30 | bar.Increment() | |
| 31 | } | |
| 32 | // wait for our bar to complete | |
| 33 | p.Wait() | |
| 34 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func main() { |
| 13 | 13 | var wg sync.WaitGroup |
| 14 | // pass &wg (optional), so p will wait for it eventually | |
| 14 | // passed wg will be accounted at p.Wait() call | |
| 15 | 15 | p := mpb.New(mpb.WithWaitGroup(&wg)) |
| 16 | 16 | total, numBars := 100, 3 |
| 17 | 17 | wg.Add(numBars) |
| 29 | 29 | // replace ETA decorator with "done" message, OnComplete event |
| 30 | 30 | decor.OnComplete( |
| 31 | 31 | // ETA decorator with ewma age of 60 |
| 32 | decor.EwmaETA(decor.ET_STYLE_GO, 60), "done", | |
| 32 | decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncWidth), "done", | |
| 33 | 33 | ), |
| 34 | 34 | ), |
| 35 | 35 | ) |
| 49 | 49 | } |
| 50 | 50 | }() |
| 51 | 51 | } |
| 52 | // Waiting for passed &wg and for all bars to complete and flush | |
| 52 | // wait for passed wg and for all bars to complete and flush | |
| 53 | 53 | p.Wait() |
| 54 | 54 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 6 | 6 | "sync" |
| 7 | 7 | "time" |
| 8 | 8 | |
| 9 | "github.com/vbauerster/mpb/v6" | |
| 10 | "github.com/vbauerster/mpb/v6/decor" | |
| 9 | "github.com/vbauerster/mpb/v7" | |
| 10 | "github.com/vbauerster/mpb/v7/decor" | |
| 11 | 11 | ) |
| 12 | 12 | |
| 13 | 13 | func main() { |
| 14 | 14 | var wg sync.WaitGroup |
| 15 | p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithDebugOutput(os.Stderr)) | |
| 15 | // passed wg will be accounted at p.Wait() call | |
| 16 | p := mpb.New( | |
| 17 | mpb.WithWaitGroup(&wg), | |
| 18 | mpb.WithDebugOutput(os.Stderr), | |
| 19 | ) | |
| 16 | 20 | |
| 17 | 21 | wantPanic := strings.Repeat("Panic ", 64) |
| 18 | 22 | numBars := 3 |
| 30 | 34 | } |
| 31 | 35 | }() |
| 32 | 36 | } |
| 33 | ||
| 37 | // wait for passed wg and for all bars to complete and flush | |
| 34 | 38 | p.Wait() |
| 35 | 39 | } |
| 36 | 40 | |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 4 | 4 | "math/rand" |
| 5 | 5 | "time" |
| 6 | 6 | |
| 7 | "github.com/vbauerster/mpb/v6" | |
| 8 | "github.com/vbauerster/mpb/v6/decor" | |
| 7 | "github.com/vbauerster/mpb/v7" | |
| 8 | "github.com/vbauerster/mpb/v7/decor" | |
| 9 | 9 | ) |
| 10 | 10 | |
| 11 | 11 | func main() { |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 6 | 6 | "sync" |
| 7 | 7 | "time" |
| 8 | 8 | |
| 9 | "github.com/vbauerster/mpb/v6" | |
| 10 | "github.com/vbauerster/mpb/v6/decor" | |
| 9 | "github.com/vbauerster/mpb/v7" | |
| 10 | "github.com/vbauerster/mpb/v7/decor" | |
| 11 | 11 | ) |
| 12 | 12 | |
| 13 | 13 | var quietMode bool |
| 19 | 19 | func main() { |
| 20 | 20 | flag.Parse() |
| 21 | 21 | var wg sync.WaitGroup |
| 22 | // pass &wg (optional), so p will wait for it eventually | |
| 22 | // passed wg will be accounted at p.Wait() call | |
| 23 | 23 | p := mpb.New( |
| 24 | 24 | mpb.WithWaitGroup(&wg), |
| 25 | 25 | mpb.ContainerOptional( |
| 67 | 67 | } |
| 68 | 68 | }() |
| 69 | 69 | } |
| 70 | // Waiting for passed &wg and for all bars to complete and flush | |
| 70 | // wait for passed wg and for all bars to complete and flush | |
| 71 | 71 | p.Wait() |
| 72 | 72 | fmt.Println("done") |
| 73 | 73 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func main() { |
| 13 | 13 | var wg sync.WaitGroup |
| 14 | // passed wg will be accounted at p.Wait() call | |
| 14 | 15 | p := mpb.New(mpb.WithWaitGroup(&wg)) |
| 15 | 16 | total := 100 |
| 16 | 17 | numBars := 3 |
| 23 | 24 | mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0), |
| 24 | 25 | mpb.PrependDecorators( |
| 25 | 26 | decor.Name(name), |
| 26 | decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace), | |
| 27 | 27 | ), |
| 28 | mpb.AppendDecorators(decor.Percentage()), | |
| 28 | mpb.AppendDecorators( | |
| 29 | decor.Any(func(s decor.Statistics) string { | |
| 30 | return fmt.Sprintf("completed: %v", s.Completed) | |
| 31 | }, decor.WCSyncSpaceR), | |
| 32 | decor.Any(func(s decor.Statistics) string { | |
| 33 | return fmt.Sprintf("aborted: %v", s.Aborted) | |
| 34 | }, decor.WCSyncSpaceR), | |
| 35 | decor.OnComplete(decor.NewPercentage("%d", decor.WCSyncSpace), "done"), | |
| 36 | decor.OnAbort(decor.NewPercentage("%d", decor.WCSyncSpace), "ohno"), | |
| 37 | ), | |
| 29 | 38 | ) |
| 30 | 39 | go func() { |
| 31 | 40 | defer wg.Done() |
| 32 | 41 | rng := rand.New(rand.NewSource(time.Now().UnixNano())) |
| 33 | 42 | max := 100 * time.Millisecond |
| 34 | 43 | for i := 0; !bar.Completed(); i++ { |
| 35 | // start variable is solely for EWMA calculation | |
| 36 | // EWMA's unit of measure is an iteration's duration | |
| 37 | start := time.Now() | |
| 38 | 44 | if bar.ID() == 2 && i >= 42 { |
| 39 | // aborting and removing while bar is running | |
| 40 | bar.Abort(true) | |
| 45 | bar.Abort(false) | |
| 41 | 46 | } |
| 42 | 47 | time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) |
| 43 | 48 | bar.Increment() |
| 44 | // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract | |
| 45 | bar.DecoratorEwmaUpdate(time.Since(start)) | |
| 46 | 49 | } |
| 47 | 50 | }() |
| 48 | 51 | } |
| 49 | ||
| 52 | // wait for passed wg and for all bars to complete and flush | |
| 50 | 53 | p.Wait() |
| 51 | 54 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func main() { |
| 13 | 13 | var wg sync.WaitGroup |
| 14 | // pass &wg (optional), so p will wait for it eventually | |
| 14 | // passed wg will be accounted at p.Wait() call | |
| 15 | 15 | p := mpb.New(mpb.WithWaitGroup(&wg)) |
| 16 | 16 | total, numBars := 100, 3 |
| 17 | 17 | wg.Add(numBars) |
| 18 | 18 | |
| 19 | 19 | for i := 0; i < numBars; i++ { |
| 20 | 20 | name := fmt.Sprintf("Bar#%d:", i) |
| 21 | bar := p.Add(int64(total), | |
| 22 | // reverse Bar#1 | |
| 23 | mpb.NewBarFillerPick("", i == 1), | |
| 21 | bar := p.New(int64(total), condBuilder(i == 1), | |
| 24 | 22 | mpb.PrependDecorators( |
| 25 | 23 | // simple name decorator |
| 26 | 24 | decor.Name(name), |
| 51 | 49 | } |
| 52 | 50 | }() |
| 53 | 51 | } |
| 54 | // Waiting for passed &wg and for all bars to complete and flush | |
| 52 | // wait for passed wg and for all bars to complete and flush | |
| 55 | 53 | p.Wait() |
| 56 | 54 | } |
| 55 | ||
| 56 | func condBuilder(cond bool) mpb.BarFillerBuilder { | |
| 57 | return mpb.BarFillerBuilderFunc(func() mpb.BarFiller { | |
| 58 | bs := mpb.BarStyle() | |
| 59 | if cond { | |
| 60 | // reverse Bar on cond | |
| 61 | bs = bs.Tip("<").Reverse() | |
| 62 | } | |
| 63 | return bs.Build() | |
| 64 | }) | |
| 65 | } | |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 3 | 3 | "math/rand" |
| 4 | 4 | "time" |
| 5 | 5 | |
| 6 | "github.com/vbauerster/mpb/v6" | |
| 7 | "github.com/vbauerster/mpb/v6/decor" | |
| 6 | "github.com/vbauerster/mpb/v7" | |
| 7 | "github.com/vbauerster/mpb/v7/decor" | |
| 8 | 8 | ) |
| 9 | 9 | |
| 10 | 10 | func main() { |
| 13 | 13 | |
| 14 | 14 | total := 100 |
| 15 | 15 | name := "Single Bar:" |
| 16 | // adding a single bar, which will inherit container's width | |
| 17 | bar := p.Add(int64(total), | |
| 18 | // progress bar filler with customized style | |
| 19 | mpb.NewBarFiller("╢▌▌░╟"), | |
| 16 | // create a single bar, which will inherit container's width | |
| 17 | bar := p.New(int64(total), | |
| 18 | // BarFillerBuilder with custom style | |
| 19 | mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"), | |
| 20 | 20 | mpb.PrependDecorators( |
| 21 | 21 | // display our name with one space on the right |
| 22 | 22 | decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), |
| 0 | module github.com/vbauerster/mpb/_examples/spinTipBar | |
| 1 | ||
| 2 | go 1.14 | |
| 3 | ||
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "math/rand" | |
| 4 | "time" | |
| 5 | ||
| 6 | "github.com/vbauerster/mpb/v7" | |
| 7 | "github.com/vbauerster/mpb/v7/decor" | |
| 8 | ) | |
| 9 | ||
| 10 | func main() { | |
| 11 | // initialize progress container, with custom width | |
| 12 | p := mpb.New(mpb.WithWidth(80)) | |
| 13 | ||
| 14 | total := 100 | |
| 15 | name := "Single Bar:" | |
| 16 | bar := p.New(int64(total), | |
| 17 | mpb.BarStyle().Tip(`-`, `\`, `|`, `/`), | |
| 18 | mpb.PrependDecorators(decor.Name(name)), | |
| 19 | mpb.AppendDecorators(decor.Percentage()), | |
| 20 | ) | |
| 21 | // simulating some work | |
| 22 | max := 100 * time.Millisecond | |
| 23 | for i := 0; i < total; i++ { | |
| 24 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) | |
| 25 | bar.Increment() | |
| 26 | } | |
| 27 | // wait for our bar to complete and flush | |
| 28 | p.Wait() | |
| 29 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func main() { |
| 13 | 13 | var wg sync.WaitGroup |
| 14 | // passed wg will be accounted at p.Wait() call | |
| 14 | 15 | p := mpb.New( |
| 15 | 16 | mpb.WithWaitGroup(&wg), |
| 16 | mpb.WithWidth(14), | |
| 17 | mpb.WithWidth(16), | |
| 17 | 18 | ) |
| 18 | 19 | total, numBars := 101, 3 |
| 19 | 20 | wg.Add(numBars) |
| 20 | 21 | |
| 21 | spinnerStyle := []string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"} | |
| 22 | ||
| 23 | 22 | for i := 0; i < numBars; i++ { |
| 24 | 23 | name := fmt.Sprintf("Bar#%d:", i) |
| 25 | var bar *mpb.Bar | |
| 26 | if i == 0 { | |
| 27 | bar = p.Add(int64(total), | |
| 28 | mpb.NewBarFiller("╢▌▌░╟"), | |
| 29 | mpb.PrependDecorators( | |
| 30 | // simple name decorator | |
| 31 | decor.Name(name), | |
| 24 | bar := p.New(int64(total), condBuilder(i != 0), | |
| 25 | mpb.PrependDecorators( | |
| 26 | // simple name decorator | |
| 27 | decor.Name(name), | |
| 28 | ), | |
| 29 | mpb.AppendDecorators( | |
| 30 | // replace ETA decorator with "done" message, OnComplete event | |
| 31 | decor.OnComplete( | |
| 32 | // ETA decorator with ewma age of 60 | |
| 33 | decor.EwmaETA(decor.ET_STYLE_GO, 60), "done", | |
| 32 | 34 | ), |
| 33 | mpb.AppendDecorators( | |
| 34 | // replace ETA decorator with "done" message, OnComplete event | |
| 35 | decor.OnComplete( | |
| 36 | // ETA decorator with ewma age of 60 | |
| 37 | decor.EwmaETA(decor.ET_STYLE_GO, 60), "done", | |
| 38 | ), | |
| 39 | ), | |
| 40 | ) | |
| 41 | } else { | |
| 42 | bar = p.Add(int64(total), | |
| 43 | mpb.NewSpinnerFiller(spinnerStyle, mpb.SpinnerOnMiddle), | |
| 44 | mpb.PrependDecorators( | |
| 45 | // simple name decorator | |
| 46 | decor.Name(name), | |
| 47 | ), | |
| 48 | mpb.AppendDecorators( | |
| 49 | // replace ETA decorator with "done" message, OnComplete event | |
| 50 | decor.OnComplete( | |
| 51 | // ETA decorator with ewma age of 60 | |
| 52 | decor.EwmaETA(decor.ET_STYLE_GO, 60), "done", | |
| 53 | ), | |
| 54 | ), | |
| 55 | ) | |
| 56 | } | |
| 57 | ||
| 35 | ), | |
| 36 | ) | |
| 58 | 37 | // simulating some work |
| 59 | 38 | go func() { |
| 60 | 39 | defer wg.Done() |
| 71 | 50 | } |
| 72 | 51 | }() |
| 73 | 52 | } |
| 74 | // wait for all bars to complete and flush | |
| 53 | // wait for passed wg and for all bars to complete and flush | |
| 75 | 54 | p.Wait() |
| 76 | 55 | } |
| 56 | ||
| 57 | func condBuilder(cond bool) mpb.BarFillerBuilder { | |
| 58 | return mpb.BarFillerBuilderFunc(func() mpb.BarFiller { | |
| 59 | if cond { | |
| 60 | // spinner Bar on cond | |
| 61 | frames := []string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"} | |
| 62 | return mpb.SpinnerStyle(frames...).Build() | |
| 63 | } | |
| 64 | return mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟").Build() | |
| 65 | }) | |
| 66 | } | |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | func main() { |
| 13 | 13 | var wg sync.WaitGroup |
| 14 | // pass &wg (optional), so p will wait for it eventually | |
| 14 | // passed wg will be accounted at p.Wait() call | |
| 15 | 15 | p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(64)) |
| 16 | 16 | total, numBars := 100, 3 |
| 17 | 17 | wg.Add(numBars) |
| 43 | 43 | } |
| 44 | 44 | }() |
| 45 | 45 | } |
| 46 | // Waiting for passed &wg and for all bars to complete and flush | |
| 46 | // wait for passed wg and for all bars to complete and flush | |
| 47 | 47 | p.Wait() |
| 48 | 48 | } |
| 1 | 1 | |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | require github.com/vbauerster/mpb/v6 v6.0.3 | |
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | 7 | |
| 8 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v7" | |
| 9 | "github.com/vbauerster/mpb/v7/decor" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | const ( |
| 15 | 15 | |
| 16 | 16 | func main() { |
| 17 | 17 | var wg sync.WaitGroup |
| 18 | p := mpb.New( | |
| 19 | mpb.WithWaitGroup(&wg), | |
| 20 | mpb.WithRefreshRate(50*time.Millisecond), | |
| 21 | ) | |
| 18 | // passed wg will be accounted at p.Wait() call | |
| 19 | p := mpb.New(mpb.WithWaitGroup(&wg)) | |
| 22 | 20 | wg.Add(totalBars) |
| 23 | 21 | |
| 24 | 22 | for i := 0; i < totalBars; i++ { |
| 46 | 44 | } |
| 47 | 45 | }() |
| 48 | 46 | } |
| 49 | ||
| 47 | // wait for passed wg and for all bars to complete and flush | |
| 50 | 48 | p.Wait() |
| 51 | 49 | } |
| 2 | 2 | go 1.14 |
| 3 | 3 | |
| 4 | 4 | require ( |
| 5 | github.com/mattn/go-runewidth v0.0.10 | |
| 6 | github.com/vbauerster/mpb/v6 v6.0.3 | |
| 5 | github.com/mattn/go-runewidth v0.0.13 | |
| 6 | github.com/vbauerster/mpb/v7 v7.3.2 | |
| 7 | 7 | ) |
| 8 | 8 | "time" |
| 9 | 9 | |
| 10 | 10 | "github.com/mattn/go-runewidth" |
| 11 | "github.com/vbauerster/mpb/v6" | |
| 12 | "github.com/vbauerster/mpb/v6/decor" | |
| 11 | "github.com/vbauerster/mpb/v7" | |
| 12 | "github.com/vbauerster/mpb/v7/decor" | |
| 13 | 13 | ) |
| 14 | 14 | |
| 15 | 15 | func main() { |
| 0 | module github.com/vbauerster/mpb/_examples/tipOnComplete | |
| 1 | ||
| 2 | go 1.14 | |
| 3 | ||
| 4 | require github.com/vbauerster/mpb/v7 v7.3.2 |
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "math/rand" | |
| 4 | "time" | |
| 5 | ||
| 6 | "github.com/vbauerster/mpb/v7" | |
| 7 | "github.com/vbauerster/mpb/v7/decor" | |
| 8 | ) | |
| 9 | ||
| 10 | func main() { | |
| 11 | // initialize progress container, with custom width | |
| 12 | p := mpb.New(mpb.WithWidth(80)) | |
| 13 | ||
| 14 | total := 100 | |
| 15 | name := "Single Bar:" | |
| 16 | bar := p.New(int64(total), | |
| 17 | mpb.BarStyle().TipOnComplete(">"), | |
| 18 | mpb.PrependDecorators(decor.Name(name)), | |
| 19 | mpb.AppendDecorators(decor.Percentage()), | |
| 20 | ) | |
| 21 | // simulating some work | |
| 22 | max := 100 * time.Millisecond | |
| 23 | for i := 0; i < total; i++ { | |
| 24 | time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) | |
| 25 | bar.Increment() | |
| 26 | } | |
| 27 | // wait for our bar to complete and flush | |
| 28 | p.Wait() | |
| 29 | } |
| 4 | 4 | "context" |
| 5 | 5 | "fmt" |
| 6 | 6 | "io" |
| 7 | "log" | |
| 8 | 7 | "runtime/debug" |
| 9 | 8 | "strings" |
| 9 | "sync" | |
| 10 | 10 | "time" |
| 11 | 11 | |
| 12 | 12 | "github.com/acarl005/stripansi" |
| 13 | 13 | "github.com/mattn/go-runewidth" |
| 14 | "github.com/vbauerster/mpb/v6/decor" | |
| 14 | "github.com/vbauerster/mpb/v7/decor" | |
| 15 | 15 | ) |
| 16 | 16 | |
| 17 | 17 | // Bar represents a progress bar. |
| 19 | 19 | priority int // used by heap |
| 20 | 20 | index int // used by heap |
| 21 | 21 | |
| 22 | extendedLines int | |
| 23 | 22 | toShutdown bool |
| 24 | 23 | toDrop bool |
| 25 | 24 | noPop bool |
| 26 | 25 | hasEwmaDecorators bool |
| 27 | 26 | operateState chan func(*bState) |
| 28 | frameCh chan io.Reader | |
| 29 | syncTableCh chan [][]chan int | |
| 30 | completed chan bool | |
| 27 | frameCh chan *frame | |
| 31 | 28 | |
| 32 | 29 | // cancel is called either by user or on complete event |
| 33 | 30 | cancel func() |
| 34 | 31 | // done is closed after cacheState is assigned |
| 35 | 32 | done chan struct{} |
| 36 | // cacheState is populated, right after close(shutdown) | |
| 33 | // cacheState is populated, right after close(b.done) | |
| 37 | 34 | cacheState *bState |
| 38 | 35 | |
| 39 | 36 | container *Progress |
| 40 | dlogger *log.Logger | |
| 41 | 37 | recoveredPanic interface{} |
| 42 | 38 | } |
| 43 | 39 | |
| 52 | 48 | total int64 |
| 53 | 49 | current int64 |
| 54 | 50 | refill int64 |
| 55 | lastN int64 | |
| 56 | iterated bool | |
| 51 | lastIncrement int64 | |
| 57 | 52 | trimSpace bool |
| 58 | 53 | completed bool |
| 59 | 54 | completeFlushed bool |
| 55 | aborted bool | |
| 60 | 56 | triggerComplete bool |
| 61 | 57 | dropOnComplete bool |
| 62 | 58 | noPop bool |
| 65 | 61 | averageDecorators []decor.AverageDecorator |
| 66 | 62 | ewmaDecorators []decor.EwmaDecorator |
| 67 | 63 | shutdownListeners []decor.ShutdownListener |
| 68 | bufP, bufB, bufA *bytes.Buffer | |
| 64 | buffers [3]*bytes.Buffer | |
| 69 | 65 | filler BarFiller |
| 70 | 66 | middleware func(BarFiller) BarFiller |
| 71 | 67 | extender extenderFunc |
| 76 | 72 | debugOut io.Writer |
| 77 | 73 | } |
| 78 | 74 | |
| 75 | type frame struct { | |
| 76 | reader io.Reader | |
| 77 | lines int | |
| 78 | } | |
| 79 | ||
| 79 | 80 | func newBar(container *Progress, bs *bState) *Bar { |
| 80 | logPrefix := fmt.Sprintf("%sbar#%02d ", container.dlogger.Prefix(), bs.id) | |
| 81 | 81 | ctx, cancel := context.WithCancel(container.ctx) |
| 82 | 82 | |
| 83 | 83 | bar := &Bar{ |
| 86 | 86 | toDrop: bs.dropOnComplete, |
| 87 | 87 | noPop: bs.noPop, |
| 88 | 88 | operateState: make(chan func(*bState)), |
| 89 | frameCh: make(chan io.Reader, 1), | |
| 90 | syncTableCh: make(chan [][]chan int, 1), | |
| 91 | completed: make(chan bool, 1), | |
| 89 | frameCh: make(chan *frame, 1), | |
| 92 | 90 | done: make(chan struct{}), |
| 93 | 91 | cancel: cancel, |
| 94 | dlogger: log.New(bs.debugOut, logPrefix, log.Lshortfile), | |
| 95 | 92 | } |
| 96 | 93 | |
| 97 | 94 | go bar.serve(ctx, bs) |
| 104 | 101 | if r == nil { |
| 105 | 102 | panic("expected non nil io.Reader") |
| 106 | 103 | } |
| 107 | return newProxyReader(r, b) | |
| 104 | return b.newProxyReader(r) | |
| 108 | 105 | } |
| 109 | 106 | |
| 110 | 107 | // ID returs id of the bar. |
| 144 | 141 | |
| 145 | 142 | // TraverseDecorators traverses all available decorators and calls cb func on each. |
| 146 | 143 | func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) { |
| 144 | done := make(chan struct{}) | |
| 147 | 145 | select { |
| 148 | 146 | case b.operateState <- func(s *bState) { |
| 149 | 147 | for _, decorators := range [...][]decor.Decorator{ |
| 154 | 152 | cb(extractBaseDecorator(d)) |
| 155 | 153 | } |
| 156 | 154 | } |
| 157 | }: | |
| 155 | close(done) | |
| 156 | }: | |
| 157 | <-done | |
| 158 | 158 | case <-b.done: |
| 159 | 159 | } |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | 162 | // SetTotal sets total dynamically. |
| 163 | // If total is less than or equal to zero it takes progress' current value. | |
| 163 | // If total is negative it takes progress' current value. | |
| 164 | 164 | func (b *Bar) SetTotal(total int64, triggerComplete bool) { |
| 165 | 165 | select { |
| 166 | 166 | case b.operateState <- func(s *bState) { |
| 167 | 167 | s.triggerComplete = triggerComplete |
| 168 | if total <= 0 { | |
| 168 | if total < 0 { | |
| 169 | 169 | s.total = s.current |
| 170 | 170 | } else { |
| 171 | 171 | s.total = total |
| 173 | 173 | if s.triggerComplete && !s.completed { |
| 174 | 174 | s.current = s.total |
| 175 | 175 | s.completed = true |
| 176 | go b.refreshTillShutdown() | |
| 176 | go b.forceRefreshIfLastUncompleted() | |
| 177 | 177 | } |
| 178 | 178 | }: |
| 179 | 179 | case <-b.done: |
| 185 | 185 | func (b *Bar) SetCurrent(current int64) { |
| 186 | 186 | select { |
| 187 | 187 | case b.operateState <- func(s *bState) { |
| 188 | s.iterated = true | |
| 189 | s.lastN = current - s.current | |
| 188 | s.lastIncrement = current - s.current | |
| 190 | 189 | s.current = current |
| 191 | 190 | if s.triggerComplete && s.current >= s.total { |
| 192 | 191 | s.current = s.total |
| 193 | 192 | s.completed = true |
| 194 | go b.refreshTillShutdown() | |
| 193 | go b.forceRefreshIfLastUncompleted() | |
| 195 | 194 | } |
| 196 | 195 | }: |
| 197 | 196 | case <-b.done: |
| 210 | 209 | |
| 211 | 210 | // IncrInt64 increments progress by amount of n. |
| 212 | 211 | func (b *Bar) IncrInt64(n int64) { |
| 213 | select { | |
| 214 | case b.operateState <- func(s *bState) { | |
| 215 | s.iterated = true | |
| 216 | s.lastN = n | |
| 212 | if n <= 0 { | |
| 213 | return | |
| 214 | } | |
| 215 | select { | |
| 216 | case b.operateState <- func(s *bState) { | |
| 217 | s.lastIncrement = n | |
| 217 | 218 | s.current += n |
| 218 | 219 | if s.triggerComplete && s.current >= s.total { |
| 219 | 220 | s.current = s.total |
| 220 | 221 | s.completed = true |
| 221 | go b.refreshTillShutdown() | |
| 222 | go b.forceRefreshIfLastUncompleted() | |
| 222 | 223 | } |
| 223 | 224 | }: |
| 224 | 225 | case <-b.done: |
| 232 | 233 | func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) { |
| 233 | 234 | select { |
| 234 | 235 | case b.operateState <- func(s *bState) { |
| 235 | ewmaIterationUpdate(false, s, dur) | |
| 236 | }: | |
| 237 | case <-b.done: | |
| 238 | ewmaIterationUpdate(true, b.cacheState, dur) | |
| 236 | if s.lastIncrement > 0 { | |
| 237 | s.decoratorEwmaUpdate(dur) | |
| 238 | s.lastIncrement = 0 | |
| 239 | } else { | |
| 240 | panic("increment required before ewma iteration update") | |
| 241 | } | |
| 242 | }: | |
| 243 | case <-b.done: | |
| 244 | if b.cacheState.lastIncrement > 0 { | |
| 245 | b.cacheState.decoratorEwmaUpdate(dur) | |
| 246 | b.cacheState.lastIncrement = 0 | |
| 247 | } | |
| 239 | 248 | } |
| 240 | 249 | } |
| 241 | 250 | |
| 245 | 254 | func (b *Bar) DecoratorAverageAdjust(start time.Time) { |
| 246 | 255 | select { |
| 247 | 256 | case b.operateState <- func(s *bState) { |
| 248 | for _, d := range s.averageDecorators { | |
| 249 | d.AverageAdjust(start) | |
| 250 | } | |
| 257 | s.decoratorAverageAdjust(start) | |
| 251 | 258 | }: |
| 252 | 259 | case <-b.done: |
| 253 | 260 | } |
| 257 | 264 | // priority, i.e. bar will be on top. If you don't need to set priority |
| 258 | 265 | // dynamically, better use BarPriority option. |
| 259 | 266 | func (b *Bar) SetPriority(priority int) { |
| 260 | select { | |
| 261 | case <-b.done: | |
| 262 | default: | |
| 263 | b.container.setBarPriority(b, priority) | |
| 264 | } | |
| 265 | } | |
| 266 | ||
| 267 | // Abort interrupts bar's running goroutine. Call this, if you'd like | |
| 268 | // to stop/remove bar before completion event. It has no effect after | |
| 269 | // completion event. If drop is true bar will be removed as well. | |
| 267 | b.container.UpdateBarPriority(b, priority) | |
| 268 | } | |
| 269 | ||
| 270 | // Abort interrupts bar's running goroutine. Abort won't be engaged | |
| 271 | // if bar is already in complete state. If drop is true bar will be | |
| 272 | // removed as well. | |
| 270 | 273 | func (b *Bar) Abort(drop bool) { |
| 271 | select { | |
| 272 | case <-b.done: | |
| 273 | default: | |
| 274 | if drop { | |
| 275 | b.container.dropBar(b) | |
| 276 | } | |
| 274 | done := make(chan struct{}) | |
| 275 | select { | |
| 276 | case b.operateState <- func(s *bState) { | |
| 277 | if s.completed { | |
| 278 | close(done) | |
| 279 | return | |
| 280 | } | |
| 281 | s.aborted = true | |
| 277 | 282 | b.cancel() |
| 283 | // container must be run during lifetime of this inner goroutine | |
| 284 | // we control this by done channel declared above | |
| 285 | go func() { | |
| 286 | if drop { | |
| 287 | b.container.dropBar(b) | |
| 288 | } else { | |
| 289 | var uncompleted int | |
| 290 | b.container.traverseBars(func(bar *Bar) bool { | |
| 291 | if b != bar && !bar.Completed() { | |
| 292 | uncompleted++ | |
| 293 | return false | |
| 294 | } | |
| 295 | return true | |
| 296 | }) | |
| 297 | if uncompleted == 0 { | |
| 298 | b.container.refreshCh <- time.Now() | |
| 299 | } | |
| 300 | } | |
| 301 | close(done) // release hold of Abort | |
| 302 | }() | |
| 303 | }: | |
| 304 | // guarantee: container is alive during lifetime of this hold | |
| 305 | <-done | |
| 306 | case <-b.done: | |
| 278 | 307 | } |
| 279 | 308 | } |
| 280 | 309 | |
| 281 | 310 | // Completed reports whether the bar is in completed state. |
| 282 | 311 | func (b *Bar) Completed() bool { |
| 283 | select { | |
| 284 | case b.operateState <- func(s *bState) { b.completed <- s.completed }: | |
| 285 | return <-b.completed | |
| 312 | result := make(chan bool) | |
| 313 | select { | |
| 314 | case b.operateState <- func(s *bState) { result <- s.completed }: | |
| 315 | return <-result | |
| 286 | 316 | case <-b.done: |
| 287 | 317 | return true |
| 288 | 318 | } |
| 295 | 325 | case op := <-b.operateState: |
| 296 | 326 | op(s) |
| 297 | 327 | case <-ctx.Done(): |
| 328 | s.decoratorShutdownNotify() | |
| 298 | 329 | b.cacheState = s |
| 299 | 330 | close(b.done) |
| 300 | // Notifying decorators about shutdown event | |
| 301 | for _, sl := range s.shutdownListeners { | |
| 302 | sl.Shutdown() | |
| 303 | } | |
| 304 | 331 | return |
| 305 | 332 | } |
| 306 | 333 | } |
| 314 | 341 | // recovering if user defined decorator panics for example |
| 315 | 342 | if p := recover(); p != nil { |
| 316 | 343 | if b.recoveredPanic == nil { |
| 344 | if s.debugOut != nil { | |
| 345 | fmt.Fprintln(s.debugOut, p) | |
| 346 | _, _ = s.debugOut.Write(debug.Stack()) | |
| 347 | } | |
| 317 | 348 | s.extender = makePanicExtender(p) |
| 318 | 349 | b.toShutdown = !b.toShutdown |
| 319 | 350 | b.recoveredPanic = p |
| 320 | 351 | } |
| 321 | frame, lines := s.extender(nil, s.reqWidth, stat) | |
| 322 | b.extendedLines = lines | |
| 323 | b.frameCh <- frame | |
| 324 | b.dlogger.Println(p) | |
| 352 | reader, lines := s.extender(nil, s.reqWidth, stat) | |
| 353 | b.frameCh <- &frame{reader, lines + 1} | |
| 325 | 354 | } |
| 326 | 355 | s.completeFlushed = s.completed |
| 327 | 356 | }() |
| 328 | frame, lines := s.extender(s.draw(stat), s.reqWidth, stat) | |
| 329 | b.extendedLines = lines | |
| 357 | reader, lines := s.extender(s.draw(stat), s.reqWidth, stat) | |
| 330 | 358 | b.toShutdown = s.completed && !s.completeFlushed |
| 331 | b.frameCh <- frame | |
| 359 | b.frameCh <- &frame{reader, lines + 1} | |
| 332 | 360 | }: |
| 333 | 361 | case <-b.done: |
| 334 | 362 | s := b.cacheState |
| 337 | 365 | if b.recoveredPanic == nil { |
| 338 | 366 | r = s.draw(stat) |
| 339 | 367 | } |
| 340 | frame, lines := s.extender(r, s.reqWidth, stat) | |
| 341 | b.extendedLines = lines | |
| 342 | b.frameCh <- frame | |
| 368 | reader, lines := s.extender(r, s.reqWidth, stat) | |
| 369 | b.frameCh <- &frame{reader, lines + 1} | |
| 343 | 370 | } |
| 344 | 371 | } |
| 345 | 372 | |
| 358 | 385 | shutdownListeners = append(shutdownListeners, d) |
| 359 | 386 | } |
| 360 | 387 | }) |
| 388 | b.hasEwmaDecorators = len(ewmaDecorators) != 0 | |
| 361 | 389 | select { |
| 362 | 390 | case b.operateState <- func(s *bState) { |
| 363 | 391 | s.averageDecorators = averageDecorators |
| 364 | 392 | s.ewmaDecorators = ewmaDecorators |
| 365 | 393 | s.shutdownListeners = shutdownListeners |
| 366 | 394 | }: |
| 367 | b.hasEwmaDecorators = len(ewmaDecorators) != 0 | |
| 368 | case <-b.done: | |
| 369 | } | |
| 370 | } | |
| 371 | ||
| 372 | func (b *Bar) refreshTillShutdown() { | |
| 373 | for { | |
| 374 | select { | |
| 375 | case b.container.refreshCh <- time.Now(): | |
| 376 | case <-b.done: | |
| 377 | return | |
| 395 | case <-b.done: | |
| 396 | } | |
| 397 | } | |
| 398 | ||
| 399 | func (b *Bar) forceRefreshIfLastUncompleted() { | |
| 400 | var uncompleted int | |
| 401 | b.container.traverseBars(func(bar *Bar) bool { | |
| 402 | if b != bar && !bar.Completed() { | |
| 403 | uncompleted++ | |
| 404 | return false | |
| 405 | } | |
| 406 | return true | |
| 407 | }) | |
| 408 | if uncompleted == 0 { | |
| 409 | for { | |
| 410 | select { | |
| 411 | case b.container.refreshCh <- time.Now(): | |
| 412 | case <-b.done: | |
| 413 | return | |
| 414 | } | |
| 378 | 415 | } |
| 379 | 416 | } |
| 380 | 417 | } |
| 381 | 418 | |
| 382 | 419 | func (b *Bar) wSyncTable() [][]chan int { |
| 383 | select { | |
| 384 | case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }: | |
| 385 | return <-b.syncTableCh | |
| 420 | result := make(chan [][]chan int) | |
| 421 | select { | |
| 422 | case b.operateState <- func(s *bState) { result <- s.wSyncTable() }: | |
| 423 | return <-result | |
| 386 | 424 | case <-b.done: |
| 387 | 425 | return b.cacheState.wSyncTable() |
| 388 | 426 | } |
| 389 | 427 | } |
| 390 | 428 | |
| 391 | 429 | func (s *bState) draw(stat decor.Statistics) io.Reader { |
| 392 | if !s.trimSpace { | |
| 393 | stat.AvailableWidth -= 2 | |
| 394 | s.bufB.WriteByte(' ') | |
| 395 | defer s.bufB.WriteByte(' ') | |
| 396 | } | |
| 397 | ||
| 430 | bufP, bufB, bufA := s.buffers[0], s.buffers[1], s.buffers[2] | |
| 398 | 431 | nlr := strings.NewReader("\n") |
| 399 | 432 | tw := stat.AvailableWidth |
| 400 | 433 | for _, d := range s.pDecorators { |
| 401 | 434 | str := d.Decor(stat) |
| 402 | 435 | stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str)) |
| 403 | s.bufP.WriteString(str) | |
| 404 | } | |
| 405 | if stat.AvailableWidth <= 0 { | |
| 406 | trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufP.String()), tw, "…")) | |
| 407 | s.bufP.Reset() | |
| 408 | return io.MultiReader(trunc, s.bufB, nlr) | |
| 436 | bufP.WriteString(str) | |
| 437 | } | |
| 438 | if stat.AvailableWidth < 1 { | |
| 439 | trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufP.String()), tw, "…")) | |
| 440 | bufP.Reset() | |
| 441 | return io.MultiReader(trunc, nlr) | |
| 442 | } | |
| 443 | ||
| 444 | if !s.trimSpace && stat.AvailableWidth > 1 { | |
| 445 | stat.AvailableWidth -= 2 | |
| 446 | bufB.WriteByte(' ') | |
| 447 | defer bufB.WriteByte(' ') | |
| 409 | 448 | } |
| 410 | 449 | |
| 411 | 450 | tw = stat.AvailableWidth |
| 412 | 451 | for _, d := range s.aDecorators { |
| 413 | 452 | str := d.Decor(stat) |
| 414 | 453 | stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str)) |
| 415 | s.bufA.WriteString(str) | |
| 416 | } | |
| 417 | if stat.AvailableWidth <= 0 { | |
| 418 | trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufA.String()), tw, "…")) | |
| 419 | s.bufA.Reset() | |
| 420 | return io.MultiReader(s.bufP, s.bufB, trunc, nlr) | |
| 421 | } | |
| 422 | ||
| 423 | s.filler.Fill(s.bufB, s.reqWidth, stat) | |
| 424 | ||
| 425 | return io.MultiReader(s.bufP, s.bufB, s.bufA, nlr) | |
| 454 | bufA.WriteString(str) | |
| 455 | } | |
| 456 | if stat.AvailableWidth < 1 { | |
| 457 | trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufA.String()), tw, "…")) | |
| 458 | bufA.Reset() | |
| 459 | return io.MultiReader(bufP, bufB, trunc, nlr) | |
| 460 | } | |
| 461 | ||
| 462 | s.filler.Fill(bufB, s.reqWidth, stat) | |
| 463 | ||
| 464 | return io.MultiReader(bufP, bufB, bufA, nlr) | |
| 426 | 465 | } |
| 427 | 466 | |
| 428 | 467 | func (s *bState) wSyncTable() [][]chan int { |
| 445 | 484 | table[0] = columns[0:pCount] |
| 446 | 485 | table[1] = columns[pCount : pCount+aCount : pCount+aCount] |
| 447 | 486 | return table |
| 487 | } | |
| 488 | ||
| 489 | func (s bState) decoratorEwmaUpdate(dur time.Duration) { | |
| 490 | wg := new(sync.WaitGroup) | |
| 491 | for i := 0; i < len(s.ewmaDecorators); i++ { | |
| 492 | switch d := s.ewmaDecorators[i]; i { | |
| 493 | case len(s.ewmaDecorators) - 1: | |
| 494 | d.EwmaUpdate(s.lastIncrement, dur) | |
| 495 | default: | |
| 496 | wg.Add(1) | |
| 497 | go func() { | |
| 498 | d.EwmaUpdate(s.lastIncrement, dur) | |
| 499 | wg.Done() | |
| 500 | }() | |
| 501 | } | |
| 502 | } | |
| 503 | wg.Wait() | |
| 504 | } | |
| 505 | ||
| 506 | func (s bState) decoratorAverageAdjust(start time.Time) { | |
| 507 | wg := new(sync.WaitGroup) | |
| 508 | for i := 0; i < len(s.averageDecorators); i++ { | |
| 509 | switch d := s.averageDecorators[i]; i { | |
| 510 | case len(s.averageDecorators) - 1: | |
| 511 | d.AverageAdjust(start) | |
| 512 | default: | |
| 513 | wg.Add(1) | |
| 514 | go func() { | |
| 515 | d.AverageAdjust(start) | |
| 516 | wg.Done() | |
| 517 | }() | |
| 518 | } | |
| 519 | } | |
| 520 | wg.Wait() | |
| 521 | } | |
| 522 | ||
| 523 | func (s bState) decoratorShutdownNotify() { | |
| 524 | wg := new(sync.WaitGroup) | |
| 525 | for i := 0; i < len(s.shutdownListeners); i++ { | |
| 526 | switch d := s.shutdownListeners[i]; i { | |
| 527 | case len(s.shutdownListeners) - 1: | |
| 528 | d.Shutdown() | |
| 529 | default: | |
| 530 | wg.Add(1) | |
| 531 | go func() { | |
| 532 | d.Shutdown() | |
| 533 | wg.Done() | |
| 534 | }() | |
| 535 | } | |
| 536 | } | |
| 537 | wg.Wait() | |
| 448 | 538 | } |
| 449 | 539 | |
| 450 | 540 | func newStatistics(tw int, s *bState) decor.Statistics { |
| 455 | 545 | Current: s.current, |
| 456 | 546 | Refill: s.refill, |
| 457 | 547 | Completed: s.completeFlushed, |
| 548 | Aborted: s.aborted, | |
| 458 | 549 | } |
| 459 | 550 | } |
| 460 | 551 | |
| 465 | 556 | return d |
| 466 | 557 | } |
| 467 | 558 | |
| 468 | func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) { | |
| 469 | if !done && !s.iterated { | |
| 470 | panic("increment required before ewma iteration update") | |
| 471 | } else { | |
| 472 | s.iterated = false | |
| 473 | } | |
| 474 | for _, d := range s.ewmaDecorators { | |
| 475 | d.EwmaUpdate(s.lastN, dur) | |
| 476 | } | |
| 477 | } | |
| 478 | ||
| 479 | 559 | func makePanicExtender(p interface{}) extenderFunc { |
| 480 | 560 | pstr := fmt.Sprint(p) |
| 481 | stack := debug.Stack() | |
| 482 | stackLines := bytes.Count(stack, []byte("\n")) | |
| 483 | 561 | return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) { |
| 484 | 562 | mr := io.MultiReader( |
| 485 | 563 | strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")), |
| 486 | strings.NewReader(fmt.Sprintf("\n%#v\n", st)), | |
| 487 | bytes.NewReader(stack), | |
| 564 | strings.NewReader("\n"), | |
| 488 | 565 | ) |
| 489 | return mr, stackLines + 1 | |
| 490 | } | |
| 491 | } | |
| 566 | return mr, 0 | |
| 567 | } | |
| 568 | } | |
| 2 | 2 | import ( |
| 3 | 3 | "io" |
| 4 | 4 | |
| 5 | "github.com/vbauerster/mpb/v6/decor" | |
| 5 | "github.com/vbauerster/mpb/v7/decor" | |
| 6 | 6 | ) |
| 7 | 7 | |
| 8 | 8 | // BarFiller interface. |
| 9 | 9 | // Bar (without decorators) renders itself by calling BarFiller's Fill method. |
| 10 | 10 | // |
| 11 | // reqWidth is requested width, set by `func WithWidth(int) ContainerOption`. | |
| 11 | // reqWidth is requested width set by `func WithWidth(int) ContainerOption`. | |
| 12 | 12 | // If not set, it defaults to terminal width. |
| 13 | // | |
| 14 | // Default implementations can be obtained via: | |
| 15 | // | |
| 16 | // func NewBarFiller(style string) BarFiller | |
| 17 | // func NewBarFillerRev(style string) BarFiller | |
| 18 | // func NewBarFillerPick(style string, rev bool) BarFiller | |
| 19 | // func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller | |
| 20 | 13 | // |
| 21 | 14 | type BarFiller interface { |
| 22 | 15 | Fill(w io.Writer, reqWidth int, stat decor.Statistics) |
| 23 | 16 | } |
| 24 | 17 | |
| 25 | // BarFillerFunc is function type adapter to convert function into BarFiller. | |
| 18 | // BarFillerBuilder interface. | |
| 19 | // Default implementations are: | |
| 20 | // | |
| 21 | // BarStyle() | |
| 22 | // SpinnerStyle() | |
| 23 | // NopStyle() | |
| 24 | // | |
| 25 | type BarFillerBuilder interface { | |
| 26 | Build() BarFiller | |
| 27 | } | |
| 28 | ||
| 29 | // BarFillerFunc is function type adapter to convert compatible function | |
| 30 | // into BarFiller interface. | |
| 26 | 31 | type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics) |
| 27 | 32 | |
| 28 | 33 | func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { |
| 29 | 34 | f(w, reqWidth, stat) |
| 30 | 35 | } |
| 36 | ||
| 37 | // BarFillerBuilderFunc is function type adapter to convert compatible | |
| 38 | // function into BarFillerBuilder interface. | |
| 39 | type BarFillerBuilderFunc func() BarFiller | |
| 40 | ||
| 41 | func (f BarFillerBuilderFunc) Build() BarFiller { | |
| 42 | return f() | |
| 43 | } | |
| 44 | ||
| 45 | // NewBarFiller constructs a BarFiller from provided BarFillerBuilder. | |
| 46 | // Deprecated. Prefer using `*Progress.New(...)` directly. | |
| 47 | func NewBarFiller(b BarFillerBuilder) BarFiller { | |
| 48 | return b.Build() | |
| 49 | } |
| 0 | 0 | package mpb |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | "bytes" | |
| 4 | 3 | "io" |
| 5 | "unicode/utf8" | |
| 6 | ||
| 4 | ||
| 5 | "github.com/acarl005/stripansi" | |
| 7 | 6 | "github.com/mattn/go-runewidth" |
| 8 | "github.com/rivo/uniseg" | |
| 9 | "github.com/vbauerster/mpb/v6/decor" | |
| 10 | "github.com/vbauerster/mpb/v6/internal" | |
| 7 | "github.com/vbauerster/mpb/v7/decor" | |
| 8 | "github.com/vbauerster/mpb/v7/internal" | |
| 11 | 9 | ) |
| 12 | 10 | |
| 13 | 11 | const ( |
| 14 | rLeft = iota | |
| 15 | rFill | |
| 16 | rTip | |
| 17 | rSpace | |
| 18 | rRight | |
| 19 | rRevTip | |
| 20 | rRefill | |
| 12 | iLbound = iota | |
| 13 | iRbound | |
| 14 | iFiller | |
| 15 | iRefiller | |
| 16 | iPadding | |
| 17 | components | |
| 21 | 18 | ) |
| 22 | 19 | |
| 23 | // BarDefaultStyle is a style for rendering a progress bar. | |
| 24 | // It consist of 7 ordered runes: | |
| 25 | // | |
| 26 | // '1st rune' stands for left boundary rune | |
| 27 | // | |
| 28 | // '2nd rune' stands for fill rune | |
| 29 | // | |
| 30 | // '3rd rune' stands for tip rune | |
| 31 | // | |
| 32 | // '4th rune' stands for space rune | |
| 33 | // | |
| 34 | // '5th rune' stands for right boundary rune | |
| 35 | // | |
| 36 | // '6th rune' stands for reverse tip rune | |
| 37 | // | |
| 38 | // '7th rune' stands for refill rune | |
| 39 | // | |
| 40 | const BarDefaultStyle string = "[=>-]<+" | |
| 41 | ||
| 42 | type barFiller struct { | |
| 43 | format [][]byte | |
| 44 | rwidth []int | |
| 45 | tip []byte | |
| 46 | refill int64 | |
| 47 | reverse bool | |
| 48 | flush func(io.Writer, *space, [][]byte) | |
| 49 | } | |
| 50 | ||
| 51 | type space struct { | |
| 52 | space []byte | |
| 53 | rwidth int | |
| 54 | count int | |
| 55 | } | |
| 56 | ||
| 57 | // NewBarFiller returns a BarFiller implementation which renders a | |
| 58 | // progress bar in regular direction. If style is empty string, | |
| 59 | // BarDefaultStyle is applied. To be used with `*Progress.Add(...) | |
| 60 | // *Bar` method. | |
| 61 | func NewBarFiller(style string) BarFiller { | |
| 62 | return newBarFiller(style, false) | |
| 63 | } | |
| 64 | ||
| 65 | // NewBarFillerRev returns a BarFiller implementation which renders a | |
| 66 | // progress bar in reverse direction. If style is empty string, | |
| 67 | // BarDefaultStyle is applied. To be used with `*Progress.Add(...) | |
| 68 | // *Bar` method. | |
| 69 | func NewBarFillerRev(style string) BarFiller { | |
| 70 | return newBarFiller(style, true) | |
| 71 | } | |
| 72 | ||
| 73 | // NewBarFillerPick pick between regular and reverse BarFiller implementation | |
| 74 | // based on rev param. To be used with `*Progress.Add(...) *Bar` method. | |
| 75 | func NewBarFillerPick(style string, rev bool) BarFiller { | |
| 76 | return newBarFiller(style, rev) | |
| 77 | } | |
| 78 | ||
| 79 | func newBarFiller(style string, rev bool) BarFiller { | |
| 80 | bf := &barFiller{ | |
| 81 | format: make([][]byte, len(BarDefaultStyle)), | |
| 82 | rwidth: make([]int, len(BarDefaultStyle)), | |
| 83 | reverse: rev, | |
| 84 | } | |
| 85 | bf.parse(BarDefaultStyle) | |
| 86 | if style != "" && style != BarDefaultStyle { | |
| 87 | bf.parse(style) | |
| 20 | // BarStyleComposer interface. | |
| 21 | type BarStyleComposer interface { | |
| 22 | BarFillerBuilder | |
| 23 | Lbound(string) BarStyleComposer | |
| 24 | Rbound(string) BarStyleComposer | |
| 25 | Filler(string) BarStyleComposer | |
| 26 | Refiller(string) BarStyleComposer | |
| 27 | Padding(string) BarStyleComposer | |
| 28 | TipOnComplete(string) BarStyleComposer | |
| 29 | Tip(frames ...string) BarStyleComposer | |
| 30 | Reverse() BarStyleComposer | |
| 31 | } | |
| 32 | ||
| 33 | type bFiller struct { | |
| 34 | rev bool | |
| 35 | components [components]*component | |
| 36 | tip struct { | |
| 37 | count uint | |
| 38 | onComplete *component | |
| 39 | frames []*component | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | type component struct { | |
| 44 | width int | |
| 45 | bytes []byte | |
| 46 | } | |
| 47 | ||
| 48 | type barStyle struct { | |
| 49 | lbound string | |
| 50 | rbound string | |
| 51 | filler string | |
| 52 | refiller string | |
| 53 | padding string | |
| 54 | tipOnComplete string | |
| 55 | tipFrames []string | |
| 56 | rev bool | |
| 57 | } | |
| 58 | ||
| 59 | // BarStyle constructs default bar style which can be altered via | |
| 60 | // BarStyleComposer interface. | |
| 61 | func BarStyle() BarStyleComposer { | |
| 62 | return &barStyle{ | |
| 63 | lbound: "[", | |
| 64 | rbound: "]", | |
| 65 | filler: "=", | |
| 66 | refiller: "+", | |
| 67 | padding: "-", | |
| 68 | tipFrames: []string{">"}, | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | func (s *barStyle) Lbound(bound string) BarStyleComposer { | |
| 73 | s.lbound = bound | |
| 74 | return s | |
| 75 | } | |
| 76 | ||
| 77 | func (s *barStyle) Rbound(bound string) BarStyleComposer { | |
| 78 | s.rbound = bound | |
| 79 | return s | |
| 80 | } | |
| 81 | ||
| 82 | func (s *barStyle) Filler(filler string) BarStyleComposer { | |
| 83 | s.filler = filler | |
| 84 | return s | |
| 85 | } | |
| 86 | ||
| 87 | func (s *barStyle) Refiller(refiller string) BarStyleComposer { | |
| 88 | s.refiller = refiller | |
| 89 | return s | |
| 90 | } | |
| 91 | ||
| 92 | func (s *barStyle) Padding(padding string) BarStyleComposer { | |
| 93 | s.padding = padding | |
| 94 | return s | |
| 95 | } | |
| 96 | ||
| 97 | func (s *barStyle) TipOnComplete(tip string) BarStyleComposer { | |
| 98 | s.tipOnComplete = tip | |
| 99 | return s | |
| 100 | } | |
| 101 | ||
| 102 | func (s *barStyle) Tip(frames ...string) BarStyleComposer { | |
| 103 | if len(frames) != 0 { | |
| 104 | s.tipFrames = append(s.tipFrames[:0], frames...) | |
| 105 | } | |
| 106 | return s | |
| 107 | } | |
| 108 | ||
| 109 | func (s *barStyle) Reverse() BarStyleComposer { | |
| 110 | s.rev = true | |
| 111 | return s | |
| 112 | } | |
| 113 | ||
| 114 | func (s *barStyle) Build() BarFiller { | |
| 115 | bf := &bFiller{rev: s.rev} | |
| 116 | bf.components[iLbound] = &component{ | |
| 117 | width: runewidth.StringWidth(stripansi.Strip(s.lbound)), | |
| 118 | bytes: []byte(s.lbound), | |
| 119 | } | |
| 120 | bf.components[iRbound] = &component{ | |
| 121 | width: runewidth.StringWidth(stripansi.Strip(s.rbound)), | |
| 122 | bytes: []byte(s.rbound), | |
| 123 | } | |
| 124 | bf.components[iFiller] = &component{ | |
| 125 | width: runewidth.StringWidth(stripansi.Strip(s.filler)), | |
| 126 | bytes: []byte(s.filler), | |
| 127 | } | |
| 128 | bf.components[iRefiller] = &component{ | |
| 129 | width: runewidth.StringWidth(stripansi.Strip(s.refiller)), | |
| 130 | bytes: []byte(s.refiller), | |
| 131 | } | |
| 132 | bf.components[iPadding] = &component{ | |
| 133 | width: runewidth.StringWidth(stripansi.Strip(s.padding)), | |
| 134 | bytes: []byte(s.padding), | |
| 135 | } | |
| 136 | bf.tip.onComplete = &component{ | |
| 137 | width: runewidth.StringWidth(stripansi.Strip(s.tipOnComplete)), | |
| 138 | bytes: []byte(s.tipOnComplete), | |
| 139 | } | |
| 140 | bf.tip.frames = make([]*component, len(s.tipFrames)) | |
| 141 | for i, t := range s.tipFrames { | |
| 142 | bf.tip.frames[i] = &component{ | |
| 143 | width: runewidth.StringWidth(stripansi.Strip(t)), | |
| 144 | bytes: []byte(t), | |
| 145 | } | |
| 88 | 146 | } |
| 89 | 147 | return bf |
| 90 | 148 | } |
| 91 | 149 | |
| 92 | func (s *barFiller) parse(style string) { | |
| 93 | if !utf8.ValidString(style) { | |
| 94 | panic("invalid bar style") | |
| 95 | } | |
| 96 | srcFormat := make([][]byte, len(BarDefaultStyle)) | |
| 97 | srcRwidth := make([]int, len(BarDefaultStyle)) | |
| 98 | i := 0 | |
| 99 | for gr := uniseg.NewGraphemes(style); i < len(BarDefaultStyle) && gr.Next(); i++ { | |
| 100 | srcFormat[i] = gr.Bytes() | |
| 101 | srcRwidth[i] = runewidth.StringWidth(gr.Str()) | |
| 102 | } | |
| 103 | copy(s.format, srcFormat[:i]) | |
| 104 | copy(s.rwidth, srcRwidth[:i]) | |
| 105 | if s.reverse { | |
| 106 | s.tip = s.format[rRevTip] | |
| 107 | s.flush = reverseFlush | |
| 108 | } else { | |
| 109 | s.tip = s.format[rTip] | |
| 110 | s.flush = regularFlush | |
| 111 | } | |
| 112 | } | |
| 113 | ||
| 114 | func (s *barFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { | |
| 115 | width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth) | |
| 116 | brackets := s.rwidth[rLeft] + s.rwidth[rRight] | |
| 117 | if width < brackets { | |
| 118 | return | |
| 119 | } | |
| 150 | func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) { | |
| 151 | width = internal.CheckRequestedWidth(width, stat.AvailableWidth) | |
| 152 | brackets := s.components[iLbound].width + s.components[iRbound].width | |
| 120 | 153 | // don't count brackets as progress |
| 121 | 154 | width -= brackets |
| 122 | ||
| 123 | w.Write(s.format[rLeft]) | |
| 124 | defer w.Write(s.format[rRight]) | |
| 125 | ||
| 126 | cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width)) | |
| 127 | space := &space{ | |
| 128 | space: s.format[rSpace], | |
| 129 | rwidth: s.rwidth[rSpace], | |
| 130 | count: width - cwidth, | |
| 131 | } | |
| 132 | ||
| 133 | index, refill := 0, 0 | |
| 134 | bb := make([][]byte, cwidth) | |
| 135 | ||
| 136 | if cwidth > 0 && cwidth != width { | |
| 137 | bb[index] = s.tip | |
| 138 | cwidth -= s.rwidth[rTip] | |
| 139 | index++ | |
| 155 | if width < 0 { | |
| 156 | return | |
| 157 | } | |
| 158 | ||
| 159 | ow := optimisticWriter(w) | |
| 160 | ow(s.components[iLbound].bytes) | |
| 161 | defer ow(s.components[iRbound].bytes) | |
| 162 | ||
| 163 | if width == 0 { | |
| 164 | return | |
| 165 | } | |
| 166 | ||
| 167 | var filling [][]byte | |
| 168 | var padding [][]byte | |
| 169 | var tip *component | |
| 170 | var filled int | |
| 171 | var refWidth int | |
| 172 | curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width))) | |
| 173 | ||
| 174 | if stat.Current >= stat.Total { | |
| 175 | tip = s.tip.onComplete | |
| 176 | } else { | |
| 177 | tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))] | |
| 178 | } | |
| 179 | ||
| 180 | if curWidth > 0 { | |
| 181 | filling = append(filling, tip.bytes) | |
| 182 | filled += tip.width | |
| 183 | s.tip.count++ | |
| 140 | 184 | } |
| 141 | 185 | |
| 142 | 186 | if stat.Refill > 0 { |
| 143 | refill = int(internal.PercentageRound(stat.Total, int64(stat.Refill), width)) | |
| 144 | if refill > cwidth { | |
| 145 | refill = cwidth | |
| 146 | } | |
| 147 | cwidth -= refill | |
| 148 | } | |
| 149 | ||
| 150 | for cwidth > 0 { | |
| 151 | bb[index] = s.format[rFill] | |
| 152 | cwidth -= s.rwidth[rFill] | |
| 153 | index++ | |
| 154 | } | |
| 155 | ||
| 156 | for refill > 0 { | |
| 157 | bb[index] = s.format[rRefill] | |
| 158 | refill -= s.rwidth[rRefill] | |
| 159 | index++ | |
| 160 | } | |
| 161 | ||
| 162 | if cwidth+refill < 0 || space.rwidth > 1 { | |
| 163 | buf := new(bytes.Buffer) | |
| 164 | s.flush(buf, space, bb[:index]) | |
| 165 | io.WriteString(w, runewidth.Truncate(buf.String(), width, "…")) | |
| 166 | return | |
| 167 | } | |
| 168 | ||
| 169 | s.flush(w, space, bb) | |
| 170 | } | |
| 171 | ||
| 172 | func regularFlush(w io.Writer, space *space, bb [][]byte) { | |
| 173 | for i := len(bb) - 1; i >= 0; i-- { | |
| 174 | w.Write(bb[i]) | |
| 175 | } | |
| 176 | for space.count > 0 { | |
| 177 | w.Write(space.space) | |
| 178 | space.count -= space.rwidth | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | func reverseFlush(w io.Writer, space *space, bb [][]byte) { | |
| 183 | for space.count > 0 { | |
| 184 | w.Write(space.space) | |
| 185 | space.count -= space.rwidth | |
| 186 | } | |
| 187 | for i := 0; i < len(bb); i++ { | |
| 188 | w.Write(bb[i]) | |
| 189 | } | |
| 190 | } | |
| 187 | refWidth = int(internal.PercentageRound(stat.Total, stat.Refill, uint(width))) | |
| 188 | curWidth -= refWidth | |
| 189 | refWidth += curWidth | |
| 190 | } | |
| 191 | ||
| 192 | for filled < curWidth { | |
| 193 | if curWidth-filled >= s.components[iFiller].width { | |
| 194 | filling = append(filling, s.components[iFiller].bytes) | |
| 195 | if s.components[iFiller].width == 0 { | |
| 196 | break | |
| 197 | } | |
| 198 | filled += s.components[iFiller].width | |
| 199 | } else { | |
| 200 | filling = append(filling, []byte("…")) | |
| 201 | filled++ | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | for filled < refWidth { | |
| 206 | if refWidth-filled >= s.components[iRefiller].width { | |
| 207 | filling = append(filling, s.components[iRefiller].bytes) | |
| 208 | if s.components[iRefiller].width == 0 { | |
| 209 | break | |
| 210 | } | |
| 211 | filled += s.components[iRefiller].width | |
| 212 | } else { | |
| 213 | filling = append(filling, []byte("…")) | |
| 214 | filled++ | |
| 215 | } | |
| 216 | } | |
| 217 | ||
| 218 | padWidth := width - filled | |
| 219 | for padWidth > 0 { | |
| 220 | if padWidth >= s.components[iPadding].width { | |
| 221 | padding = append(padding, s.components[iPadding].bytes) | |
| 222 | if s.components[iPadding].width == 0 { | |
| 223 | break | |
| 224 | } | |
| 225 | padWidth -= s.components[iPadding].width | |
| 226 | } else { | |
| 227 | padding = append(padding, []byte("…")) | |
| 228 | padWidth-- | |
| 229 | } | |
| 230 | } | |
| 231 | ||
| 232 | if s.rev { | |
| 233 | flush(ow, padding, filling) | |
| 234 | } else { | |
| 235 | flush(ow, filling, padding) | |
| 236 | } | |
| 237 | } | |
| 238 | ||
| 239 | func flush(ow func([]byte), filling, padding [][]byte) { | |
| 240 | for i := len(filling) - 1; i >= 0; i-- { | |
| 241 | ow(filling[i]) | |
| 242 | } | |
| 243 | for i := 0; i < len(padding); i++ { | |
| 244 | ow(padding[i]) | |
| 245 | } | |
| 246 | } | |
| 247 | ||
| 248 | func optimisticWriter(w io.Writer) func([]byte) { | |
| 249 | return func(p []byte) { | |
| 250 | _, err := w.Write(p) | |
| 251 | if err != nil { | |
| 252 | panic(err) | |
| 253 | } | |
| 254 | } | |
| 255 | } |
| 0 | package mpb | |
| 1 | ||
| 2 | import ( | |
| 3 | "io" | |
| 4 | ||
| 5 | "github.com/vbauerster/mpb/v7/decor" | |
| 6 | ) | |
| 7 | ||
| 8 | // NopStyle provides BarFillerBuilder which builds NOP BarFiller. | |
| 9 | func NopStyle() BarFillerBuilder { | |
| 10 | return BarFillerBuilderFunc(func() BarFiller { | |
| 11 | return BarFillerFunc(func(io.Writer, int, decor.Statistics) {}) | |
| 12 | }) | |
| 13 | } |
| 3 | 3 | "io" |
| 4 | 4 | "strings" |
| 5 | 5 | |
| 6 | "github.com/acarl005/stripansi" | |
| 6 | 7 | "github.com/mattn/go-runewidth" |
| 7 | "github.com/vbauerster/mpb/v6/decor" | |
| 8 | "github.com/vbauerster/mpb/v6/internal" | |
| 8 | "github.com/vbauerster/mpb/v7/decor" | |
| 9 | "github.com/vbauerster/mpb/v7/internal" | |
| 9 | 10 | ) |
| 10 | 11 | |
| 11 | // SpinnerAlignment enum. | |
| 12 | type SpinnerAlignment int | |
| 13 | ||
| 14 | // SpinnerAlignment kinds. | |
| 15 | 12 | const ( |
| 16 | SpinnerOnLeft SpinnerAlignment = iota | |
| 17 | SpinnerOnMiddle | |
| 18 | SpinnerOnRight | |
| 13 | positionLeft = 1 + iota | |
| 14 | positionRight | |
| 19 | 15 | ) |
| 20 | 16 | |
| 21 | // SpinnerDefaultStyle is a style for rendering a spinner. | |
| 22 | var SpinnerDefaultStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} | |
| 23 | ||
| 24 | type spinnerFiller struct { | |
| 25 | frames []string | |
| 26 | count uint | |
| 27 | alignment SpinnerAlignment | |
| 17 | // SpinnerStyleComposer interface. | |
| 18 | type SpinnerStyleComposer interface { | |
| 19 | BarFillerBuilder | |
| 20 | PositionLeft() SpinnerStyleComposer | |
| 21 | PositionRight() SpinnerStyleComposer | |
| 28 | 22 | } |
| 29 | 23 | |
| 30 | // NewSpinnerFiller returns a BarFiller implementation which renders | |
| 31 | // a spinner. If style is nil or zero length, SpinnerDefaultStyle is | |
| 32 | // applied. To be used with `*Progress.Add(...) *Bar` method. | |
| 33 | func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller { | |
| 34 | if len(style) == 0 { | |
| 35 | style = SpinnerDefaultStyle | |
| 36 | } | |
| 37 | filler := &spinnerFiller{ | |
| 38 | frames: style, | |
| 39 | alignment: alignment, | |
| 40 | } | |
| 41 | return filler | |
| 24 | type sFiller struct { | |
| 25 | count uint | |
| 26 | position uint | |
| 27 | frames []string | |
| 42 | 28 | } |
| 43 | 29 | |
| 44 | func (s *spinnerFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { | |
| 45 | width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth) | |
| 30 | type spinnerStyle struct { | |
| 31 | position uint | |
| 32 | frames []string | |
| 33 | } | |
| 34 | ||
| 35 | // SpinnerStyle constructs default spinner style which can be altered via | |
| 36 | // SpinnerStyleComposer interface. | |
| 37 | func SpinnerStyle(frames ...string) SpinnerStyleComposer { | |
| 38 | ss := new(spinnerStyle) | |
| 39 | if len(frames) != 0 { | |
| 40 | ss.frames = append(ss.frames, frames...) | |
| 41 | } else { | |
| 42 | ss.frames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} | |
| 43 | } | |
| 44 | return ss | |
| 45 | } | |
| 46 | ||
| 47 | func (s *spinnerStyle) PositionLeft() SpinnerStyleComposer { | |
| 48 | s.position = positionLeft | |
| 49 | return s | |
| 50 | } | |
| 51 | ||
| 52 | func (s *spinnerStyle) PositionRight() SpinnerStyleComposer { | |
| 53 | s.position = positionRight | |
| 54 | return s | |
| 55 | } | |
| 56 | ||
| 57 | func (s *spinnerStyle) Build() BarFiller { | |
| 58 | sf := &sFiller{ | |
| 59 | position: s.position, | |
| 60 | frames: s.frames, | |
| 61 | } | |
| 62 | return sf | |
| 63 | } | |
| 64 | ||
| 65 | func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) { | |
| 66 | width = internal.CheckRequestedWidth(width, stat.AvailableWidth) | |
| 46 | 67 | |
| 47 | 68 | frame := s.frames[s.count%uint(len(s.frames))] |
| 48 | frameWidth := runewidth.StringWidth(frame) | |
| 69 | frameWidth := runewidth.StringWidth(stripansi.Strip(frame)) | |
| 49 | 70 | |
| 50 | 71 | if width < frameWidth { |
| 51 | 72 | return |
| 52 | 73 | } |
| 53 | 74 | |
| 54 | switch rest := width - frameWidth; s.alignment { | |
| 55 | case SpinnerOnLeft: | |
| 56 | io.WriteString(w, frame+strings.Repeat(" ", rest)) | |
| 57 | case SpinnerOnMiddle: | |
| 75 | var err error | |
| 76 | rest := width - frameWidth | |
| 77 | switch s.position { | |
| 78 | case positionLeft: | |
| 79 | _, err = io.WriteString(w, frame+strings.Repeat(" ", rest)) | |
| 80 | case positionRight: | |
| 81 | _, err = io.WriteString(w, strings.Repeat(" ", rest)+frame) | |
| 82 | default: | |
| 58 | 83 | str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2) |
| 59 | io.WriteString(w, str) | |
| 60 | case SpinnerOnRight: | |
| 61 | io.WriteString(w, strings.Repeat(" ", rest)+frame) | |
| 84 | _, err = io.WriteString(w, str) | |
| 85 | } | |
| 86 | if err != nil { | |
| 87 | panic(err) | |
| 62 | 88 | } |
| 63 | 89 | s.count++ |
| 64 | 90 | } |
| 3 | 3 | "bytes" |
| 4 | 4 | "io" |
| 5 | 5 | |
| 6 | "github.com/vbauerster/mpb/v6/decor" | |
| 7 | "github.com/vbauerster/mpb/v6/internal" | |
| 6 | "github.com/vbauerster/mpb/v7/decor" | |
| 8 | 7 | ) |
| 9 | 8 | |
| 10 | 9 | // BarOption is a func option to alter default behavior of a bar. |
| 11 | 10 | type BarOption func(*bState) |
| 11 | ||
| 12 | func skipNil(decorators []decor.Decorator) (filtered []decor.Decorator) { | |
| 13 | for _, d := range decorators { | |
| 14 | if d != nil { | |
| 15 | filtered = append(filtered, d) | |
| 16 | } | |
| 17 | } | |
| 18 | return | |
| 19 | } | |
| 12 | 20 | |
| 13 | 21 | func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) { |
| 14 | 22 | type mergeWrapper interface { |
| 25 | 33 | // AppendDecorators let you inject decorators to the bar's right side. |
| 26 | 34 | func AppendDecorators(decorators ...decor.Decorator) BarOption { |
| 27 | 35 | return func(s *bState) { |
| 28 | s.addDecorators(&s.aDecorators, decorators...) | |
| 36 | s.addDecorators(&s.aDecorators, skipNil(decorators)...) | |
| 29 | 37 | } |
| 30 | 38 | } |
| 31 | 39 | |
| 32 | 40 | // PrependDecorators let you inject decorators to the bar's left side. |
| 33 | 41 | func PrependDecorators(decorators ...decor.Decorator) BarOption { |
| 34 | 42 | return func(s *bState) { |
| 35 | s.addDecorators(&s.pDecorators, decorators...) | |
| 43 | s.addDecorators(&s.pDecorators, skipNil(decorators)...) | |
| 36 | 44 | } |
| 37 | 45 | } |
| 38 | 46 | |
| 80 | 88 | return BarFillerMiddleware(func(base BarFiller) BarFiller { |
| 81 | 89 | return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) { |
| 82 | 90 | if st.Completed { |
| 83 | io.WriteString(w, message) | |
| 91 | _, err := io.WriteString(w, message) | |
| 92 | if err != nil { | |
| 93 | panic(err) | |
| 94 | } | |
| 84 | 95 | } else { |
| 85 | 96 | base.Fill(w, reqWidth, st) |
| 86 | 97 | } |
| 137 | 148 | } |
| 138 | 149 | } |
| 139 | 150 | |
| 140 | // BarOptional will invoke provided option only when pick is true. | |
| 141 | func BarOptional(option BarOption, pick bool) BarOption { | |
| 142 | return BarOptOn(option, internal.Predicate(pick)) | |
| 151 | // BarOptional will invoke provided option only when cond is true. | |
| 152 | func BarOptional(option BarOption, cond bool) BarOption { | |
| 153 | if cond { | |
| 154 | return option | |
| 155 | } | |
| 156 | return nil | |
| 143 | 157 | } |
| 144 | 158 | |
| 145 | 159 | // BarOptOn will invoke provided option only when higher order predicate |
| 9 | 9 | "time" |
| 10 | 10 | "unicode/utf8" |
| 11 | 11 | |
| 12 | "github.com/vbauerster/mpb/v6" | |
| 13 | "github.com/vbauerster/mpb/v6/decor" | |
| 12 | "github.com/vbauerster/mpb/v7" | |
| 13 | "github.com/vbauerster/mpb/v7/decor" | |
| 14 | 14 | ) |
| 15 | 15 | |
| 16 | 16 | func TestBarCompleted(t *testing.T) { |
| 60 | 60 | |
| 61 | 61 | total := 100 |
| 62 | 62 | till := 30 |
| 63 | refillRune, _ := utf8.DecodeLastRuneInString(mpb.BarDefaultStyle) | |
| 64 | ||
| 65 | bar := p.AddBar(int64(total), mpb.BarFillerTrim()) | |
| 63 | refiller := "+" | |
| 64 | ||
| 65 | bar := p.New(int64(total), mpb.BarStyle().Refiller(refiller), mpb.BarFillerTrim()) | |
| 66 | 66 | |
| 67 | 67 | bar.SetRefill(int64(till)) |
| 68 | 68 | bar.IncrBy(till) |
| 75 | 75 | p.Wait() |
| 76 | 76 | |
| 77 | 77 | wantBar := fmt.Sprintf("[%s%s]", |
| 78 | strings.Repeat(string(refillRune), till-1), | |
| 78 | strings.Repeat(refiller, till-1), | |
| 79 | 79 | strings.Repeat("=", total-till-1), |
| 80 | 80 | ) |
| 81 | 81 | |
| 142 | 142 | func TestBarStyle(t *testing.T) { |
| 143 | 143 | var buf bytes.Buffer |
| 144 | 144 | customFormat := "╢▌▌░╟" |
| 145 | runes := []rune(customFormat) | |
| 145 | 146 | total := 80 |
| 146 | 147 | p := mpb.New(mpb.WithWidth(total), mpb.WithOutput(&buf)) |
| 147 | bar := p.Add(int64(total), mpb.NewBarFiller(customFormat), mpb.BarFillerTrim()) | |
| 148 | ||
| 149 | for i := 0; i < total; i++ { | |
| 150 | bar.Increment() | |
| 151 | time.Sleep(10 * time.Millisecond) | |
| 152 | } | |
| 153 | ||
| 154 | p.Wait() | |
| 155 | ||
| 156 | runes := []rune(customFormat) | |
| 157 | wantBar := fmt.Sprintf("%s%s%s", | |
| 148 | bs := mpb.BarStyle() | |
| 149 | bs.Lbound(string(runes[0])) | |
| 150 | bs.Filler(string(runes[1])) | |
| 151 | bs.Tip(string(runes[2])) | |
| 152 | bs.Padding(string(runes[3])) | |
| 153 | bs.Rbound(string(runes[4])) | |
| 154 | bar := p.New(int64(total), bs, mpb.BarFillerTrim()) | |
| 155 | ||
| 156 | for i := 0; i < total; i++ { | |
| 157 | bar.Increment() | |
| 158 | time.Sleep(10 * time.Millisecond) | |
| 159 | } | |
| 160 | ||
| 161 | p.Wait() | |
| 162 | ||
| 163 | wantBar := fmt.Sprintf("%s%s%s%s", | |
| 158 | 164 | string(runes[0]), |
| 159 | strings.Repeat(string(runes[1]), total-2), | |
| 160 | string(runes[len(runes)-1]), | |
| 165 | strings.Repeat(string(runes[1]), total-3), | |
| 166 | string(runes[2]), | |
| 167 | string(runes[4]), | |
| 161 | 168 | ) |
| 162 | 169 | got := string(getLastLine(buf.Bytes())) |
| 163 | 170 | |
| 246 | 253 | } |
| 247 | 254 | } |
| 248 | 255 | |
| 256 | func TestDecorStatisticsAvailableWidth(t *testing.T) { | |
| 257 | total := 100 | |
| 258 | down := make(chan struct{}) | |
| 259 | checkDone := make(chan struct{}) | |
| 260 | td1 := func(s decor.Statistics) string { | |
| 261 | if s.AvailableWidth != 80 { | |
| 262 | t.Errorf("expected AvailableWidth %d got %d\n", 80, s.AvailableWidth) | |
| 263 | } | |
| 264 | return fmt.Sprintf("\x1b[31;1;4m%s\x1b[0m", strings.Repeat("0", 20)) | |
| 265 | } | |
| 266 | td2 := func(s decor.Statistics) string { | |
| 267 | defer func() { | |
| 268 | select { | |
| 269 | case checkDone <- struct{}{}: | |
| 270 | default: | |
| 271 | } | |
| 272 | }() | |
| 273 | if s.AvailableWidth != 40 { | |
| 274 | t.Errorf("expected AvailableWidth %d got %d\n", 40, s.AvailableWidth) | |
| 275 | } | |
| 276 | return "" | |
| 277 | } | |
| 278 | p := mpb.New( | |
| 279 | mpb.WithWidth(100), | |
| 280 | mpb.WithShutdownNotifier(down), | |
| 281 | mpb.WithOutput(ioutil.Discard), | |
| 282 | ) | |
| 283 | bar := p.AddBar(int64(total), | |
| 284 | mpb.BarFillerTrim(), | |
| 285 | mpb.PrependDecorators( | |
| 286 | decor.Name(strings.Repeat("0", 20)), | |
| 287 | decor.Any(td1), | |
| 288 | ), | |
| 289 | mpb.AppendDecorators( | |
| 290 | decor.Name(strings.Repeat("0", 20)), | |
| 291 | decor.Any(td2), | |
| 292 | ), | |
| 293 | ) | |
| 294 | go func() { | |
| 295 | for { | |
| 296 | select { | |
| 297 | case <-checkDone: | |
| 298 | bar.Abort(true) | |
| 299 | case <-down: | |
| 300 | return | |
| 301 | } | |
| 302 | } | |
| 303 | }() | |
| 304 | for !bar.Completed() { | |
| 305 | bar.Increment() | |
| 306 | } | |
| 307 | p.Wait() | |
| 308 | } | |
| 309 | ||
| 249 | 310 | func panicDecorator(panicMsg string, cond func(decor.Statistics) bool) decor.Decorator { |
| 250 | 311 | return decor.Any(func(st decor.Statistics) string { |
| 251 | 312 | if cond(st) { |
| 0 | 0 | package mpb |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | "io/ioutil" | |
| 3 | "sync" | |
| 4 | 4 | "testing" |
| 5 | ||
| 6 | "github.com/vbauerster/mpb/v6/decor" | |
| 7 | 5 | ) |
| 8 | 6 | |
| 9 | func BenchmarkIncrSingleBar(b *testing.B) { | |
| 10 | p := New(WithOutput(ioutil.Discard), WithWidth(80)) | |
| 11 | bar := p.AddBar(int64(b.N)) | |
| 12 | for i := 0; i < b.N; i++ { | |
| 13 | bar.Increment() | |
| 14 | } | |
| 7 | const total = 1000 | |
| 8 | ||
| 9 | func BenchmarkIncrementOneBar(b *testing.B) { | |
| 10 | benchBody(1, b) | |
| 15 | 11 | } |
| 16 | 12 | |
| 17 | func BenchmarkIncrSingleBarWhileIsNotCompleted(b *testing.B) { | |
| 18 | p := New(WithOutput(ioutil.Discard), WithWidth(80)) | |
| 19 | bar := p.AddBar(int64(b.N)) | |
| 20 | for !bar.Completed() { | |
| 21 | bar.Increment() | |
| 22 | } | |
| 13 | func BenchmarkIncrementTwoBars(b *testing.B) { | |
| 14 | benchBody(2, b) | |
| 23 | 15 | } |
| 24 | 16 | |
| 25 | func BenchmarkIncrSingleBarWithNameDecorator(b *testing.B) { | |
| 26 | p := New(WithOutput(ioutil.Discard), WithWidth(80)) | |
| 27 | bar := p.AddBar(int64(b.N), PrependDecorators(decor.Name("test"))) | |
| 28 | for i := 0; i < b.N; i++ { | |
| 29 | bar.Increment() | |
| 30 | } | |
| 17 | func BenchmarkIncrementThreeBars(b *testing.B) { | |
| 18 | benchBody(3, b) | |
| 31 | 19 | } |
| 32 | 20 | |
| 33 | func BenchmarkIncrSingleBarWithNameAndEwmaETADecorator(b *testing.B) { | |
| 34 | p := New(WithOutput(ioutil.Discard), WithWidth(80)) | |
| 35 | bar := p.AddBar(int64(b.N), | |
| 36 | PrependDecorators(decor.Name("test")), | |
| 37 | AppendDecorators(decor.EwmaETA(decor.ET_STYLE_GO, 60)), | |
| 38 | ) | |
| 21 | func BenchmarkIncrementFourBars(b *testing.B) { | |
| 22 | benchBody(4, b) | |
| 23 | } | |
| 24 | ||
| 25 | func benchBody(n int, b *testing.B) { | |
| 26 | p := New(WithOutput(nil), WithWidth(80)) | |
| 27 | wg := new(sync.WaitGroup) | |
| 28 | b.ResetTimer() | |
| 39 | 29 | for i := 0; i < b.N; i++ { |
| 40 | bar.Increment() | |
| 30 | for j := 0; j < n; j++ { | |
| 31 | switch j { | |
| 32 | case n - 1: | |
| 33 | bar := p.AddBar(total) | |
| 34 | for c := 0; c < total; c++ { | |
| 35 | bar.Increment() | |
| 36 | } | |
| 37 | if !bar.Completed() { | |
| 38 | b.Fail() | |
| 39 | } | |
| 40 | default: | |
| 41 | wg.Add(1) | |
| 42 | go func() { | |
| 43 | bar := p.AddBar(total) | |
| 44 | for c := 0; c < total; c++ { | |
| 45 | bar.Increment() | |
| 46 | } | |
| 47 | if !bar.Completed() { | |
| 48 | b.Fail() | |
| 49 | } | |
| 50 | wg.Done() | |
| 51 | }() | |
| 52 | } | |
| 53 | } | |
| 54 | wg.Wait() | |
| 41 | 55 | } |
| 56 | p.Wait() | |
| 42 | 57 | } |
| 4 | 4 | "io/ioutil" |
| 5 | 5 | "sync" |
| 6 | 6 | "time" |
| 7 | ||
| 8 | "github.com/vbauerster/mpb/v6/internal" | |
| 9 | 7 | ) |
| 10 | 8 | |
| 11 | 9 | // ContainerOption is a func option to alter default behavior of a bar |
| 61 | 59 | // have been rendered. |
| 62 | 60 | func WithShutdownNotifier(ch chan struct{}) ContainerOption { |
| 63 | 61 | return func(s *pState) { |
| 64 | s.shutdownNotifier = ch | |
| 62 | select { | |
| 63 | case <-ch: | |
| 64 | default: | |
| 65 | s.shutdownNotifier = ch | |
| 66 | } | |
| 65 | 67 | } |
| 66 | 68 | } |
| 67 | 69 | |
| 96 | 98 | } |
| 97 | 99 | } |
| 98 | 100 | |
| 99 | // ContainerOptional will invoke provided option only when pick is true. | |
| 100 | func ContainerOptional(option ContainerOption, pick bool) ContainerOption { | |
| 101 | return ContainerOptOn(option, internal.Predicate(pick)) | |
| 101 | // ContainerOptional will invoke provided option only when cond is true. | |
| 102 | func ContainerOptional(option ContainerOption, cond bool) ContainerOption { | |
| 103 | if cond { | |
| 104 | return option | |
| 105 | } | |
| 106 | return nil | |
| 102 | 107 | } |
| 103 | 108 | |
| 104 | 109 | // ContainerOptOn will invoke provided option only when higher order |
| 17 | 17 | func BenchmarkWithJoin(b *testing.B) { |
| 18 | 18 | bCuuAndEd := [][]byte{[]byte("\x1b["), []byte("A\x1b[J")} |
| 19 | 19 | for i := 0; i < b.N; i++ { |
| 20 | ioutil.Discard.Write(bytes.Join(bCuuAndEd, []byte(strconv.Itoa(4)))) | |
| 20 | _, _ = ioutil.Discard.Write(bytes.Join(bCuuAndEd, []byte(strconv.Itoa(4)))) | |
| 21 | 21 | } |
| 22 | 22 | } |
| 23 | 23 | |
| 25 | 25 | escOpen := []byte("\x1b[") |
| 26 | 26 | cuuAndEd := []byte("A\x1b[J") |
| 27 | 27 | for i := 0; i < b.N; i++ { |
| 28 | ioutil.Discard.Write(append(strconv.AppendInt(escOpen, 4, 10), cuuAndEd...)) | |
| 28 | _, _ = ioutil.Discard.Write(append(strconv.AppendInt(escOpen, 4, 10), cuuAndEd...)) | |
| 29 | 29 | } |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | 32 | func BenchmarkWithCopy(b *testing.B) { |
| 33 | 33 | w := New(ioutil.Discard) |
| 34 | w.lineCount = 4 | |
| 34 | w.lines = 4 | |
| 35 | 35 | for i := 0; i < b.N; i++ { |
| 36 | w.ansiCuuAndEd() | |
| 36 | _ = w.ansiCuuAndEd() | |
| 37 | 37 | } |
| 38 | 38 | } |
| 0 | // +build zos | |
| 1 | ||
| 2 | package cwriter | |
| 3 | ||
| 4 | import "golang.org/x/sys/unix" | |
| 5 | ||
| 6 | const ioctlReadTermios = unix.TCGETS |
| 21 | 21 | type Writer struct { |
| 22 | 22 | out io.Writer |
| 23 | 23 | buf bytes.Buffer |
| 24 | lineCount int | |
| 24 | lines int | |
| 25 | 25 | fd int |
| 26 | 26 | isTerminal bool |
| 27 | 27 | } |
| 37 | 37 | } |
| 38 | 38 | |
| 39 | 39 | // Flush flushes the underlying buffer. |
| 40 | func (w *Writer) Flush(lineCount int) (err error) { | |
| 40 | func (w *Writer) Flush(lines int) (err error) { | |
| 41 | 41 | // some terminals interpret 'cursor up 0' as 'cursor up 1' |
| 42 | if w.lineCount > 0 { | |
| 42 | if w.lines > 0 { | |
| 43 | 43 | err = w.clearLines() |
| 44 | 44 | if err != nil { |
| 45 | 45 | return |
| 46 | 46 | } |
| 47 | 47 | } |
| 48 | w.lineCount = lineCount | |
| 48 | w.lines = lines | |
| 49 | 49 | _, err = w.buf.WriteTo(w.out) |
| 50 | 50 | return |
| 51 | 51 | } |
| 75 | 75 | return tw, err |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | func (w *Writer) ansiCuuAndEd() (err error) { | |
| 78 | func (w *Writer) ansiCuuAndEd() error { | |
| 79 | 79 | buf := make([]byte, 8) |
| 80 | buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lineCount), 10) | |
| 81 | _, err = w.out.Write(append(buf, cuuAndEd...)) | |
| 82 | return | |
| 80 | buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lines), 10) | |
| 81 | _, err := w.out.Write(append(buf, cuuAndEd...)) | |
| 82 | return err | |
| 83 | 83 | } |
| 25 | 25 | return err |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | info.CursorPosition.Y -= int16(w.lineCount) | |
| 28 | info.CursorPosition.Y -= int16(w.lines) | |
| 29 | 29 | if info.CursorPosition.Y < 0 { |
| 30 | 30 | info.CursorPosition.Y = 0 |
| 31 | 31 | } |
| 39 | 39 | X: info.Window.Left, |
| 40 | 40 | Y: info.CursorPosition.Y, |
| 41 | 41 | } |
| 42 | count := uint32(info.Size.X) * uint32(w.lineCount) | |
| 42 | count := uint32(info.Size.X) * uint32(w.lines) | |
| 43 | 43 | _, _, _ = procFillConsoleOutputCharacter.Call( |
| 44 | 44 | uintptr(w.fd), |
| 45 | 45 | uintptr(' '), |
| 52 | 52 | Current int64 |
| 53 | 53 | Refill int64 |
| 54 | 54 | Completed bool |
| 55 | Aborted bool | |
| 55 | 56 | } |
| 56 | 57 | |
| 57 | 58 | // Decorator interface. |
| 0 | // Package decor provides common decorators for "github.com/vbauerster/mpb/v6" module. | |
| 1 | /* | |
| 2 | Some decorators returned by this package might have a closure state. It is ok to use | |
| 3 | decorators concurrently, unless you share the same decorator among multiple | |
| 4 | *mpb.Bar instances. To avoid data races, create new decorator per *mpb.Bar instance. | |
| 5 | ||
| 6 | Don't: | |
| 7 | ||
| 8 | p := mpb.New() | |
| 9 | name := decor.Name("bar") | |
| 10 | p.AddBar(100, mpb.AppendDecorators(name)) | |
| 11 | p.AddBar(100, mpb.AppendDecorators(name)) | |
| 12 | ||
| 13 | Do: | |
| 14 | ||
| 15 | p := mpb.New() | |
| 16 | p.AddBar(100, mpb.AppendDecorators(decor.Name("bar1"))) | |
| 17 | p.AddBar(100, mpb.AppendDecorators(decor.Name("bar2"))) | |
| 18 | */ | |
| 0 | // Package decor provides common decorators for "github.com/vbauerster/mpb/v7" module. | |
| 1 | // | |
| 2 | // Some decorators returned by this package might have a closure state. It is ok to use | |
| 3 | // decorators concurrently, unless you share the same decorator among multiple | |
| 4 | // *mpb.Bar instances. To avoid data races, create new decorator per *mpb.Bar instance. | |
| 5 | // | |
| 6 | // Don't: | |
| 7 | // | |
| 8 | // p := mpb.New() | |
| 9 | // name := decor.Name("bar") | |
| 10 | // p.AddBar(100, mpb.AppendDecorators(name)) | |
| 11 | // p.AddBar(100, mpb.AppendDecorators(name)) | |
| 12 | // | |
| 13 | // Do: | |
| 14 | // | |
| 15 | // p := mpb.New() | |
| 16 | // p.AddBar(100, mpb.AppendDecorators(decor.Name("bar1"))) | |
| 17 | // p.AddBar(100, mpb.AppendDecorators(decor.Name("bar2"))) | |
| 19 | 18 | package decor |
| 16 | 16 | // +----+--------+---------+--------+ |
| 17 | 17 | // |
| 18 | 18 | func Merge(decorator Decorator, placeholders ...WC) Decorator { |
| 19 | if decorator == nil { | |
| 20 | return nil | |
| 21 | } | |
| 19 | 22 | if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 { |
| 20 | 23 | return decorator |
| 21 | 24 | } |
| 0 | package decor | |
| 1 | ||
| 2 | // OnAbort returns decorator, which wraps provided decorator with sole | |
| 3 | // purpose to display provided message on abort event. It has no effect | |
| 4 | // if bar.Abort(drop bool) is called with true argument. | |
| 5 | // | |
| 6 | // `decorator` Decorator to wrap | |
| 7 | // | |
| 8 | // `message` message to display on abort event | |
| 9 | // | |
| 10 | func OnAbort(decorator Decorator, message string) Decorator { | |
| 11 | if decorator == nil { | |
| 12 | return nil | |
| 13 | } | |
| 14 | d := &onAbortWrapper{ | |
| 15 | Decorator: decorator, | |
| 16 | msg: message, | |
| 17 | } | |
| 18 | if md, ok := decorator.(*mergeDecorator); ok { | |
| 19 | d.Decorator, md.Decorator = md.Decorator, d | |
| 20 | return md | |
| 21 | } | |
| 22 | return d | |
| 23 | } | |
| 24 | ||
| 25 | type onAbortWrapper struct { | |
| 26 | Decorator | |
| 27 | msg string | |
| 28 | } | |
| 29 | ||
| 30 | func (d *onAbortWrapper) Decor(s Statistics) string { | |
| 31 | if s.Aborted { | |
| 32 | wc := d.GetConf() | |
| 33 | return wc.FormatMsg(d.msg) | |
| 34 | } | |
| 35 | return d.Decorator.Decor(s) | |
| 36 | } | |
| 37 | ||
| 38 | func (d *onAbortWrapper) Base() Decorator { | |
| 39 | return d.Decorator | |
| 40 | } |
| 0 | 0 | package decor |
| 1 | 1 | |
| 2 | // OnComplete returns decorator, which wraps provided decorator, with | |
| 2 | // OnComplete returns decorator, which wraps provided decorator with | |
| 3 | 3 | // sole purpose to display provided message on complete event. |
| 4 | 4 | // |
| 5 | 5 | // `decorator` Decorator to wrap |
| 7 | 7 | // `message` message to display on complete event |
| 8 | 8 | // |
| 9 | 9 | func OnComplete(decorator Decorator, message string) Decorator { |
| 10 | if decorator == nil { | |
| 11 | return nil | |
| 12 | } | |
| 10 | 13 | d := &onCompleteWrapper{ |
| 11 | 14 | Decorator: decorator, |
| 12 | 15 | msg: message, |
| 0 | package decor | |
| 1 | ||
| 2 | // OnPredicate returns decorator if predicate evaluates to true. | |
| 3 | // | |
| 4 | // `decorator` Decorator | |
| 5 | // | |
| 6 | // `predicate` func() bool | |
| 7 | // | |
| 8 | func OnPredicate(decorator Decorator, predicate func() bool) Decorator { | |
| 9 | if predicate() { | |
| 10 | return decorator | |
| 11 | } | |
| 12 | return nil | |
| 13 | } | |
| 14 | ||
| 15 | // OnCondition returns decorator if condition is true. | |
| 16 | // | |
| 17 | // `decorator` Decorator | |
| 18 | // | |
| 19 | // `cond` bool | |
| 20 | // | |
| 21 | func OnCondition(decorator Decorator, cond bool) Decorator { | |
| 22 | if cond { | |
| 23 | return decorator | |
| 24 | } | |
| 25 | return nil | |
| 26 | } |
| 0 | package decor | |
| 1 | ||
| 2 | import "io" | |
| 3 | ||
| 4 | func optimisticStringWriter(w io.Writer) func(string) { | |
| 5 | return func(s string) { | |
| 6 | _, err := io.WriteString(w, s) | |
| 7 | if err != nil { | |
| 8 | panic(err) | |
| 9 | } | |
| 10 | } | |
| 11 | } |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | 3 | "fmt" |
| 4 | "io" | |
| 5 | 4 | "strconv" |
| 6 | 5 | |
| 7 | "github.com/vbauerster/mpb/v6/internal" | |
| 6 | "github.com/vbauerster/mpb/v7/internal" | |
| 8 | 7 | ) |
| 9 | 8 | |
| 10 | 9 | type percentageType float64 |
| 23 | 22 | } |
| 24 | 23 | } |
| 25 | 24 | |
| 26 | io.WriteString(st, strconv.FormatFloat(float64(s), 'f', prec, 64)) | |
| 27 | ||
| 25 | osw := optimisticStringWriter(st) | |
| 26 | osw(strconv.FormatFloat(float64(s), 'f', prec, 64)) | |
| 28 | 27 | if st.Flag(' ') { |
| 29 | io.WriteString(st, " ") | |
| 28 | osw(" ") | |
| 30 | 29 | } |
| 31 | io.WriteString(st, "%") | |
| 30 | osw("%") | |
| 32 | 31 | } |
| 33 | 32 | |
| 34 | 33 | // Percentage returns percentage decorator. It's a wrapper of NewPercentage. |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | 3 | "fmt" |
| 4 | "io" | |
| 5 | "math" | |
| 6 | 4 | "strconv" |
| 7 | 5 | ) |
| 8 | 6 | |
| 46 | 44 | unit = _iMiB |
| 47 | 45 | case self < _iTiB: |
| 48 | 46 | unit = _iGiB |
| 49 | case self <= math.MaxInt64: | |
| 47 | default: | |
| 50 | 48 | unit = _iTiB |
| 51 | 49 | } |
| 52 | 50 | |
| 53 | io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64)) | |
| 54 | ||
| 51 | osw := optimisticStringWriter(st) | |
| 52 | osw(strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64)) | |
| 55 | 53 | if st.Flag(' ') { |
| 56 | io.WriteString(st, " ") | |
| 54 | osw(" ") | |
| 57 | 55 | } |
| 58 | io.WriteString(st, unit.String()) | |
| 56 | osw(unit.String()) | |
| 59 | 57 | } |
| 60 | 58 | |
| 61 | 59 | const ( |
| 95 | 93 | unit = _MB |
| 96 | 94 | case self < _TB: |
| 97 | 95 | unit = _GB |
| 98 | case self <= math.MaxInt64: | |
| 96 | default: | |
| 99 | 97 | unit = _TB |
| 100 | 98 | } |
| 101 | 99 | |
| 102 | io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64)) | |
| 103 | ||
| 100 | osw := optimisticStringWriter(st) | |
| 101 | osw(strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64)) | |
| 104 | 102 | if st.Flag(' ') { |
| 105 | io.WriteString(st, " ") | |
| 103 | osw(" ") | |
| 106 | 104 | } |
| 107 | io.WriteString(st, unit.String()) | |
| 105 | osw(unit.String()) | |
| 108 | 106 | } |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | 3 | "fmt" |
| 4 | "io" | |
| 5 | 4 | "math" |
| 6 | 5 | "time" |
| 7 | 6 | |
| 23 | 22 | |
| 24 | 23 | func (self *speedFormatter) Format(st fmt.State, verb rune) { |
| 25 | 24 | self.Formatter.Format(st, verb) |
| 26 | io.WriteString(st, "/s") | |
| 25 | optimisticStringWriter(st)("/s") | |
| 27 | 26 | } |
| 28 | 27 | |
| 29 | 28 | // EwmaSpeed exponential-weighted-moving-average based speed decorator. |
| 3 | 3 | "sync" |
| 4 | 4 | "testing" |
| 5 | 5 | |
| 6 | "github.com/vbauerster/mpb/v6" | |
| 7 | "github.com/vbauerster/mpb/v6/decor" | |
| 6 | "github.com/vbauerster/mpb/v7" | |
| 7 | "github.com/vbauerster/mpb/v7/decor" | |
| 8 | 8 | ) |
| 9 | 9 | |
| 10 | 10 | func TestNameDecorator(t *testing.T) { |
| 5 | 5 | "unicode/utf8" |
| 6 | 6 | ) |
| 7 | 7 | |
| 8 | func TestDraw(t *testing.T) { | |
| 8 | func TestDrawDefault(t *testing.T) { | |
| 9 | 9 | // key is termWidth |
| 10 | 10 | testSuite := map[int][]struct { |
| 11 | style BarStyleComposer | |
| 11 | 12 | name string |
| 12 | style string | |
| 13 | 13 | total int64 |
| 14 | 14 | current int64 |
| 15 | 15 | refill int64 |
| 16 | 16 | barWidth int |
| 17 | 17 | trim bool |
| 18 | reverse bool | |
| 19 | 18 | want string |
| 20 | 19 | }{ |
| 21 | 20 | 0: { |
| 22 | 21 | { |
| 22 | style: BarStyle(), | |
| 23 | 23 | name: "t,c{60,20}", |
| 24 | 24 | total: 60, |
| 25 | 25 | current: 20, |
| 26 | want: "… ", | |
| 27 | }, | |
| 28 | { | |
| 26 | want: "", | |
| 27 | }, | |
| 28 | { | |
| 29 | style: BarStyle(), | |
| 29 | 30 | name: "t,c{60,20}trim", |
| 30 | 31 | total: 60, |
| 31 | 32 | current: 20, |
| 35 | 36 | }, |
| 36 | 37 | 1: { |
| 37 | 38 | { |
| 39 | style: BarStyle(), | |
| 38 | 40 | name: "t,c{60,20}", |
| 39 | 41 | total: 60, |
| 40 | 42 | current: 20, |
| 41 | want: "… ", | |
| 42 | }, | |
| 43 | { | |
| 43 | want: "", | |
| 44 | }, | |
| 45 | { | |
| 46 | style: BarStyle(), | |
| 44 | 47 | name: "t,c{60,20}trim", |
| 45 | 48 | total: 60, |
| 46 | 49 | current: 20, |
| 50 | 53 | }, |
| 51 | 54 | 2: { |
| 52 | 55 | { |
| 56 | style: BarStyle(), | |
| 53 | 57 | name: "t,c{60,20}", |
| 54 | 58 | total: 60, |
| 55 | 59 | current: 20, |
| 56 | 60 | want: " ", |
| 57 | 61 | }, |
| 58 | 62 | { |
| 63 | style: BarStyle(), | |
| 59 | 64 | name: "t,c{60,20}trim", |
| 60 | 65 | total: 60, |
| 61 | 66 | current: 20, |
| 65 | 70 | }, |
| 66 | 71 | 3: { |
| 67 | 72 | { |
| 73 | style: BarStyle(), | |
| 68 | 74 | name: "t,c{60,20}", |
| 69 | 75 | total: 60, |
| 70 | 76 | current: 20, |
| 71 | 77 | want: " ", |
| 72 | 78 | }, |
| 73 | 79 | { |
| 80 | style: BarStyle(), | |
| 74 | 81 | name: "t,c{60,20}trim", |
| 75 | 82 | total: 60, |
| 76 | 83 | current: 20, |
| 77 | 84 | trim: true, |
| 78 | 85 | want: "[-]", |
| 79 | 86 | }, |
| 87 | { | |
| 88 | style: BarStyle(), | |
| 89 | name: "t,c{60,59}", | |
| 90 | total: 60, | |
| 91 | current: 59, | |
| 92 | want: " ", | |
| 93 | }, | |
| 94 | { | |
| 95 | style: BarStyle(), | |
| 96 | name: "t,c{60,59}trim", | |
| 97 | total: 60, | |
| 98 | current: 59, | |
| 99 | trim: true, | |
| 100 | want: "[>]", | |
| 101 | }, | |
| 102 | { | |
| 103 | style: BarStyle(), | |
| 104 | name: "t,c{60,60}", | |
| 105 | total: 60, | |
| 106 | current: 60, | |
| 107 | want: " ", | |
| 108 | }, | |
| 109 | { | |
| 110 | style: BarStyle(), | |
| 111 | name: "t,c{60,60}trim", | |
| 112 | total: 60, | |
| 113 | current: 60, | |
| 114 | trim: true, | |
| 115 | want: "[=]", | |
| 116 | }, | |
| 80 | 117 | }, |
| 81 | 118 | 4: { |
| 82 | 119 | { |
| 120 | style: BarStyle(), | |
| 83 | 121 | name: "t,c{60,20}", |
| 84 | 122 | total: 60, |
| 85 | 123 | current: 20, |
| 86 | 124 | want: " [] ", |
| 87 | 125 | }, |
| 88 | 126 | { |
| 127 | style: BarStyle(), | |
| 89 | 128 | name: "t,c{60,20}trim", |
| 90 | 129 | total: 60, |
| 91 | 130 | current: 20, |
| 92 | 131 | trim: true, |
| 93 | 132 | want: "[>-]", |
| 94 | 133 | }, |
| 134 | { | |
| 135 | style: BarStyle(), | |
| 136 | name: "t,c{60,59}", | |
| 137 | total: 60, | |
| 138 | current: 59, | |
| 139 | want: " [] ", | |
| 140 | }, | |
| 141 | { | |
| 142 | style: BarStyle(), | |
| 143 | name: "t,c{60,59}trim", | |
| 144 | total: 60, | |
| 145 | current: 59, | |
| 146 | trim: true, | |
| 147 | want: "[=>]", | |
| 148 | }, | |
| 149 | { | |
| 150 | style: BarStyle(), | |
| 151 | name: "t,c{60,60}", | |
| 152 | total: 60, | |
| 153 | current: 60, | |
| 154 | want: " [] ", | |
| 155 | }, | |
| 156 | { | |
| 157 | style: BarStyle(), | |
| 158 | name: "t,c{60,60}trim", | |
| 159 | total: 60, | |
| 160 | current: 60, | |
| 161 | trim: true, | |
| 162 | want: "[==]", | |
| 163 | }, | |
| 95 | 164 | }, |
| 96 | 165 | 5: { |
| 97 | 166 | { |
| 167 | style: BarStyle(), | |
| 98 | 168 | name: "t,c{60,20}", |
| 99 | 169 | total: 60, |
| 100 | 170 | current: 20, |
| 101 | 171 | want: " [-] ", |
| 102 | 172 | }, |
| 103 | 173 | { |
| 174 | style: BarStyle(), | |
| 104 | 175 | name: "t,c{60,20}trim", |
| 105 | 176 | total: 60, |
| 106 | 177 | current: 20, |
| 107 | 178 | trim: true, |
| 108 | 179 | want: "[>--]", |
| 109 | 180 | }, |
| 181 | { | |
| 182 | style: BarStyle(), | |
| 183 | name: "t,c{60,59}", | |
| 184 | total: 60, | |
| 185 | current: 59, | |
| 186 | want: " [>] ", | |
| 187 | }, | |
| 188 | { | |
| 189 | style: BarStyle(), | |
| 190 | name: "t,c{60,59}trim", | |
| 191 | total: 60, | |
| 192 | current: 59, | |
| 193 | trim: true, | |
| 194 | want: "[==>]", | |
| 195 | }, | |
| 196 | { | |
| 197 | style: BarStyle(), | |
| 198 | name: "t,c{60,60}", | |
| 199 | total: 60, | |
| 200 | current: 60, | |
| 201 | want: " [=] ", | |
| 202 | }, | |
| 203 | { | |
| 204 | style: BarStyle(), | |
| 205 | name: "t,c{60,60}trim", | |
| 206 | total: 60, | |
| 207 | current: 60, | |
| 208 | trim: true, | |
| 209 | want: "[===]", | |
| 210 | }, | |
| 110 | 211 | }, |
| 111 | 212 | 6: { |
| 112 | 213 | { |
| 214 | style: BarStyle(), | |
| 113 | 215 | name: "t,c{60,20}", |
| 114 | 216 | total: 60, |
| 115 | 217 | current: 20, |
| 116 | 218 | want: " [>-] ", |
| 117 | 219 | }, |
| 118 | 220 | { |
| 221 | style: BarStyle(), | |
| 119 | 222 | name: "t,c{60,20}trim", |
| 120 | 223 | total: 60, |
| 121 | 224 | current: 20, |
| 122 | 225 | trim: true, |
| 123 | 226 | want: "[>---]", |
| 124 | 227 | }, |
| 228 | { | |
| 229 | style: BarStyle(), | |
| 230 | name: "t,c{60,59}", | |
| 231 | total: 60, | |
| 232 | current: 59, | |
| 233 | want: " [=>] ", | |
| 234 | }, | |
| 235 | { | |
| 236 | style: BarStyle(), | |
| 237 | name: "t,c{60,59}trim", | |
| 238 | total: 60, | |
| 239 | current: 59, | |
| 240 | trim: true, | |
| 241 | want: "[===>]", | |
| 242 | }, | |
| 243 | { | |
| 244 | style: BarStyle(), | |
| 245 | name: "t,c{60,60}", | |
| 246 | total: 60, | |
| 247 | current: 60, | |
| 248 | want: " [==] ", | |
| 249 | }, | |
| 250 | { | |
| 251 | style: BarStyle(), | |
| 252 | name: "t,c{60,60}trim", | |
| 253 | total: 60, | |
| 254 | current: 60, | |
| 255 | trim: true, | |
| 256 | want: "[====]", | |
| 257 | }, | |
| 125 | 258 | }, |
| 126 | 259 | 7: { |
| 127 | 260 | { |
| 261 | style: BarStyle(), | |
| 128 | 262 | name: "t,c{60,20}", |
| 129 | 263 | total: 60, |
| 130 | 264 | current: 20, |
| 131 | 265 | want: " [>--] ", |
| 132 | 266 | }, |
| 133 | 267 | { |
| 268 | style: BarStyle(), | |
| 134 | 269 | name: "t,c{60,20}trim", |
| 135 | 270 | total: 60, |
| 136 | 271 | current: 20, |
| 137 | 272 | trim: true, |
| 138 | 273 | want: "[=>---]", |
| 139 | 274 | }, |
| 275 | { | |
| 276 | style: BarStyle(), | |
| 277 | name: "t,c{60,59}", | |
| 278 | total: 60, | |
| 279 | current: 59, | |
| 280 | want: " [==>] ", | |
| 281 | }, | |
| 282 | { | |
| 283 | style: BarStyle(), | |
| 284 | name: "t,c{60,59}trim", | |
| 285 | total: 60, | |
| 286 | current: 59, | |
| 287 | trim: true, | |
| 288 | want: "[====>]", | |
| 289 | }, | |
| 290 | { | |
| 291 | style: BarStyle(), | |
| 292 | name: "t,c{60,60}", | |
| 293 | total: 60, | |
| 294 | current: 60, | |
| 295 | want: " [===] ", | |
| 296 | }, | |
| 297 | { | |
| 298 | style: BarStyle(), | |
| 299 | name: "t,c{60,60}trim", | |
| 300 | total: 60, | |
| 301 | current: 60, | |
| 302 | trim: true, | |
| 303 | want: "[=====]", | |
| 304 | }, | |
| 140 | 305 | }, |
| 141 | 306 | 8: { |
| 142 | 307 | { |
| 308 | style: BarStyle(), | |
| 143 | 309 | name: "t,c{60,20}", |
| 144 | 310 | total: 60, |
| 145 | 311 | current: 20, |
| 146 | 312 | want: " [>---] ", |
| 147 | 313 | }, |
| 148 | 314 | { |
| 315 | style: BarStyle(), | |
| 149 | 316 | name: "t,c{60,20}trim", |
| 150 | 317 | total: 60, |
| 151 | 318 | current: 20, |
| 152 | 319 | trim: true, |
| 153 | 320 | want: "[=>----]", |
| 154 | 321 | }, |
| 322 | { | |
| 323 | style: BarStyle(), | |
| 324 | name: "t,c{60,59}", | |
| 325 | total: 60, | |
| 326 | current: 59, | |
| 327 | want: " [===>] ", | |
| 328 | }, | |
| 329 | { | |
| 330 | style: BarStyle(), | |
| 331 | name: "t,c{60,59}trim", | |
| 332 | total: 60, | |
| 333 | current: 59, | |
| 334 | trim: true, | |
| 335 | want: "[=====>]", | |
| 336 | }, | |
| 337 | { | |
| 338 | style: BarStyle(), | |
| 339 | name: "t,c{60,60}", | |
| 340 | total: 60, | |
| 341 | current: 60, | |
| 342 | want: " [====] ", | |
| 343 | }, | |
| 344 | { | |
| 345 | style: BarStyle(), | |
| 346 | name: "t,c{60,60}trim", | |
| 347 | total: 60, | |
| 348 | current: 60, | |
| 349 | trim: true, | |
| 350 | want: "[======]", | |
| 351 | }, | |
| 155 | 352 | }, |
| 156 | 353 | 80: { |
| 157 | 354 | { |
| 355 | style: BarStyle(), | |
| 158 | 356 | name: "t,c{60,20}", |
| 159 | 357 | total: 60, |
| 160 | 358 | current: 20, |
| 161 | 359 | want: " [========================>---------------------------------------------------] ", |
| 162 | 360 | }, |
| 163 | 361 | { |
| 362 | style: BarStyle(), | |
| 164 | 363 | name: "t,c{60,20}trim", |
| 165 | 364 | total: 60, |
| 166 | 365 | current: 20, |
| 168 | 367 | want: "[=========================>----------------------------------------------------]", |
| 169 | 368 | }, |
| 170 | 369 | { |
| 370 | style: BarStyle(), | |
| 171 | 371 | name: "t,c,bw{60,20,60}", |
| 172 | 372 | total: 60, |
| 173 | 373 | current: 20, |
| 175 | 375 | want: " [==================>---------------------------------------] ", |
| 176 | 376 | }, |
| 177 | 377 | { |
| 378 | style: BarStyle(), | |
| 178 | 379 | name: "t,c,bw{60,20,60}trim", |
| 179 | 380 | total: 60, |
| 180 | 381 | current: 20, |
| 182 | 383 | trim: true, |
| 183 | 384 | want: "[==================>---------------------------------------]", |
| 184 | 385 | }, |
| 386 | { | |
| 387 | style: BarStyle(), | |
| 388 | name: "t,c{60,59}", | |
| 389 | total: 60, | |
| 390 | current: 59, | |
| 391 | want: " [==========================================================================>-] ", | |
| 392 | }, | |
| 393 | { | |
| 394 | style: BarStyle(), | |
| 395 | name: "t,c{60,59}trim", | |
| 396 | total: 60, | |
| 397 | current: 59, | |
| 398 | trim: true, | |
| 399 | want: "[============================================================================>-]", | |
| 400 | }, | |
| 401 | { | |
| 402 | style: BarStyle(), | |
| 403 | name: "t,c,bw{60,59,60}", | |
| 404 | total: 60, | |
| 405 | current: 59, | |
| 406 | barWidth: 60, | |
| 407 | want: " [========================================================>-] ", | |
| 408 | }, | |
| 409 | { | |
| 410 | style: BarStyle(), | |
| 411 | name: "t,c,bw{60,59,60}trim", | |
| 412 | total: 60, | |
| 413 | current: 59, | |
| 414 | barWidth: 60, | |
| 415 | trim: true, | |
| 416 | want: "[========================================================>-]", | |
| 417 | }, | |
| 418 | { | |
| 419 | style: BarStyle(), | |
| 420 | name: "t,c{60,60}", | |
| 421 | total: 60, | |
| 422 | current: 60, | |
| 423 | want: " [============================================================================] ", | |
| 424 | }, | |
| 425 | { | |
| 426 | style: BarStyle(), | |
| 427 | name: "t,c{60,60}trim", | |
| 428 | total: 60, | |
| 429 | current: 60, | |
| 430 | trim: true, | |
| 431 | want: "[==============================================================================]", | |
| 432 | }, | |
| 433 | { | |
| 434 | style: BarStyle(), | |
| 435 | name: "t,c,bw{60,60,60}", | |
| 436 | total: 60, | |
| 437 | current: 60, | |
| 438 | barWidth: 60, | |
| 439 | want: " [==========================================================] ", | |
| 440 | }, | |
| 441 | { | |
| 442 | style: BarStyle(), | |
| 443 | name: "t,c,bw{60,60,60}trim", | |
| 444 | total: 60, | |
| 445 | current: 60, | |
| 446 | barWidth: 60, | |
| 447 | trim: true, | |
| 448 | want: "[==========================================================]", | |
| 449 | }, | |
| 450 | }, | |
| 451 | 99: { | |
| 452 | { | |
| 453 | style: BarStyle(), | |
| 454 | name: "t,c{100,1}", | |
| 455 | total: 100, | |
| 456 | current: 1, | |
| 457 | want: " [>----------------------------------------------------------------------------------------------] ", | |
| 458 | }, | |
| 459 | { | |
| 460 | style: BarStyle(), | |
| 461 | name: "t,c{100,1}trim", | |
| 462 | total: 100, | |
| 463 | current: 1, | |
| 464 | trim: true, | |
| 465 | want: "[>------------------------------------------------------------------------------------------------]", | |
| 466 | }, | |
| 467 | { | |
| 468 | style: BarStyle(), | |
| 469 | name: "t,c,r{100,40,33}", | |
| 470 | total: 100, | |
| 471 | current: 40, | |
| 472 | refill: 33, | |
| 473 | want: " [+++++++++++++++++++++++++++++++======>---------------------------------------------------------] ", | |
| 474 | }, | |
| 475 | { | |
| 476 | style: BarStyle(), | |
| 477 | name: "t,c,r{100,40,33}trim", | |
| 478 | total: 100, | |
| 479 | current: 40, | |
| 480 | refill: 33, | |
| 481 | trim: true, | |
| 482 | want: "[++++++++++++++++++++++++++++++++======>----------------------------------------------------------]", | |
| 483 | }, | |
| 484 | { | |
| 485 | style: BarStyle().Tip("<").Reverse(), | |
| 486 | name: "t,c,r{100,40,33},rev", | |
| 487 | total: 100, | |
| 488 | current: 40, | |
| 489 | refill: 33, | |
| 490 | want: " [---------------------------------------------------------<======+++++++++++++++++++++++++++++++] ", | |
| 491 | }, | |
| 492 | { | |
| 493 | style: BarStyle().Tip("<").Reverse(), | |
| 494 | name: "t,c,r{100,40,33}trim,rev", | |
| 495 | total: 100, | |
| 496 | current: 40, | |
| 497 | refill: 33, | |
| 498 | trim: true, | |
| 499 | want: "[----------------------------------------------------------<======++++++++++++++++++++++++++++++++]", | |
| 500 | }, | |
| 501 | { | |
| 502 | style: BarStyle(), | |
| 503 | name: "t,c{100,99}", | |
| 504 | total: 100, | |
| 505 | current: 99, | |
| 506 | want: " [=============================================================================================>-] ", | |
| 507 | }, | |
| 508 | { | |
| 509 | style: BarStyle(), | |
| 510 | name: "t,c{100,99}trim", | |
| 511 | total: 100, | |
| 512 | current: 99, | |
| 513 | trim: true, | |
| 514 | want: "[===============================================================================================>-]", | |
| 515 | }, | |
| 516 | { | |
| 517 | style: BarStyle(), | |
| 518 | name: "t,c{100,100}", | |
| 519 | total: 100, | |
| 520 | current: 100, | |
| 521 | want: " [===============================================================================================] ", | |
| 522 | }, | |
| 523 | { | |
| 524 | style: BarStyle(), | |
| 525 | name: "t,c{100,100}trim", | |
| 526 | total: 100, | |
| 527 | current: 100, | |
| 528 | trim: true, | |
| 529 | want: "[=================================================================================================]", | |
| 530 | }, | |
| 531 | { | |
| 532 | style: BarStyle(), | |
| 533 | name: "t,c,r{100,100,99}", | |
| 534 | total: 100, | |
| 535 | current: 100, | |
| 536 | refill: 99, | |
| 537 | want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=] ", | |
| 538 | }, | |
| 539 | { | |
| 540 | style: BarStyle(), | |
| 541 | name: "t,c,r{100,100,99}trim", | |
| 542 | total: 100, | |
| 543 | current: 100, | |
| 544 | refill: 99, | |
| 545 | trim: true, | |
| 546 | want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=]", | |
| 547 | }, | |
| 548 | { | |
| 549 | style: BarStyle().Reverse(), | |
| 550 | name: "t,c,r{100,100,99}rev", | |
| 551 | total: 100, | |
| 552 | current: 100, | |
| 553 | refill: 99, | |
| 554 | want: " [=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", | |
| 555 | }, | |
| 556 | { | |
| 557 | style: BarStyle().Reverse(), | |
| 558 | name: "t,c,r{100,100,99}trim,rev", | |
| 559 | total: 100, | |
| 560 | current: 100, | |
| 561 | refill: 99, | |
| 562 | trim: true, | |
| 563 | want: "[=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", | |
| 564 | }, | |
| 565 | { | |
| 566 | style: BarStyle(), | |
| 567 | name: "t,c,r{100,100,100}", | |
| 568 | total: 100, | |
| 569 | current: 100, | |
| 570 | refill: 100, | |
| 571 | want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", | |
| 572 | }, | |
| 573 | { | |
| 574 | style: BarStyle(), | |
| 575 | name: "t,c,r{100,100,100}trim", | |
| 576 | total: 100, | |
| 577 | current: 100, | |
| 578 | refill: 100, | |
| 579 | trim: true, | |
| 580 | want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", | |
| 581 | }, | |
| 582 | { | |
| 583 | style: BarStyle().Reverse(), | |
| 584 | name: "t,c,r{100,100,100}rev", | |
| 585 | total: 100, | |
| 586 | current: 100, | |
| 587 | refill: 100, | |
| 588 | want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", | |
| 589 | }, | |
| 590 | { | |
| 591 | style: BarStyle().Reverse(), | |
| 592 | name: "t,c,r{100,100,100}trim", | |
| 593 | total: 100, | |
| 594 | current: 100, | |
| 595 | refill: 100, | |
| 596 | trim: true, | |
| 597 | want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", | |
| 598 | }, | |
| 185 | 599 | }, |
| 186 | 600 | 100: { |
| 187 | 601 | { |
| 602 | style: BarStyle(), | |
| 188 | 603 | name: "t,c{100,0}", |
| 189 | 604 | total: 100, |
| 190 | 605 | current: 0, |
| 191 | 606 | want: " [------------------------------------------------------------------------------------------------] ", |
| 192 | 607 | }, |
| 193 | 608 | { |
| 609 | style: BarStyle(), | |
| 194 | 610 | name: "t,c{100,0}trim", |
| 195 | 611 | total: 100, |
| 196 | 612 | current: 0, |
| 198 | 614 | want: "[--------------------------------------------------------------------------------------------------]", |
| 199 | 615 | }, |
| 200 | 616 | { |
| 617 | style: BarStyle(), | |
| 201 | 618 | name: "t,c{100,1}", |
| 202 | 619 | total: 100, |
| 203 | 620 | current: 1, |
| 204 | 621 | want: " [>-----------------------------------------------------------------------------------------------] ", |
| 205 | 622 | }, |
| 206 | 623 | { |
| 624 | style: BarStyle(), | |
| 207 | 625 | name: "t,c{100,1}trim", |
| 208 | 626 | total: 100, |
| 209 | 627 | current: 1, |
| 211 | 629 | want: "[>-------------------------------------------------------------------------------------------------]", |
| 212 | 630 | }, |
| 213 | 631 | { |
| 632 | style: BarStyle(), | |
| 214 | 633 | name: "t,c{100,99}", |
| 215 | 634 | total: 100, |
| 216 | 635 | current: 99, |
| 217 | 636 | want: " [==============================================================================================>-] ", |
| 218 | 637 | }, |
| 219 | 638 | { |
| 639 | style: BarStyle(), | |
| 220 | 640 | name: "t,c{100,99}trim", |
| 221 | 641 | total: 100, |
| 222 | 642 | current: 99, |
| 224 | 644 | want: "[================================================================================================>-]", |
| 225 | 645 | }, |
| 226 | 646 | { |
| 647 | style: BarStyle(), | |
| 227 | 648 | name: "t,c{100,100}", |
| 228 | 649 | total: 100, |
| 229 | 650 | current: 100, |
| 230 | 651 | want: " [================================================================================================] ", |
| 231 | 652 | }, |
| 232 | 653 | { |
| 654 | style: BarStyle(), | |
| 233 | 655 | name: "t,c{100,100}trim", |
| 234 | 656 | total: 100, |
| 235 | 657 | current: 100, |
| 237 | 659 | want: "[==================================================================================================]", |
| 238 | 660 | }, |
| 239 | 661 | { |
| 662 | style: BarStyle(), | |
| 663 | name: "t,c,r{100,100,99}", | |
| 664 | total: 100, | |
| 665 | current: 100, | |
| 666 | refill: 99, | |
| 667 | want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=] ", | |
| 668 | }, | |
| 669 | { | |
| 670 | style: BarStyle(), | |
| 671 | name: "t,c,r{100,100,99}trim", | |
| 672 | total: 100, | |
| 673 | current: 100, | |
| 674 | refill: 99, | |
| 675 | trim: true, | |
| 676 | want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=]", | |
| 677 | }, | |
| 678 | { | |
| 679 | style: BarStyle(), | |
| 680 | name: "t,c,r{100,100,100}", | |
| 681 | total: 100, | |
| 682 | current: 100, | |
| 683 | refill: 100, | |
| 684 | want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", | |
| 685 | }, | |
| 686 | { | |
| 687 | style: BarStyle(), | |
| 240 | 688 | name: "t,c,r{100,100,100}trim", |
| 241 | 689 | total: 100, |
| 242 | 690 | current: 100, |
| 245 | 693 | want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", |
| 246 | 694 | }, |
| 247 | 695 | { |
| 248 | name: "t,c{100,33}", | |
| 249 | total: 100, | |
| 250 | current: 33, | |
| 251 | want: " [===============================>----------------------------------------------------------------] ", | |
| 252 | }, | |
| 253 | { | |
| 254 | name: "t,c{100,33}trim", | |
| 255 | total: 100, | |
| 256 | current: 33, | |
| 257 | trim: true, | |
| 258 | want: "[===============================>------------------------------------------------------------------]", | |
| 259 | }, | |
| 260 | { | |
| 261 | name: "t,c{100,33}trim,rev", | |
| 262 | total: 100, | |
| 263 | current: 33, | |
| 264 | trim: true, | |
| 265 | reverse: true, | |
| 266 | want: "[------------------------------------------------------------------<===============================]", | |
| 267 | }, | |
| 268 | { | |
| 269 | name: "t,c,r{100,33,33}", | |
| 270 | total: 100, | |
| 271 | current: 33, | |
| 272 | refill: 33, | |
| 273 | want: " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ", | |
| 274 | }, | |
| 275 | { | |
| 276 | name: "t,c,r{100,33,33}trim", | |
| 277 | total: 100, | |
| 278 | current: 33, | |
| 279 | refill: 33, | |
| 280 | trim: true, | |
| 281 | want: "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]", | |
| 282 | }, | |
| 283 | { | |
| 284 | name: "t,c,r{100,33,33}trim,rev", | |
| 285 | total: 100, | |
| 286 | current: 33, | |
| 287 | refill: 33, | |
| 288 | trim: true, | |
| 289 | reverse: true, | |
| 290 | want: "[------------------------------------------------------------------<+++++++++++++++++++++++++++++++]", | |
| 291 | }, | |
| 292 | { | |
| 696 | style: BarStyle().Reverse(), | |
| 697 | name: "t,c,r{100,100,99}rev", | |
| 698 | total: 100, | |
| 699 | current: 100, | |
| 700 | refill: 99, | |
| 701 | want: " [=+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", | |
| 702 | }, | |
| 703 | { | |
| 704 | style: BarStyle().Reverse(), | |
| 705 | name: "t,c,r{100,100,99}trim,rev", | |
| 706 | total: 100, | |
| 707 | current: 100, | |
| 708 | refill: 99, | |
| 709 | trim: true, | |
| 710 | want: "[=+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", | |
| 711 | }, | |
| 712 | { | |
| 713 | style: BarStyle().Reverse(), | |
| 714 | name: "t,c,r{100,100,100}rev", | |
| 715 | total: 100, | |
| 716 | current: 100, | |
| 717 | refill: 100, | |
| 718 | want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", | |
| 719 | }, | |
| 720 | { | |
| 721 | style: BarStyle().Reverse(), | |
| 722 | name: "t,c,r{100,100,100}trim", | |
| 723 | total: 100, | |
| 724 | current: 100, | |
| 725 | refill: 100, | |
| 726 | trim: true, | |
| 727 | want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", | |
| 728 | }, | |
| 729 | { | |
| 730 | style: BarStyle(), | |
| 293 | 731 | name: "t,c,r{100,40,33}", |
| 294 | 732 | total: 100, |
| 295 | 733 | current: 40, |
| 297 | 735 | want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ", |
| 298 | 736 | }, |
| 299 | 737 | { |
| 738 | style: BarStyle(), | |
| 300 | 739 | name: "t,c,r{100,40,33}trim", |
| 301 | 740 | total: 100, |
| 302 | 741 | current: 40, |
| 305 | 744 | want: "[++++++++++++++++++++++++++++++++======>-----------------------------------------------------------]", |
| 306 | 745 | }, |
| 307 | 746 | { |
| 747 | style: BarStyle().Tip("<").Reverse(), | |
| 308 | 748 | name: "t,c,r{100,40,33},rev", |
| 309 | 749 | total: 100, |
| 310 | 750 | current: 40, |
| 311 | 751 | refill: 33, |
| 312 | reverse: true, | |
| 313 | 752 | want: " [----------------------------------------------------------<=====++++++++++++++++++++++++++++++++] ", |
| 314 | 753 | }, |
| 315 | 754 | { |
| 755 | style: BarStyle().Tip("<").Reverse(), | |
| 316 | 756 | name: "t,c,r{100,40,33}trim,rev", |
| 317 | 757 | total: 100, |
| 318 | 758 | current: 40, |
| 319 | 759 | refill: 33, |
| 320 | 760 | trim: true, |
| 321 | reverse: true, | |
| 322 | 761 | want: "[-----------------------------------------------------------<======++++++++++++++++++++++++++++++++]", |
| 323 | }, | |
| 324 | { | |
| 325 | name: "[=の-] t,c{100,1}", | |
| 326 | style: "[=の-]", | |
| 327 | total: 100, | |
| 328 | current: 1, | |
| 329 | want: " [の---------------------------------------------------------------------------------------------…] ", | |
| 330 | }, | |
| 331 | }, | |
| 332 | 197: { | |
| 333 | { | |
| 334 | name: "t,c,r{97486999,2805950,2805483}trim", | |
| 335 | total: 97486999, | |
| 336 | current: 2805950, | |
| 337 | refill: 2805483, | |
| 338 | barWidth: 60, | |
| 339 | trim: true, | |
| 340 | want: "[+>--------------------------------------------------------]", | |
| 341 | 762 | }, |
| 342 | 763 | }, |
| 343 | 764 | } |
| 345 | 766 | var tmpBuf bytes.Buffer |
| 346 | 767 | for tw, cases := range testSuite { |
| 347 | 768 | for _, tc := range cases { |
| 348 | s := newTestState(tc.style, tc.reverse) | |
| 769 | s := newTestState(tc.style.Build()) | |
| 349 | 770 | s.reqWidth = tc.barWidth |
| 350 | 771 | s.total = tc.total |
| 351 | 772 | s.current = tc.current |
| 352 | 773 | s.trimSpace = tc.trim |
| 353 | 774 | s.refill = tc.refill |
| 354 | 775 | tmpBuf.Reset() |
| 355 | tmpBuf.ReadFrom(s.draw(newStatistics(tw, s))) | |
| 776 | _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s))) | |
| 777 | if err != nil { | |
| 778 | t.FailNow() | |
| 779 | } | |
| 356 | 780 | by := tmpBuf.Bytes() |
| 357 | 781 | |
| 358 | 782 | got := string(by[:len(by)-1]) |
| 366 | 790 | } |
| 367 | 791 | } |
| 368 | 792 | |
| 369 | func newTestState(style string, rev bool) *bState { | |
| 370 | s := &bState{ | |
| 371 | filler: NewBarFillerPick(style, rev), | |
| 372 | bufP: new(bytes.Buffer), | |
| 373 | bufB: new(bytes.Buffer), | |
| 374 | bufA: new(bytes.Buffer), | |
| 793 | func TestDrawTipOnComplete(t *testing.T) { | |
| 794 | // key is termWidth | |
| 795 | testSuite := map[int][]struct { | |
| 796 | style BarStyleComposer | |
| 797 | name string | |
| 798 | total int64 | |
| 799 | current int64 | |
| 800 | refill int64 | |
| 801 | barWidth int | |
| 802 | trim bool | |
| 803 | want string | |
| 804 | }{ | |
| 805 | 3: { | |
| 806 | { | |
| 807 | style: BarStyle().TipOnComplete(">"), | |
| 808 | name: "t,c{60,60}", | |
| 809 | total: 60, | |
| 810 | current: 60, | |
| 811 | want: " ", | |
| 812 | }, | |
| 813 | { | |
| 814 | style: BarStyle().TipOnComplete(">"), | |
| 815 | name: "t,c{60,60}trim", | |
| 816 | total: 60, | |
| 817 | current: 60, | |
| 818 | trim: true, | |
| 819 | want: "[>]", | |
| 820 | }, | |
| 821 | }, | |
| 822 | 4: { | |
| 823 | { | |
| 824 | style: BarStyle().TipOnComplete(">"), | |
| 825 | name: "t,c{60,59}", | |
| 826 | total: 60, | |
| 827 | current: 59, | |
| 828 | want: " [] ", | |
| 829 | }, | |
| 830 | { | |
| 831 | style: BarStyle().TipOnComplete(">"), | |
| 832 | name: "t,c{60,59}trim", | |
| 833 | total: 60, | |
| 834 | current: 59, | |
| 835 | trim: true, | |
| 836 | want: "[=>]", | |
| 837 | }, | |
| 838 | { | |
| 839 | style: BarStyle().TipOnComplete(">"), | |
| 840 | name: "t,c{60,60}", | |
| 841 | total: 60, | |
| 842 | current: 60, | |
| 843 | want: " [] ", | |
| 844 | }, | |
| 845 | { | |
| 846 | style: BarStyle().TipOnComplete(">"), | |
| 847 | name: "t,c{60,60}trim", | |
| 848 | total: 60, | |
| 849 | current: 60, | |
| 850 | trim: true, | |
| 851 | want: "[=>]", | |
| 852 | }, | |
| 853 | }, | |
| 854 | 5: { | |
| 855 | { | |
| 856 | style: BarStyle().TipOnComplete(">"), | |
| 857 | name: "t,c{60,59}", | |
| 858 | total: 60, | |
| 859 | current: 59, | |
| 860 | want: " [>] ", | |
| 861 | }, | |
| 862 | { | |
| 863 | style: BarStyle().TipOnComplete(">"), | |
| 864 | name: "t,c{60,59}trim", | |
| 865 | total: 60, | |
| 866 | current: 59, | |
| 867 | trim: true, | |
| 868 | want: "[==>]", | |
| 869 | }, | |
| 870 | { | |
| 871 | style: BarStyle().TipOnComplete(">"), | |
| 872 | name: "t,c{60,60}", | |
| 873 | total: 60, | |
| 874 | current: 60, | |
| 875 | want: " [>] ", | |
| 876 | }, | |
| 877 | { | |
| 878 | style: BarStyle().TipOnComplete(">"), | |
| 879 | name: "t,c{60,60}trim", | |
| 880 | total: 60, | |
| 881 | current: 60, | |
| 882 | trim: true, | |
| 883 | want: "[==>]", | |
| 884 | }, | |
| 885 | }, | |
| 886 | 6: { | |
| 887 | { | |
| 888 | style: BarStyle().TipOnComplete(">"), | |
| 889 | name: "t,c{60,59}", | |
| 890 | total: 60, | |
| 891 | current: 59, | |
| 892 | want: " [=>] ", | |
| 893 | }, | |
| 894 | { | |
| 895 | style: BarStyle().TipOnComplete(">"), | |
| 896 | name: "t,c{60,59}trim", | |
| 897 | total: 60, | |
| 898 | current: 59, | |
| 899 | trim: true, | |
| 900 | want: "[===>]", | |
| 901 | }, | |
| 902 | { | |
| 903 | style: BarStyle().TipOnComplete(">"), | |
| 904 | name: "t,c{60,60}", | |
| 905 | total: 60, | |
| 906 | current: 60, | |
| 907 | want: " [=>] ", | |
| 908 | }, | |
| 909 | { | |
| 910 | style: BarStyle().TipOnComplete(">"), | |
| 911 | name: "t,c{60,60}trim", | |
| 912 | total: 60, | |
| 913 | current: 60, | |
| 914 | trim: true, | |
| 915 | want: "[===>]", | |
| 916 | }, | |
| 917 | }, | |
| 918 | 7: { | |
| 919 | { | |
| 920 | style: BarStyle().TipOnComplete(">"), | |
| 921 | name: "t,c{60,59}", | |
| 922 | total: 60, | |
| 923 | current: 59, | |
| 924 | want: " [==>] ", | |
| 925 | }, | |
| 926 | { | |
| 927 | style: BarStyle().TipOnComplete(">"), | |
| 928 | name: "t,c{60,59}trim", | |
| 929 | total: 60, | |
| 930 | current: 59, | |
| 931 | trim: true, | |
| 932 | want: "[====>]", | |
| 933 | }, | |
| 934 | { | |
| 935 | style: BarStyle().TipOnComplete(">"), | |
| 936 | name: "t,c{60,60}", | |
| 937 | total: 60, | |
| 938 | current: 60, | |
| 939 | want: " [==>] ", | |
| 940 | }, | |
| 941 | { | |
| 942 | style: BarStyle().TipOnComplete(">"), | |
| 943 | name: "t,c{60,60}trim", | |
| 944 | total: 60, | |
| 945 | current: 60, | |
| 946 | trim: true, | |
| 947 | want: "[====>]", | |
| 948 | }, | |
| 949 | }, | |
| 950 | 8: { | |
| 951 | { | |
| 952 | style: BarStyle().TipOnComplete(">"), | |
| 953 | name: "t,c{60,59}", | |
| 954 | total: 60, | |
| 955 | current: 59, | |
| 956 | want: " [===>] ", | |
| 957 | }, | |
| 958 | { | |
| 959 | style: BarStyle().TipOnComplete(">"), | |
| 960 | name: "t,c{60,59}trim", | |
| 961 | total: 60, | |
| 962 | current: 59, | |
| 963 | trim: true, | |
| 964 | want: "[=====>]", | |
| 965 | }, | |
| 966 | { | |
| 967 | style: BarStyle().TipOnComplete(">"), | |
| 968 | name: "t,c{60,60}", | |
| 969 | total: 60, | |
| 970 | current: 60, | |
| 971 | want: " [===>] ", | |
| 972 | }, | |
| 973 | { | |
| 974 | style: BarStyle().TipOnComplete(">"), | |
| 975 | name: "t,c{60,60}trim", | |
| 976 | total: 60, | |
| 977 | current: 60, | |
| 978 | trim: true, | |
| 979 | want: "[=====>]", | |
| 980 | }, | |
| 981 | }, | |
| 982 | 80: { | |
| 983 | { | |
| 984 | style: BarStyle().TipOnComplete(">"), | |
| 985 | name: "t,c{60,59}", | |
| 986 | total: 60, | |
| 987 | current: 59, | |
| 988 | want: " [==========================================================================>-] ", | |
| 989 | }, | |
| 990 | { | |
| 991 | style: BarStyle().TipOnComplete(">"), | |
| 992 | name: "t,c{60,59}trim", | |
| 993 | total: 60, | |
| 994 | current: 59, | |
| 995 | trim: true, | |
| 996 | want: "[============================================================================>-]", | |
| 997 | }, | |
| 998 | { | |
| 999 | style: BarStyle().TipOnComplete(">"), | |
| 1000 | name: "t,c,bw{60,59,60}", | |
| 1001 | total: 60, | |
| 1002 | current: 59, | |
| 1003 | barWidth: 60, | |
| 1004 | want: " [========================================================>-] ", | |
| 1005 | }, | |
| 1006 | { | |
| 1007 | style: BarStyle().TipOnComplete(">"), | |
| 1008 | name: "t,c,bw{60,59,60}trim", | |
| 1009 | total: 60, | |
| 1010 | current: 59, | |
| 1011 | barWidth: 60, | |
| 1012 | trim: true, | |
| 1013 | want: "[========================================================>-]", | |
| 1014 | }, | |
| 1015 | { | |
| 1016 | style: BarStyle().TipOnComplete(">"), | |
| 1017 | name: "t,c{60,60}", | |
| 1018 | total: 60, | |
| 1019 | current: 60, | |
| 1020 | want: " [===========================================================================>] ", | |
| 1021 | }, | |
| 1022 | { | |
| 1023 | style: BarStyle().TipOnComplete(">"), | |
| 1024 | name: "t,c{60,60}trim", | |
| 1025 | total: 60, | |
| 1026 | current: 60, | |
| 1027 | trim: true, | |
| 1028 | want: "[=============================================================================>]", | |
| 1029 | }, | |
| 1030 | { | |
| 1031 | style: BarStyle().TipOnComplete(">"), | |
| 1032 | name: "t,c,bw{60,60,60}", | |
| 1033 | total: 60, | |
| 1034 | current: 60, | |
| 1035 | barWidth: 60, | |
| 1036 | want: " [=========================================================>] ", | |
| 1037 | }, | |
| 1038 | { | |
| 1039 | style: BarStyle().TipOnComplete(">"), | |
| 1040 | name: "t,c,bw{60,60,60}trim", | |
| 1041 | total: 60, | |
| 1042 | current: 60, | |
| 1043 | barWidth: 60, | |
| 1044 | trim: true, | |
| 1045 | want: "[=========================================================>]", | |
| 1046 | }, | |
| 1047 | }, | |
| 1048 | 99: { | |
| 1049 | { | |
| 1050 | style: BarStyle().TipOnComplete(">"), | |
| 1051 | name: "t,c{100,99}", | |
| 1052 | total: 100, | |
| 1053 | current: 99, | |
| 1054 | want: " [=============================================================================================>-] ", | |
| 1055 | }, | |
| 1056 | { | |
| 1057 | style: BarStyle().TipOnComplete(">"), | |
| 1058 | name: "t,c{100,99}trim", | |
| 1059 | total: 100, | |
| 1060 | current: 99, | |
| 1061 | trim: true, | |
| 1062 | want: "[===============================================================================================>-]", | |
| 1063 | }, | |
| 1064 | { | |
| 1065 | style: BarStyle().TipOnComplete(">"), | |
| 1066 | name: "t,c{100,100}", | |
| 1067 | total: 100, | |
| 1068 | current: 100, | |
| 1069 | want: " [==============================================================================================>] ", | |
| 1070 | }, | |
| 1071 | { | |
| 1072 | style: BarStyle().TipOnComplete(">"), | |
| 1073 | name: "t,c{100,100}trim", | |
| 1074 | total: 100, | |
| 1075 | current: 100, | |
| 1076 | trim: true, | |
| 1077 | want: "[================================================================================================>]", | |
| 1078 | }, | |
| 1079 | { | |
| 1080 | style: BarStyle().TipOnComplete(">"), | |
| 1081 | name: "t,c,r{100,100,99}", | |
| 1082 | total: 100, | |
| 1083 | current: 100, | |
| 1084 | refill: 99, | |
| 1085 | want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ", | |
| 1086 | }, | |
| 1087 | { | |
| 1088 | style: BarStyle().TipOnComplete(">"), | |
| 1089 | name: "t,c,r{100,100,99}trim", | |
| 1090 | total: 100, | |
| 1091 | current: 100, | |
| 1092 | refill: 99, | |
| 1093 | trim: true, | |
| 1094 | want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", | |
| 1095 | }, | |
| 1096 | { | |
| 1097 | style: BarStyle().TipOnComplete("<").Reverse(), | |
| 1098 | name: `t,c,r{100,100,99}TipOnComplete("<").Reverse()`, | |
| 1099 | total: 100, | |
| 1100 | current: 100, | |
| 1101 | refill: 99, | |
| 1102 | want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", | |
| 1103 | }, | |
| 1104 | { | |
| 1105 | style: BarStyle().TipOnComplete("<").Reverse(), | |
| 1106 | name: `t,c,r{100,100,99}TipOnComplete("<").Reverse()trim`, | |
| 1107 | total: 100, | |
| 1108 | current: 100, | |
| 1109 | refill: 99, | |
| 1110 | trim: true, | |
| 1111 | want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", | |
| 1112 | }, | |
| 1113 | { | |
| 1114 | style: BarStyle().TipOnComplete(">"), | |
| 1115 | name: "t,c,r{100,100,100}", | |
| 1116 | total: 100, | |
| 1117 | current: 100, | |
| 1118 | refill: 100, | |
| 1119 | want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ", | |
| 1120 | }, | |
| 1121 | { | |
| 1122 | style: BarStyle().TipOnComplete(">"), | |
| 1123 | name: "t,c,r{100,100,100}trim", | |
| 1124 | total: 100, | |
| 1125 | current: 100, | |
| 1126 | refill: 100, | |
| 1127 | trim: true, | |
| 1128 | want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", | |
| 1129 | }, | |
| 1130 | { | |
| 1131 | style: BarStyle().TipOnComplete("<").Reverse(), | |
| 1132 | name: `t,c,r{100,100,100}TipOnComplete("<").Reverse()`, | |
| 1133 | total: 100, | |
| 1134 | current: 100, | |
| 1135 | refill: 100, | |
| 1136 | want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ", | |
| 1137 | }, | |
| 1138 | { | |
| 1139 | style: BarStyle().TipOnComplete("<").Reverse(), | |
| 1140 | name: `t,c,r{100,100,100}TipOnComplete("<").Reverse()trim`, | |
| 1141 | total: 100, | |
| 1142 | current: 100, | |
| 1143 | refill: 100, | |
| 1144 | trim: true, | |
| 1145 | want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]", | |
| 1146 | }, | |
| 1147 | }, | |
| 1148 | 100: { | |
| 1149 | { | |
| 1150 | style: BarStyle().TipOnComplete(">"), | |
| 1151 | name: "t,c{100,99}", | |
| 1152 | total: 100, | |
| 1153 | current: 99, | |
| 1154 | want: " [==============================================================================================>-] ", | |
| 1155 | }, | |
| 1156 | { | |
| 1157 | style: BarStyle().TipOnComplete(">"), | |
| 1158 | name: "t,c{100,99}trim", | |
| 1159 | total: 100, | |
| 1160 | current: 99, | |
| 1161 | trim: true, | |
| 1162 | want: "[================================================================================================>-]", | |
| 1163 | }, | |
| 1164 | { | |
| 1165 | style: BarStyle().TipOnComplete(">"), | |
| 1166 | name: "t,c{100,100}", | |
| 1167 | total: 100, | |
| 1168 | current: 100, | |
| 1169 | want: " [===============================================================================================>] ", | |
| 1170 | }, | |
| 1171 | { | |
| 1172 | style: BarStyle().TipOnComplete(">"), | |
| 1173 | name: "t,c{100,100}trim", | |
| 1174 | total: 100, | |
| 1175 | current: 100, | |
| 1176 | trim: true, | |
| 1177 | want: "[=================================================================================================>]", | |
| 1178 | }, | |
| 1179 | { | |
| 1180 | style: BarStyle().TipOnComplete(">"), | |
| 1181 | name: "t,c,r{100,100,99}", | |
| 1182 | total: 100, | |
| 1183 | current: 100, | |
| 1184 | refill: 99, | |
| 1185 | want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ", | |
| 1186 | }, | |
| 1187 | { | |
| 1188 | style: BarStyle().TipOnComplete(">"), | |
| 1189 | name: "t,c,r{100,100,99}trim", | |
| 1190 | total: 100, | |
| 1191 | current: 100, | |
| 1192 | refill: 99, | |
| 1193 | trim: true, | |
| 1194 | want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", | |
| 1195 | }, | |
| 1196 | { | |
| 1197 | style: BarStyle().TipOnComplete(">"), | |
| 1198 | name: "t,c,r{100,100,100}", | |
| 1199 | total: 100, | |
| 1200 | current: 100, | |
| 1201 | refill: 100, | |
| 1202 | want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ", | |
| 1203 | }, | |
| 1204 | { | |
| 1205 | style: BarStyle().TipOnComplete(">"), | |
| 1206 | name: "t,c,r{100,100,100}trim", | |
| 1207 | total: 100, | |
| 1208 | current: 100, | |
| 1209 | refill: 100, | |
| 1210 | trim: true, | |
| 1211 | want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", | |
| 1212 | }, | |
| 1213 | }, | |
| 375 | 1214 | } |
| 376 | return s | |
| 1215 | ||
| 1216 | var tmpBuf bytes.Buffer | |
| 1217 | for tw, cases := range testSuite { | |
| 1218 | for _, tc := range cases { | |
| 1219 | s := newTestState(tc.style.Build()) | |
| 1220 | s.reqWidth = tc.barWidth | |
| 1221 | s.total = tc.total | |
| 1222 | s.current = tc.current | |
| 1223 | s.trimSpace = tc.trim | |
| 1224 | s.refill = tc.refill | |
| 1225 | tmpBuf.Reset() | |
| 1226 | _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s))) | |
| 1227 | if err != nil { | |
| 1228 | t.FailNow() | |
| 1229 | } | |
| 1230 | by := tmpBuf.Bytes() | |
| 1231 | ||
| 1232 | got := string(by[:len(by)-1]) | |
| 1233 | if !utf8.ValidString(got) { | |
| 1234 | t.Fail() | |
| 1235 | } | |
| 1236 | if got != tc.want { | |
| 1237 | 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)) | |
| 1238 | } | |
| 1239 | } | |
| 1240 | } | |
| 377 | 1241 | } |
| 1242 | ||
| 1243 | func TestDrawDoubleWidth(t *testing.T) { | |
| 1244 | // key is termWidth | |
| 1245 | testSuite := map[int][]struct { | |
| 1246 | style BarStyleComposer | |
| 1247 | name string | |
| 1248 | total int64 | |
| 1249 | current int64 | |
| 1250 | refill int64 | |
| 1251 | barWidth int | |
| 1252 | trim bool | |
| 1253 | want string | |
| 1254 | }{ | |
| 1255 | 99: { | |
| 1256 | { | |
| 1257 | style: BarStyle().Lbound("の").Rbound("の"), | |
| 1258 | name: `t,c{100,1}.Lbound("の").Rbound("の")`, | |
| 1259 | total: 100, | |
| 1260 | current: 1, | |
| 1261 | want: " の>--------------------------------------------------------------------------------------------の ", | |
| 1262 | }, | |
| 1263 | { | |
| 1264 | style: BarStyle().Lbound("の").Rbound("の"), | |
| 1265 | name: `t,c{100,1}.Lbound("の").Rbound("の")`, | |
| 1266 | total: 100, | |
| 1267 | current: 2, | |
| 1268 | want: " の=>-------------------------------------------------------------------------------------------の ", | |
| 1269 | }, | |
| 1270 | { | |
| 1271 | style: BarStyle().Tip("だ"), | |
| 1272 | name: `t,c{100,1}Tip("だ")`, | |
| 1273 | total: 100, | |
| 1274 | current: 1, | |
| 1275 | want: " [だ---------------------------------------------------------------------------------------------] ", | |
| 1276 | }, | |
| 1277 | { | |
| 1278 | style: BarStyle().Tip("だ"), | |
| 1279 | name: `t,c{100,2}Tip("だ")`, | |
| 1280 | total: 100, | |
| 1281 | current: 2, | |
| 1282 | want: " [だ---------------------------------------------------------------------------------------------] ", | |
| 1283 | }, | |
| 1284 | { | |
| 1285 | style: BarStyle().Tip("だ"), | |
| 1286 | name: `t,c{100,3}Tip("だ")`, | |
| 1287 | total: 100, | |
| 1288 | current: 3, | |
| 1289 | want: " [=だ--------------------------------------------------------------------------------------------] ", | |
| 1290 | }, | |
| 1291 | { | |
| 1292 | style: BarStyle().Tip("だ"), | |
| 1293 | name: `t,c{100,99}Tip("だ")`, | |
| 1294 | total: 100, | |
| 1295 | current: 99, | |
| 1296 | want: " [============================================================================================だ-] ", | |
| 1297 | }, | |
| 1298 | { | |
| 1299 | style: BarStyle().Tip("だ"), | |
| 1300 | name: `t,c{100,100}Tip("だ")`, | |
| 1301 | total: 100, | |
| 1302 | current: 100, | |
| 1303 | want: " [===============================================================================================] ", | |
| 1304 | }, | |
| 1305 | { | |
| 1306 | style: BarStyle().TipOnComplete("だ"), | |
| 1307 | name: `t,c{100,100}TipOnComplete("だ")`, | |
| 1308 | total: 100, | |
| 1309 | current: 100, | |
| 1310 | want: " [=============================================================================================だ] ", | |
| 1311 | }, | |
| 1312 | { | |
| 1313 | style: BarStyle().Filler("の").Tip("だ").Padding("つ"), | |
| 1314 | name: `t,c{100,1}Filler("の").Tip("だ").Padding("つ")`, | |
| 1315 | total: 100, | |
| 1316 | current: 1, | |
| 1317 | want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", | |
| 1318 | }, | |
| 1319 | { | |
| 1320 | style: BarStyle().Filler("の").Tip("だ").Padding("つ"), | |
| 1321 | name: `t,c{100,2}Filler("の").Tip("だ").Padding("つ")`, | |
| 1322 | total: 100, | |
| 1323 | current: 2, | |
| 1324 | want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", | |
| 1325 | }, | |
| 1326 | { | |
| 1327 | style: BarStyle().Filler("の").Tip("だ").Padding("つ"), | |
| 1328 | name: `t,c{100,99}Filler("の").Tip("だ").Padding("つ")`, | |
| 1329 | total: 100, | |
| 1330 | current: 99, | |
| 1331 | want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ", | |
| 1332 | }, | |
| 1333 | { | |
| 1334 | style: BarStyle().Filler("の").Tip("だ").Padding("つ"), | |
| 1335 | name: `t,c{100,100}.Filler("の").Tip("だ").Padding("つ")`, | |
| 1336 | total: 100, | |
| 1337 | current: 100, | |
| 1338 | want: " […ののののののののののののののののののののののののののののののののののののののののののののののの] ", | |
| 1339 | }, | |
| 1340 | { | |
| 1341 | style: BarStyle().Filler("の").Tip("だ").Padding("つ").Reverse(), | |
| 1342 | name: `t,c{100,100}Filler("の").Tip("だ").Padding("つ").Reverse()`, | |
| 1343 | total: 100, | |
| 1344 | current: 100, | |
| 1345 | want: " [ののののののののののののののののののののののののののののののののののののののののののののののの…] ", | |
| 1346 | }, | |
| 1347 | { | |
| 1348 | style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ"), | |
| 1349 | name: `t,c{100,99}Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ")`, | |
| 1350 | total: 100, | |
| 1351 | current: 99, | |
| 1352 | want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ", | |
| 1353 | }, | |
| 1354 | { | |
| 1355 | style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ"), | |
| 1356 | name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ")`, | |
| 1357 | total: 100, | |
| 1358 | current: 100, | |
| 1359 | want: " […ののののののののののののののののののののののののののののののののののののののののののののののだ] ", | |
| 1360 | }, | |
| 1361 | { | |
| 1362 | style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ").Reverse(), | |
| 1363 | name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ").Reverse()`, | |
| 1364 | total: 100, | |
| 1365 | current: 100, | |
| 1366 | want: " [だのののののののののののののののののののののののののののののののののののののののののののののの…] ", | |
| 1367 | }, | |
| 1368 | { | |
| 1369 | style: BarStyle().Refiller("の"), | |
| 1370 | name: `t,c,r{100,100,99}Refiller("の")`, | |
| 1371 | total: 100, | |
| 1372 | current: 100, | |
| 1373 | refill: 99, | |
| 1374 | want: " [ののののののののののののののののののののののののののののののののののののののののののののののの=] ", | |
| 1375 | }, | |
| 1376 | { | |
| 1377 | style: BarStyle().Refiller("の"), | |
| 1378 | name: `t,c,r{100,100,99}Refiller("の")trim`, | |
| 1379 | total: 100, | |
| 1380 | current: 100, | |
| 1381 | refill: 99, | |
| 1382 | trim: true, | |
| 1383 | want: "[のののののののののののののののののののののののののののののののののののののののののののののののの=]", | |
| 1384 | }, | |
| 1385 | }, | |
| 1386 | } | |
| 1387 | ||
| 1388 | var tmpBuf bytes.Buffer | |
| 1389 | for tw, cases := range testSuite { | |
| 1390 | for _, tc := range cases { | |
| 1391 | s := newTestState(tc.style.Build()) | |
| 1392 | s.reqWidth = tc.barWidth | |
| 1393 | s.total = tc.total | |
| 1394 | s.current = tc.current | |
| 1395 | s.trimSpace = tc.trim | |
| 1396 | s.refill = tc.refill | |
| 1397 | tmpBuf.Reset() | |
| 1398 | _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s))) | |
| 1399 | if err != nil { | |
| 1400 | t.FailNow() | |
| 1401 | } | |
| 1402 | by := tmpBuf.Bytes() | |
| 1403 | ||
| 1404 | got := string(by[:len(by)-1]) | |
| 1405 | if !utf8.ValidString(got) { | |
| 1406 | t.Fail() | |
| 1407 | } | |
| 1408 | if got != tc.want { | |
| 1409 | 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)) | |
| 1410 | } | |
| 1411 | } | |
| 1412 | } | |
| 1413 | } | |
| 1414 | ||
| 1415 | func newTestState(filler BarFiller) *bState { | |
| 1416 | bs := &bState{ | |
| 1417 | filler: filler, | |
| 1418 | } | |
| 1419 | for i := 0; i < len(bs.buffers); i++ { | |
| 1420 | bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512)) | |
| 1421 | } | |
| 1422 | return bs | |
| 1423 | } | |
| 6 | 6 | "math/rand" |
| 7 | 7 | "time" |
| 8 | 8 | |
| 9 | "github.com/vbauerster/mpb/v6" | |
| 10 | "github.com/vbauerster/mpb/v6/decor" | |
| 9 | "github.com/vbauerster/mpb/v7" | |
| 10 | "github.com/vbauerster/mpb/v7/decor" | |
| 11 | 11 | ) |
| 12 | 12 | |
| 13 | 13 | func Example() { |
| 16 | 16 | |
| 17 | 17 | total := 100 |
| 18 | 18 | name := "Single Bar:" |
| 19 | // adding a single bar, which will inherit container's width | |
| 20 | bar := p.Add(int64(total), | |
| 21 | // progress bar filler with customized style | |
| 22 | mpb.NewBarFiller("╢▌▌░╟"), | |
| 19 | // create a single bar, which will inherit container's width | |
| 20 | bar := p.New(int64(total), | |
| 21 | // BarFillerBuilder with custom style | |
| 22 | mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"), | |
| 23 | 23 | mpb.PrependDecorators( |
| 24 | 24 | // display our name with one space on the right |
| 25 | 25 | decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), |
| 77 | 77 | defer proxyReader.Close() |
| 78 | 78 | |
| 79 | 79 | // and copy from reader, ignoring errors |
| 80 | io.Copy(ioutil.Discard, proxyReader) | |
| 80 | _, _ = io.Copy(ioutil.Discard, proxyReader) | |
| 81 | 81 | |
| 82 | 82 | p.Wait() |
| 83 | 83 | } |
| 0 | module github.com/vbauerster/mpb/v6 | |
| 0 | module github.com/vbauerster/mpb/v7 | |
| 1 | 1 | |
| 2 | 2 | require ( |
| 3 | github.com/VividCortex/ewma v1.1.1 | |
| 3 | github.com/VividCortex/ewma v1.2.0 | |
| 4 | 4 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d |
| 5 | github.com/mattn/go-runewidth v0.0.10 | |
| 6 | github.com/rivo/uniseg v0.2.0 | |
| 7 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492 | |
| 5 | github.com/mattn/go-runewidth v0.0.13 | |
| 6 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 | |
| 8 | 7 | ) |
| 9 | 8 | |
| 10 | 9 | go 1.14 |
| 0 | github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= | |
| 1 | github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= | |
| 0 | github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= | |
| 1 | github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= | |
| 2 | 2 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= |
| 3 | 3 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= |
| 4 | github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= | |
| 5 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= | |
| 6 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | |
| 4 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= | |
| 5 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | |
| 7 | 6 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= |
| 8 | 7 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= |
| 9 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= | |
| 10 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
| 8 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= | |
| 9 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
| 2 | 2 | import "math" |
| 3 | 3 | |
| 4 | 4 | // Percentage is a helper function, to calculate percentage. |
| 5 | func Percentage(total, current int64, width int) float64 { | |
| 5 | func Percentage(total, current int64, width uint) float64 { | |
| 6 | 6 | if total <= 0 { |
| 7 | 7 | return 0 |
| 8 | 8 | } |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | // PercentageRound same as Percentage but with math.Round. |
| 16 | func PercentageRound(total, current int64, width int) float64 { | |
| 16 | func PercentageRound(total, current int64, width uint) float64 { | |
| 17 | 17 | return math.Round(Percentage(total, current, width)) |
| 18 | 18 | } |
| 3 | 3 | |
| 4 | 4 | func TestPercentage(t *testing.T) { |
| 5 | 5 | // key is barWidth |
| 6 | testSuite := map[int][]struct { | |
| 6 | testSuite := map[uint][]struct { | |
| 7 | 7 | name string |
| 8 | 8 | total int64 |
| 9 | 9 | current int64 |
| 0 | package internal | |
| 1 | ||
| 2 | // Predicate helper for internal use. | |
| 3 | func Predicate(pick bool) func() bool { | |
| 4 | return func() bool { return pick } | |
| 5 | } |
| 2 | 2 | // CheckRequestedWidth checks that requested width doesn't overflow |
| 3 | 3 | // available width |
| 4 | 4 | func CheckRequestedWidth(requested, available int) int { |
| 5 | if requested <= 0 || requested >= available { | |
| 5 | if requested < 1 || requested >= available { | |
| 6 | 6 | return available |
| 7 | 7 | } |
| 8 | 8 | return requested |
| 5 | 5 | "context" |
| 6 | 6 | "fmt" |
| 7 | 7 | "io" |
| 8 | "io/ioutil" | |
| 9 | "log" | |
| 10 | 8 | "math" |
| 11 | 9 | "os" |
| 12 | 10 | "sync" |
| 13 | 11 | "time" |
| 14 | 12 | |
| 15 | "github.com/vbauerster/mpb/v6/cwriter" | |
| 16 | "github.com/vbauerster/mpb/v6/decor" | |
| 13 | "github.com/vbauerster/mpb/v7/cwriter" | |
| 14 | "github.com/vbauerster/mpb/v7/decor" | |
| 17 | 15 | ) |
| 18 | 16 | |
| 19 | 17 | const ( |
| 20 | 18 | // default RefreshRate |
| 21 | prr = 120 * time.Millisecond | |
| 19 | prr = 150 * time.Millisecond | |
| 22 | 20 | ) |
| 23 | 21 | |
| 24 | 22 | // Progress represents a container that renders one or more progress |
| 32 | 30 | done chan struct{} |
| 33 | 31 | refreshCh chan time.Time |
| 34 | 32 | once sync.Once |
| 35 | dlogger *log.Logger | |
| 36 | 33 | } |
| 37 | 34 | |
| 38 | 35 | // pState holds bars in its priorityQueue. It gets passed to |
| 74 | 71 | rr: prr, |
| 75 | 72 | parkedBars: make(map[*Bar]*Bar), |
| 76 | 73 | output: os.Stdout, |
| 77 | debugOut: ioutil.Discard, | |
| 78 | 74 | } |
| 79 | 75 | |
| 80 | 76 | for _, opt := range options { |
| 90 | 86 | bwg: new(sync.WaitGroup), |
| 91 | 87 | operateState: make(chan func(*pState)), |
| 92 | 88 | done: make(chan struct{}), |
| 93 | dlogger: log.New(s.debugOut, "[mpb] ", log.Lshortfile), | |
| 94 | 89 | } |
| 95 | 90 | |
| 96 | 91 | p.cwg.Add(1) |
| 98 | 93 | return p |
| 99 | 94 | } |
| 100 | 95 | |
| 101 | // AddBar creates a bar with default bar filler. Different filler can | |
| 102 | // be choosen and applied via `*Progress.Add(...) *Bar` method. | |
| 96 | // AddBar creates a bar with default bar filler. | |
| 103 | 97 | func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { |
| 104 | return p.Add(total, NewBarFiller(BarDefaultStyle), options...) | |
| 105 | } | |
| 106 | ||
| 107 | // AddSpinner creates a bar with default spinner filler. Different | |
| 108 | // filler can be choosen and applied via `*Progress.Add(...) *Bar` | |
| 109 | // method. | |
| 110 | func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar { | |
| 111 | return p.Add(total, NewSpinnerFiller(SpinnerDefaultStyle, alignment), options...) | |
| 98 | return p.New(total, BarStyle(), options...) | |
| 99 | } | |
| 100 | ||
| 101 | // AddSpinner creates a bar with default spinner filler. | |
| 102 | func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar { | |
| 103 | return p.New(total, SpinnerStyle(), options...) | |
| 104 | } | |
| 105 | ||
| 106 | // New creates a bar with provided BarFillerBuilder. | |
| 107 | func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar { | |
| 108 | return p.Add(total, builder.Build(), options...) | |
| 112 | 109 | } |
| 113 | 110 | |
| 114 | 111 | // Add creates a bar which renders itself by provided filler. |
| 116 | 113 | // Panics if *Progress instance is done, i.e. called after *Progress.Wait(). |
| 117 | 114 | func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar { |
| 118 | 115 | if filler == nil { |
| 119 | filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {}) | |
| 116 | filler = NopStyle().Build() | |
| 120 | 117 | } |
| 121 | 118 | p.bwg.Add(1) |
| 122 | 119 | result := make(chan *Bar) |
| 156 | 153 | } |
| 157 | 154 | } |
| 158 | 155 | |
| 159 | func (p *Progress) setBarPriority(b *Bar, priority int) { | |
| 156 | func (p *Progress) traverseBars(cb func(b *Bar) bool) { | |
| 157 | done := make(chan struct{}) | |
| 158 | select { | |
| 159 | case p.operateState <- func(s *pState) { | |
| 160 | for i := 0; i < s.bHeap.Len(); i++ { | |
| 161 | bar := s.bHeap[i] | |
| 162 | if !cb(bar) { | |
| 163 | break | |
| 164 | } | |
| 165 | } | |
| 166 | close(done) | |
| 167 | }: | |
| 168 | <-done | |
| 169 | case <-p.done: | |
| 170 | } | |
| 171 | } | |
| 172 | ||
| 173 | // UpdateBarPriority same as *Bar.SetPriority(int). | |
| 174 | func (p *Progress) UpdateBarPriority(b *Bar, priority int) { | |
| 160 | 175 | select { |
| 161 | 176 | case p.operateState <- func(s *pState) { |
| 162 | 177 | if b.index < 0 { |
| 169 | 184 | } |
| 170 | 185 | } |
| 171 | 186 | |
| 172 | // UpdateBarPriority same as *Bar.SetPriority(int). | |
| 173 | func (p *Progress) UpdateBarPriority(b *Bar, priority int) { | |
| 174 | p.setBarPriority(b, priority) | |
| 175 | } | |
| 176 | ||
| 177 | 187 | // BarCount returns bars count. |
| 178 | 188 | func (p *Progress) BarCount() int { |
| 179 | result := make(chan int, 1) | |
| 189 | result := make(chan int) | |
| 180 | 190 | select { |
| 181 | 191 | case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }: |
| 182 | 192 | return <-result |
| 218 | 228 | op(s) |
| 219 | 229 | case <-p.refreshCh: |
| 220 | 230 | if err := s.render(cw); err != nil { |
| 221 | p.dlogger.Println(err) | |
| 231 | if s.debugOut != nil { | |
| 232 | _, e := fmt.Fprintln(s.debugOut, err) | |
| 233 | if e != nil { | |
| 234 | panic(err) | |
| 235 | } | |
| 236 | } else { | |
| 237 | panic(err) | |
| 238 | } | |
| 222 | 239 | } |
| 223 | 240 | case <-s.shutdownNotifier: |
| 224 | if s.heapUpdated { | |
| 241 | for s.heapUpdated { | |
| 225 | 242 | if err := s.render(cw); err != nil { |
| 226 | p.dlogger.Println(err) | |
| 243 | if s.debugOut != nil { | |
| 244 | _, e := fmt.Fprintln(s.debugOut, err) | |
| 245 | if e != nil { | |
| 246 | panic(err) | |
| 247 | } | |
| 248 | } else { | |
| 249 | panic(err) | |
| 250 | } | |
| 227 | 251 | } |
| 228 | 252 | } |
| 229 | 253 | return |
| 290 | 314 | } |
| 291 | 315 | |
| 292 | 316 | func (s *pState) flush(cw *cwriter.Writer) error { |
| 293 | var lineCount int | |
| 294 | bm := make(map[*Bar]struct{}, s.bHeap.Len()) | |
| 317 | var totalLines int | |
| 318 | bm := make(map[*Bar]int, s.bHeap.Len()) | |
| 295 | 319 | for s.bHeap.Len() > 0 { |
| 296 | 320 | b := heap.Pop(&s.bHeap).(*Bar) |
| 297 | cw.ReadFrom(<-b.frameCh) | |
| 321 | frame := <-b.frameCh | |
| 322 | _, err := cw.ReadFrom(frame.reader) | |
| 323 | if err != nil { | |
| 324 | return err | |
| 325 | } | |
| 298 | 326 | if b.toShutdown { |
| 299 | 327 | if b.recoveredPanic != nil { |
| 300 | 328 | s.barShutdownQueue = append(s.barShutdownQueue, b) |
| 307 | 335 | }() |
| 308 | 336 | } |
| 309 | 337 | } |
| 310 | lineCount += b.extendedLines + 1 | |
| 311 | bm[b] = struct{}{} | |
| 338 | bm[b] = frame.lines | |
| 339 | totalLines += frame.lines | |
| 312 | 340 | } |
| 313 | 341 | |
| 314 | 342 | for _, b := range s.barShutdownQueue { |
| 319 | 347 | b.toDrop = true |
| 320 | 348 | } |
| 321 | 349 | if s.popCompleted && !b.noPop { |
| 322 | lineCount -= b.extendedLines + 1 | |
| 350 | totalLines -= bm[b] | |
| 323 | 351 | b.toDrop = true |
| 324 | 352 | } |
| 325 | 353 | if b.toDrop { |
| 334 | 362 | heap.Push(&s.bHeap, b) |
| 335 | 363 | } |
| 336 | 364 | |
| 337 | return cw.Flush(lineCount) | |
| 365 | return cw.Flush(totalLines) | |
| 338 | 366 | } |
| 339 | 367 | |
| 340 | 368 | func (s *pState) updateSyncMatrix() { |
| 385 | 413 | bs.priority = -(math.MaxInt32 - s.idCount) |
| 386 | 414 | } |
| 387 | 415 | |
| 388 | bs.bufP = bytes.NewBuffer(make([]byte, 0, 128)) | |
| 389 | bs.bufB = bytes.NewBuffer(make([]byte, 0, 256)) | |
| 390 | bs.bufA = bytes.NewBuffer(make([]byte, 0, 128)) | |
| 416 | for i := 0; i < len(bs.buffers); i++ { | |
| 417 | bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512)) | |
| 418 | } | |
| 391 | 419 | |
| 392 | 420 | return bs |
| 393 | 421 | } |
| 8 | 8 | "testing" |
| 9 | 9 | "time" |
| 10 | 10 | |
| 11 | "github.com/vbauerster/mpb/v6" | |
| 12 | "github.com/vbauerster/mpb/v6/decor" | |
| 11 | "github.com/vbauerster/mpb/v7" | |
| 12 | "github.com/vbauerster/mpb/v7/decor" | |
| 13 | 13 | ) |
| 14 | 14 | |
| 15 | 15 | func init() { |
| 68 | 68 | wg.Wait() |
| 69 | 69 | count := p.BarCount() |
| 70 | 70 | if count != 2 { |
| 71 | t.Errorf("BarCount want: %q, got: %q\n", 2, count) | |
| 71 | t.Errorf("BarCount want: %d, got: %d\n", 2, count) | |
| 72 | 72 | } |
| 73 | 73 | bars[1].Abort(true) |
| 74 | 74 | bars[2].Abort(true) |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | 78 | func TestWithContext(t *testing.T) { |
| 79 | shutdown := make(chan struct{}) | |
| 79 | 80 | ctx, cancel := context.WithCancel(context.Background()) |
| 80 | shutdown := make(chan struct{}) | |
| 81 | p := mpb.NewWithContext(ctx, | |
| 82 | mpb.WithOutput(ioutil.Discard), | |
| 83 | mpb.WithRefreshRate(50*time.Millisecond), | |
| 84 | mpb.WithShutdownNotifier(shutdown), | |
| 85 | ) | |
| 81 | p := mpb.NewWithContext(ctx, mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(ioutil.Discard)) | |
| 86 | 82 | |
| 87 | total := 10000 | |
| 88 | numBars := 3 | |
| 89 | bars := make([]*mpb.Bar, 0, numBars) | |
| 90 | for i := 0; i < numBars; i++ { | |
| 91 | bar := p.AddBar(int64(total)) | |
| 92 | bars = append(bars, bar) | |
| 93 | go func() { | |
| 94 | for !bar.Completed() { | |
| 95 | bar.Increment() | |
| 96 | time.Sleep(randomDuration(100 * time.Millisecond)) | |
| 97 | } | |
| 98 | }() | |
| 99 | } | |
| 83 | start := make(chan struct{}) | |
| 84 | done := make(chan struct{}) | |
| 85 | fail := make(chan struct{}) | |
| 86 | bar := p.AddBar(0) // never complete bar | |
| 87 | go func() { | |
| 88 | close(start) | |
| 89 | for !bar.Completed() { | |
| 90 | bar.Increment() | |
| 91 | time.Sleep(randomDuration(100 * time.Millisecond)) | |
| 92 | } | |
| 93 | close(done) | |
| 94 | }() | |
| 100 | 95 | |
| 101 | time.Sleep(50 * time.Millisecond) | |
| 96 | go func() { | |
| 97 | select { | |
| 98 | case <-done: | |
| 99 | p.Wait() | |
| 100 | case <-time.After(150 * time.Millisecond): | |
| 101 | close(fail) | |
| 102 | } | |
| 103 | }() | |
| 104 | ||
| 105 | <-start | |
| 102 | 106 | cancel() |
| 103 | ||
| 104 | p.Wait() | |
| 105 | 107 | select { |
| 106 | 108 | case <-shutdown: |
| 107 | case <-time.After(100 * time.Millisecond): | |
| 108 | t.Error("Progress didn't stop") | |
| 109 | case <-fail: | |
| 110 | t.Error("Progress didn't shutdown") | |
| 109 | 111 | } |
| 110 | 112 | } |
| 111 | 113 | |
| 126 | 128 | *mpb.MaxWidthDistributor = makeWrapper(*mpb.MaxWidthDistributor, start, end) |
| 127 | 129 | |
| 128 | 130 | total := 80 |
| 129 | numBars := 3 | |
| 131 | numBars := 6 | |
| 130 | 132 | p := mpb.New(mpb.WithOutput(ioutil.Discard)) |
| 131 | 133 | for i := 0; i < numBars; i++ { |
| 132 | 134 | bar := p.AddBar(int64(total), |
| 140 | 142 | rng := rand.New(rand.NewSource(time.Now().UnixNano())) |
| 141 | 143 | for i := 0; i < total; i++ { |
| 142 | 144 | start := time.Now() |
| 143 | if bar.ID() >= numBars-1 && i >= 42 { | |
| 144 | bar.Abort(true) | |
| 145 | if id := bar.ID(); id > 1 && i >= 42 { | |
| 146 | if id&1 == 1 { | |
| 147 | bar.Abort(true) | |
| 148 | } else { | |
| 149 | bar.Abort(false) | |
| 150 | } | |
| 145 | 151 | } |
| 146 | time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2) | |
| 147 | bar.Increment() | |
| 152 | time.Sleep((time.Duration(rng.Intn(10)+1) * (50 * time.Millisecond)) / 2) | |
| 153 | bar.IncrInt64(rand.Int63n(5) + 1) | |
| 148 | 154 | bar.DecoratorEwmaUpdate(time.Since(start)) |
| 149 | 155 | } |
| 150 | 156 | }() |
| 10 | 10 | bar *Bar |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | func (x *proxyReader) Read(p []byte) (int, error) { | |
| 13 | func (x proxyReader) Read(p []byte) (int, error) { | |
| 14 | 14 | n, err := x.ReadCloser.Read(p) |
| 15 | 15 | x.bar.IncrBy(n) |
| 16 | 16 | if err == io.EOF { |
| 17 | go x.bar.SetTotal(0, true) | |
| 17 | go x.bar.SetTotal(-1, true) | |
| 18 | 18 | } |
| 19 | 19 | return n, err |
| 20 | 20 | } |
| 21 | 21 | |
| 22 | 22 | type proxyWriterTo struct { |
| 23 | io.ReadCloser // *proxyReader | |
| 24 | wt io.WriterTo | |
| 25 | bar *Bar | |
| 23 | proxyReader | |
| 24 | wt io.WriterTo | |
| 26 | 25 | } |
| 27 | 26 | |
| 28 | func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) { | |
| 27 | func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) { | |
| 29 | 28 | n, err := x.wt.WriteTo(w) |
| 30 | 29 | x.bar.IncrInt64(n) |
| 31 | 30 | if err == io.EOF { |
| 32 | go x.bar.SetTotal(0, true) | |
| 31 | go x.bar.SetTotal(-1, true) | |
| 33 | 32 | } |
| 34 | 33 | return n, err |
| 35 | 34 | } |
| 36 | 35 | |
| 37 | 36 | type ewmaProxyReader struct { |
| 38 | io.ReadCloser // *proxyReader | |
| 39 | bar *Bar | |
| 40 | iT time.Time | |
| 37 | proxyReader | |
| 41 | 38 | } |
| 42 | 39 | |
| 43 | func (x *ewmaProxyReader) Read(p []byte) (int, error) { | |
| 44 | n, err := x.ReadCloser.Read(p) | |
| 40 | func (x ewmaProxyReader) Read(p []byte) (int, error) { | |
| 41 | start := time.Now() | |
| 42 | n, err := x.proxyReader.Read(p) | |
| 45 | 43 | if n > 0 { |
| 46 | x.bar.DecoratorEwmaUpdate(time.Since(x.iT)) | |
| 47 | x.iT = time.Now() | |
| 44 | x.bar.DecoratorEwmaUpdate(time.Since(start)) | |
| 48 | 45 | } |
| 49 | 46 | return n, err |
| 50 | 47 | } |
| 51 | 48 | |
| 52 | 49 | type ewmaProxyWriterTo struct { |
| 53 | io.ReadCloser // *ewmaProxyReader | |
| 54 | wt io.WriterTo // *proxyWriterTo | |
| 55 | bar *Bar | |
| 56 | iT time.Time | |
| 50 | ewmaProxyReader | |
| 51 | wt proxyWriterTo | |
| 57 | 52 | } |
| 58 | 53 | |
| 59 | func (x *ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) { | |
| 54 | func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) { | |
| 55 | start := time.Now() | |
| 60 | 56 | n, err := x.wt.WriteTo(w) |
| 61 | 57 | if n > 0 { |
| 62 | x.bar.DecoratorEwmaUpdate(time.Since(x.iT)) | |
| 63 | x.iT = time.Now() | |
| 58 | x.bar.DecoratorEwmaUpdate(time.Since(start)) | |
| 64 | 59 | } |
| 65 | 60 | return n, err |
| 66 | 61 | } |
| 67 | 62 | |
| 68 | func newProxyReader(r io.Reader, bar *Bar) io.ReadCloser { | |
| 69 | rc := toReadCloser(r) | |
| 70 | rc = &proxyReader{rc, bar} | |
| 71 | ||
| 72 | if wt, isWriterTo := r.(io.WriterTo); bar.hasEwmaDecorators { | |
| 73 | now := time.Now() | |
| 74 | rc = &ewmaProxyReader{rc, bar, now} | |
| 75 | if isWriterTo { | |
| 76 | rc = &ewmaProxyWriterTo{rc, wt, bar, now} | |
| 63 | func (b *Bar) newProxyReader(r io.Reader) (rc io.ReadCloser) { | |
| 64 | pr := proxyReader{toReadCloser(r), b} | |
| 65 | if wt, ok := r.(io.WriterTo); ok { | |
| 66 | pw := proxyWriterTo{pr, wt} | |
| 67 | if b.hasEwmaDecorators { | |
| 68 | rc = ewmaProxyWriterTo{ewmaProxyReader{pr}, pw} | |
| 69 | } else { | |
| 70 | rc = pw | |
| 77 | 71 | } |
| 78 | } else if isWriterTo { | |
| 79 | rc = &proxyWriterTo{rc, wt, bar} | |
| 72 | } else if b.hasEwmaDecorators { | |
| 73 | rc = ewmaProxyReader{pr} | |
| 74 | } else { | |
| 75 | rc = pr | |
| 80 | 76 | } |
| 81 | 77 | return rc |
| 82 | 78 | } |
| 6 | 6 | "strings" |
| 7 | 7 | "testing" |
| 8 | 8 | |
| 9 | "github.com/vbauerster/mpb/v6" | |
| 9 | "github.com/vbauerster/mpb/v7" | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 12 | const content = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do |
| 66 | 66 | p := mpb.New(mpb.WithOutput(ioutil.Discard)) |
| 67 | 67 | |
| 68 | 68 | var reader io.Reader = strings.NewReader(content) |
| 69 | wt := reader.(io.WriterTo) | |
| 70 | tReader := &testWriterTo{reader, wt, false} | |
| 69 | tReader := &testWriterTo{reader, reader.(io.WriterTo), false} | |
| 71 | 70 | |
| 72 | 71 | bar := p.AddBar(int64(len(content)), mpb.BarFillerTrim()) |
| 73 | 72 | |