Codebase list golang-github-vbauerster-mpb / 9ca8331
New upstream version 7.3.2 Andreas Tille 4 years ago
83 changed file(s) with 2436 addition(s) and 841 deletion(s). Raw diff Collapse all Expand all
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
-11
.travis.yml less more
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
00 # Multi Progress Bar
11
2 [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v6)
3 [![Build Status](https://travis-ci.org/vbauerster/mpb.svg?branch=master)](https://travis-ci.org/vbauerster/mpb)
4 [![Go Report Card](https://goreportcard.com/badge/github.com/vbauerster/mpb)](https://goreportcard.com/report/github.com/vbauerster/mpb)
2 [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
3 [![Test status](https://github.com/vbauerster/mpb/actions/workflows/test.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
4 [![Donate with PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/vbauerster)
55
66 **mpb** is a Go lib for rendering progress bars in terminal applications.
77
2525 "math/rand"
2626 "time"
2727
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"
3030 )
3131
3232 func main() {
3535
3636 total := 100
3737 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("╟"),
4242 mpb.PrependDecorators(
4343 // display our name with one space on the right
4444 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
6464
6565 ```go
6666 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
6868 p := mpb.New(mpb.WithWaitGroup(&wg))
6969 total, numBars := 100, 3
7070 wg.Add(numBars)
8282 // replace ETA decorator with "done" message, OnComplete event
8383 decor.OnComplete(
8484 // 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",
8686 ),
8787 ),
8888 )
102102 }
103103 }()
104104 }
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
106106 p.Wait()
107107 ```
108108
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
66 "sync"
77 "time"
88
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"
1111 )
1212
1313 func main() {
1414 var wg sync.WaitGroup
15 // passed wg will be accounted at p.Wait() call
1516 p := mpb.New(mpb.WithWaitGroup(&wg))
1617 total, numBars := 100, 3
1718 wg.Add(numBars)
5455 }
5556 }()
5657 }
57 // wait for all bars to complete and flush
58 // wait for passed wg and for all bars to complete and flush
5859 p.Wait()
5960 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
66 "sync"
77 "time"
88
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"
1111 )
1212
1313 func main() {
1515 defer cancel()
1616
1717 var wg sync.WaitGroup
18 // passed wg will be accounted at p.Wait() call
1819 p := mpb.NewWithContext(ctx, mpb.WithWaitGroup(&wg))
1920 total := 300
2021 numBars := 3
4849 }
4950 }()
5051 }
51
52 // wait for passed wg and for all bars to complete and flush
5253 p.Wait()
5354 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 func init() {
1515
1616 func main() {
1717 doneWg := new(sync.WaitGroup)
18 // passed doneWg will be accounted at p.Wait() call
1819 p := mpb.New(mpb.WithWaitGroup(doneWg))
1920 numBars := 4
2021
6364 go newTask(doneWg, b, numBars-i)
6465 }()
6566 }
66
67 // wait for passed doneWg and for all bars to complete and flush
6768 p.Wait()
6869 }
6970
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
44 "math/rand"
55 "time"
66
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"
99 )
1010
1111 func main() {
1212 p := mpb.New()
1313
1414 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
1618 mpb.PrependDecorators(
1719 decor.Name("Percentage: "),
1820 decor.NewPercentage("%d"),
2325 decor.AverageETA(decor.ET_STYLE_GO), "done",
2426 ),
2527 ),
26 mpb.BarExtender(nlBarFiller(mpb.NewBarFiller("╢▌▌░╟"))),
2728 )
2829 // simulating some work
2930 max := 100 * time.Millisecond
3536 p.Wait()
3637 }
3738
38 func nlBarFiller(filler mpb.BarFiller) mpb.BarFiller {
39 func extended(builder mpb.BarFillerBuilder) mpb.BarFiller {
40 filler := builder.Build()
3941 return mpb.BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
4042 filler.Fill(w, reqWidth, st)
4143 w.Write([]byte("\n"))
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // passed wg will be accounted at p.Wait() call
1415 p := mpb.New(
1516 mpb.WithWaitGroup(&wg),
16 // container's width.
1717 mpb.WithWidth(60),
1818 )
1919 total, numBars := 100, 3
5454 }
5555 }()
5656 }
57 // wait for all bars to complete and flush
57 // wait for passed wg and for all bars to complete and flush
5858 p.Wait()
5959 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
44 "math/rand"
55 "time"
66
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"
99 )
1010
1111 func init() {
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "io/ioutil"
66 "time"
77
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"
1010 )
1111
1212 func main() {
1818 mpb.WithRefreshRate(180*time.Millisecond),
1919 )
2020
21 bar := p.Add(total,
22 mpb.NewBarFiller("[=>-|"),
21 bar := p.New(total,
22 mpb.BarStyle().Rbound("|"),
2323 mpb.PrependDecorators(
2424 decor.CountersKibiByte("% .2f / % .2f"),
2525 ),
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 func main() {
1313 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
1515 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(60))
1616 total, numBars := 100, 3
1717 wg.Add(numBars)
5858 }
5959 }()
6060 }
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
6262 p.Wait()
6363 }
6464
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 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 func main() {
1313 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
1515 p := mpb.New(mpb.WithWaitGroup(&wg))
1616 total, numBars := 100, 3
1717 wg.Add(numBars)
2929 // replace ETA decorator with "done" message, OnComplete event
3030 decor.OnComplete(
3131 // 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",
3333 ),
3434 ),
3535 )
4949 }
5050 }()
5151 }
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
5353 p.Wait()
5454 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
66 "sync"
77 "time"
88
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"
1111 )
1212
1313 func main() {
1414 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 )
1620
1721 wantPanic := strings.Repeat("Panic ", 64)
1822 numBars := 3
3034 }
3135 }()
3236 }
33
37 // wait for passed wg and for all bars to complete and flush
3438 p.Wait()
3539 }
3640
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
44 "math/rand"
55 "time"
66
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"
99 )
1010
1111 func main() {
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
66 "sync"
77 "time"
88
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"
1111 )
1212
1313 var quietMode bool
1919 func main() {
2020 flag.Parse()
2121 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
2323 p := mpb.New(
2424 mpb.WithWaitGroup(&wg),
2525 mpb.ContainerOptional(
6767 }
6868 }()
6969 }
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
7171 p.Wait()
7272 fmt.Println("done")
7373 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // passed wg will be accounted at p.Wait() call
1415 p := mpb.New(mpb.WithWaitGroup(&wg))
1516 total := 100
1617 numBars := 3
2324 mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0),
2425 mpb.PrependDecorators(
2526 decor.Name(name),
26 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace),
2727 ),
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 ),
2938 )
3039 go func() {
3140 defer wg.Done()
3241 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
3342 max := 100 * time.Millisecond
3443 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()
3844 if bar.ID() == 2 && i >= 42 {
39 // aborting and removing while bar is running
40 bar.Abort(true)
45 bar.Abort(false)
4146 }
4247 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
4348 bar.Increment()
44 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
45 bar.DecoratorEwmaUpdate(time.Since(start))
4649 }
4750 }()
4851 }
49
52 // wait for passed wg and for all bars to complete and flush
5053 p.Wait()
5154 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 func main() {
1313 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
1515 p := mpb.New(mpb.WithWaitGroup(&wg))
1616 total, numBars := 100, 3
1717 wg.Add(numBars)
1818
1919 for i := 0; i < numBars; i++ {
2020 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),
2422 mpb.PrependDecorators(
2523 // simple name decorator
2624 decor.Name(name),
5149 }
5250 }()
5351 }
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
5553 p.Wait()
5654 }
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 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
33 "math/rand"
44 "time"
55
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"
88 )
99
1010 func main() {
1313
1414 total := 100
1515 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("╟"),
2020 mpb.PrependDecorators(
2121 // display our name with one space on the right
2222 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 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // passed wg will be accounted at p.Wait() call
1415 p := mpb.New(
1516 mpb.WithWaitGroup(&wg),
16 mpb.WithWidth(14),
17 mpb.WithWidth(16),
1718 )
1819 total, numBars := 101, 3
1920 wg.Add(numBars)
2021
21 spinnerStyle := []string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"}
22
2322 for i := 0; i < numBars; i++ {
2423 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",
3234 ),
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 )
5837 // simulating some work
5938 go func() {
6039 defer wg.Done()
7150 }
7251 }()
7352 }
74 // wait for all bars to complete and flush
53 // wait for passed wg and for all bars to complete and flush
7554 p.Wait()
7655 }
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 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 func main() {
1313 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
1515 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(64))
1616 total, numBars := 100, 3
1717 wg.Add(numBars)
4343 }
4444 }()
4545 }
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
4747 p.Wait()
4848 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
55 "sync"
66 "time"
77
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"
1010 )
1111
1212 const (
1515
1616 func main() {
1717 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))
2220 wg.Add(totalBars)
2321
2422 for i := 0; i < totalBars; i++ {
4644 }
4745 }()
4846 }
49
47 // wait for passed wg and for all bars to complete and flush
5048 p.Wait()
5149 }
22 go 1.14
33
44 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
77 )
88 "time"
99
1010 "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"
1313 )
1414
1515 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 }
+201
-124
bar.go less more
44 "context"
55 "fmt"
66 "io"
7 "log"
87 "runtime/debug"
98 "strings"
9 "sync"
1010 "time"
1111
1212 "github.com/acarl005/stripansi"
1313 "github.com/mattn/go-runewidth"
14 "github.com/vbauerster/mpb/v6/decor"
14 "github.com/vbauerster/mpb/v7/decor"
1515 )
1616
1717 // Bar represents a progress bar.
1919 priority int // used by heap
2020 index int // used by heap
2121
22 extendedLines int
2322 toShutdown bool
2423 toDrop bool
2524 noPop bool
2625 hasEwmaDecorators bool
2726 operateState chan func(*bState)
28 frameCh chan io.Reader
29 syncTableCh chan [][]chan int
30 completed chan bool
27 frameCh chan *frame
3128
3229 // cancel is called either by user or on complete event
3330 cancel func()
3431 // done is closed after cacheState is assigned
3532 done chan struct{}
36 // cacheState is populated, right after close(shutdown)
33 // cacheState is populated, right after close(b.done)
3734 cacheState *bState
3835
3936 container *Progress
40 dlogger *log.Logger
4137 recoveredPanic interface{}
4238 }
4339
5248 total int64
5349 current int64
5450 refill int64
55 lastN int64
56 iterated bool
51 lastIncrement int64
5752 trimSpace bool
5853 completed bool
5954 completeFlushed bool
55 aborted bool
6056 triggerComplete bool
6157 dropOnComplete bool
6258 noPop bool
6561 averageDecorators []decor.AverageDecorator
6662 ewmaDecorators []decor.EwmaDecorator
6763 shutdownListeners []decor.ShutdownListener
68 bufP, bufB, bufA *bytes.Buffer
64 buffers [3]*bytes.Buffer
6965 filler BarFiller
7066 middleware func(BarFiller) BarFiller
7167 extender extenderFunc
7672 debugOut io.Writer
7773 }
7874
75 type frame struct {
76 reader io.Reader
77 lines int
78 }
79
7980 func newBar(container *Progress, bs *bState) *Bar {
80 logPrefix := fmt.Sprintf("%sbar#%02d ", container.dlogger.Prefix(), bs.id)
8181 ctx, cancel := context.WithCancel(container.ctx)
8282
8383 bar := &Bar{
8686 toDrop: bs.dropOnComplete,
8787 noPop: bs.noPop,
8888 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),
9290 done: make(chan struct{}),
9391 cancel: cancel,
94 dlogger: log.New(bs.debugOut, logPrefix, log.Lshortfile),
9592 }
9693
9794 go bar.serve(ctx, bs)
104101 if r == nil {
105102 panic("expected non nil io.Reader")
106103 }
107 return newProxyReader(r, b)
104 return b.newProxyReader(r)
108105 }
109106
110107 // ID returs id of the bar.
144141
145142 // TraverseDecorators traverses all available decorators and calls cb func on each.
146143 func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
144 done := make(chan struct{})
147145 select {
148146 case b.operateState <- func(s *bState) {
149147 for _, decorators := range [...][]decor.Decorator{
154152 cb(extractBaseDecorator(d))
155153 }
156154 }
157 }:
155 close(done)
156 }:
157 <-done
158158 case <-b.done:
159159 }
160160 }
161161
162162 // 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.
164164 func (b *Bar) SetTotal(total int64, triggerComplete bool) {
165165 select {
166166 case b.operateState <- func(s *bState) {
167167 s.triggerComplete = triggerComplete
168 if total <= 0 {
168 if total < 0 {
169169 s.total = s.current
170170 } else {
171171 s.total = total
173173 if s.triggerComplete && !s.completed {
174174 s.current = s.total
175175 s.completed = true
176 go b.refreshTillShutdown()
176 go b.forceRefreshIfLastUncompleted()
177177 }
178178 }:
179179 case <-b.done:
185185 func (b *Bar) SetCurrent(current int64) {
186186 select {
187187 case b.operateState <- func(s *bState) {
188 s.iterated = true
189 s.lastN = current - s.current
188 s.lastIncrement = current - s.current
190189 s.current = current
191190 if s.triggerComplete && s.current >= s.total {
192191 s.current = s.total
193192 s.completed = true
194 go b.refreshTillShutdown()
193 go b.forceRefreshIfLastUncompleted()
195194 }
196195 }:
197196 case <-b.done:
210209
211210 // IncrInt64 increments progress by amount of n.
212211 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
217218 s.current += n
218219 if s.triggerComplete && s.current >= s.total {
219220 s.current = s.total
220221 s.completed = true
221 go b.refreshTillShutdown()
222 go b.forceRefreshIfLastUncompleted()
222223 }
223224 }:
224225 case <-b.done:
232233 func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
233234 select {
234235 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 }
239248 }
240249 }
241250
245254 func (b *Bar) DecoratorAverageAdjust(start time.Time) {
246255 select {
247256 case b.operateState <- func(s *bState) {
248 for _, d := range s.averageDecorators {
249 d.AverageAdjust(start)
250 }
257 s.decoratorAverageAdjust(start)
251258 }:
252259 case <-b.done:
253260 }
257264 // priority, i.e. bar will be on top. If you don't need to set priority
258265 // dynamically, better use BarPriority option.
259266 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.
270273 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
277282 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:
278307 }
279308 }
280309
281310 // Completed reports whether the bar is in completed state.
282311 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
286316 case <-b.done:
287317 return true
288318 }
295325 case op := <-b.operateState:
296326 op(s)
297327 case <-ctx.Done():
328 s.decoratorShutdownNotify()
298329 b.cacheState = s
299330 close(b.done)
300 // Notifying decorators about shutdown event
301 for _, sl := range s.shutdownListeners {
302 sl.Shutdown()
303 }
304331 return
305332 }
306333 }
314341 // recovering if user defined decorator panics for example
315342 if p := recover(); p != nil {
316343 if b.recoveredPanic == nil {
344 if s.debugOut != nil {
345 fmt.Fprintln(s.debugOut, p)
346 _, _ = s.debugOut.Write(debug.Stack())
347 }
317348 s.extender = makePanicExtender(p)
318349 b.toShutdown = !b.toShutdown
319350 b.recoveredPanic = p
320351 }
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}
325354 }
326355 s.completeFlushed = s.completed
327356 }()
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)
330358 b.toShutdown = s.completed && !s.completeFlushed
331 b.frameCh <- frame
359 b.frameCh <- &frame{reader, lines + 1}
332360 }:
333361 case <-b.done:
334362 s := b.cacheState
337365 if b.recoveredPanic == nil {
338366 r = s.draw(stat)
339367 }
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}
343370 }
344371 }
345372
358385 shutdownListeners = append(shutdownListeners, d)
359386 }
360387 })
388 b.hasEwmaDecorators = len(ewmaDecorators) != 0
361389 select {
362390 case b.operateState <- func(s *bState) {
363391 s.averageDecorators = averageDecorators
364392 s.ewmaDecorators = ewmaDecorators
365393 s.shutdownListeners = shutdownListeners
366394 }:
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 }
378415 }
379416 }
380417 }
381418
382419 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
386424 case <-b.done:
387425 return b.cacheState.wSyncTable()
388426 }
389427 }
390428
391429 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]
398431 nlr := strings.NewReader("\n")
399432 tw := stat.AvailableWidth
400433 for _, d := range s.pDecorators {
401434 str := d.Decor(stat)
402435 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(' ')
409448 }
410449
411450 tw = stat.AvailableWidth
412451 for _, d := range s.aDecorators {
413452 str := d.Decor(stat)
414453 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)
426465 }
427466
428467 func (s *bState) wSyncTable() [][]chan int {
445484 table[0] = columns[0:pCount]
446485 table[1] = columns[pCount : pCount+aCount : pCount+aCount]
447486 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()
448538 }
449539
450540 func newStatistics(tw int, s *bState) decor.Statistics {
455545 Current: s.current,
456546 Refill: s.refill,
457547 Completed: s.completeFlushed,
548 Aborted: s.aborted,
458549 }
459550 }
460551
465556 return d
466557 }
467558
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
479559 func makePanicExtender(p interface{}) extenderFunc {
480560 pstr := fmt.Sprint(p)
481 stack := debug.Stack()
482 stackLines := bytes.Count(stack, []byte("\n"))
483561 return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) {
484562 mr := io.MultiReader(
485563 strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")),
486 strings.NewReader(fmt.Sprintf("\n%#v\n", st)),
487 bytes.NewReader(stack),
564 strings.NewReader("\n"),
488565 )
489 return mr, stackLines + 1
490 }
491 }
566 return mr, 0
567 }
568 }
22 import (
33 "io"
44
5 "github.com/vbauerster/mpb/v6/decor"
5 "github.com/vbauerster/mpb/v7/decor"
66 )
77
88 // BarFiller interface.
99 // Bar (without decorators) renders itself by calling BarFiller's Fill method.
1010 //
11 // reqWidth is requested width, set by `func WithWidth(int) ContainerOption`.
11 // reqWidth is requested width set by `func WithWidth(int) ContainerOption`.
1212 // 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
2013 //
2114 type BarFiller interface {
2215 Fill(w io.Writer, reqWidth int, stat decor.Statistics)
2316 }
2417
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.
2631 type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
2732
2833 func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
2934 f(w, reqWidth, stat)
3035 }
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 }
00 package mpb
11
22 import (
3 "bytes"
43 "io"
5 "unicode/utf8"
6
4
5 "github.com/acarl005/stripansi"
76 "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"
119 )
1210
1311 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
2118 )
2219
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 }
88146 }
89147 return bf
90148 }
91149
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
120153 // don't count brackets as progress
121154 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++
140184 }
141185
142186 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 }
33 "io"
44 "strings"
55
6 "github.com/acarl005/stripansi"
67 "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"
910 )
1011
11 // SpinnerAlignment enum.
12 type SpinnerAlignment int
13
14 // SpinnerAlignment kinds.
1512 const (
16 SpinnerOnLeft SpinnerAlignment = iota
17 SpinnerOnMiddle
18 SpinnerOnRight
13 positionLeft = 1 + iota
14 positionRight
1915 )
2016
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
2822 }
2923
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
4228 }
4329
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)
4667
4768 frame := s.frames[s.count%uint(len(s.frames))]
48 frameWidth := runewidth.StringWidth(frame)
69 frameWidth := runewidth.StringWidth(stripansi.Strip(frame))
4970
5071 if width < frameWidth {
5172 return
5273 }
5374
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:
5883 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)
6288 }
6389 s.count++
6490 }
33 "bytes"
44 "io"
55
6 "github.com/vbauerster/mpb/v6/decor"
7 "github.com/vbauerster/mpb/v6/internal"
6 "github.com/vbauerster/mpb/v7/decor"
87 )
98
109 // BarOption is a func option to alter default behavior of a bar.
1110 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 }
1220
1321 func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
1422 type mergeWrapper interface {
2533 // AppendDecorators let you inject decorators to the bar's right side.
2634 func AppendDecorators(decorators ...decor.Decorator) BarOption {
2735 return func(s *bState) {
28 s.addDecorators(&s.aDecorators, decorators...)
36 s.addDecorators(&s.aDecorators, skipNil(decorators)...)
2937 }
3038 }
3139
3240 // PrependDecorators let you inject decorators to the bar's left side.
3341 func PrependDecorators(decorators ...decor.Decorator) BarOption {
3442 return func(s *bState) {
35 s.addDecorators(&s.pDecorators, decorators...)
43 s.addDecorators(&s.pDecorators, skipNil(decorators)...)
3644 }
3745 }
3846
8088 return BarFillerMiddleware(func(base BarFiller) BarFiller {
8189 return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
8290 if st.Completed {
83 io.WriteString(w, message)
91 _, err := io.WriteString(w, message)
92 if err != nil {
93 panic(err)
94 }
8495 } else {
8596 base.Fill(w, reqWidth, st)
8697 }
137148 }
138149 }
139150
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
143157 }
144158
145159 // BarOptOn will invoke provided option only when higher order predicate
99 "time"
1010 "unicode/utf8"
1111
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"
1414 )
1515
1616 func TestBarCompleted(t *testing.T) {
6060
6161 total := 100
6262 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())
6666
6767 bar.SetRefill(int64(till))
6868 bar.IncrBy(till)
7575 p.Wait()
7676
7777 wantBar := fmt.Sprintf("[%s%s]",
78 strings.Repeat(string(refillRune), till-1),
78 strings.Repeat(refiller, till-1),
7979 strings.Repeat("=", total-till-1),
8080 )
8181
142142 func TestBarStyle(t *testing.T) {
143143 var buf bytes.Buffer
144144 customFormat := "╢▌▌░╟"
145 runes := []rune(customFormat)
145146 total := 80
146147 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",
158164 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]),
161168 )
162169 got := string(getLastLine(buf.Bytes()))
163170
246253 }
247254 }
248255
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
249310 func panicDecorator(panicMsg string, cond func(decor.Statistics) bool) decor.Decorator {
250311 return decor.Any(func(st decor.Statistics) string {
251312 if cond(st) {
00 package mpb
11
22 import (
3 "io/ioutil"
3 "sync"
44 "testing"
5
6 "github.com/vbauerster/mpb/v6/decor"
75 )
86
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)
1511 }
1612
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)
2315 }
2416
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)
3119 }
3220
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()
3929 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()
4155 }
56 p.Wait()
4257 }
44 "io/ioutil"
55 "sync"
66 "time"
7
8 "github.com/vbauerster/mpb/v6/internal"
97 )
108
119 // ContainerOption is a func option to alter default behavior of a bar
6159 // have been rendered.
6260 func WithShutdownNotifier(ch chan struct{}) ContainerOption {
6361 return func(s *pState) {
64 s.shutdownNotifier = ch
62 select {
63 case <-ch:
64 default:
65 s.shutdownNotifier = ch
66 }
6567 }
6668 }
6769
9698 }
9799 }
98100
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
102107 }
103108
104109 // ContainerOptOn will invoke provided option only when higher order
1717 func BenchmarkWithJoin(b *testing.B) {
1818 bCuuAndEd := [][]byte{[]byte("\x1b["), []byte("A\x1b[J")}
1919 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))))
2121 }
2222 }
2323
2525 escOpen := []byte("\x1b[")
2626 cuuAndEd := []byte("A\x1b[J")
2727 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...))
2929 }
3030 }
3131
3232 func BenchmarkWithCopy(b *testing.B) {
3333 w := New(ioutil.Discard)
34 w.lineCount = 4
34 w.lines = 4
3535 for i := 0; i < b.N; i++ {
36 w.ansiCuuAndEd()
36 _ = w.ansiCuuAndEd()
3737 }
3838 }
0 // +build zos
1
2 package cwriter
3
4 import "golang.org/x/sys/unix"
5
6 const ioctlReadTermios = unix.TCGETS
2121 type Writer struct {
2222 out io.Writer
2323 buf bytes.Buffer
24 lineCount int
24 lines int
2525 fd int
2626 isTerminal bool
2727 }
3737 }
3838
3939 // Flush flushes the underlying buffer.
40 func (w *Writer) Flush(lineCount int) (err error) {
40 func (w *Writer) Flush(lines int) (err error) {
4141 // some terminals interpret 'cursor up 0' as 'cursor up 1'
42 if w.lineCount > 0 {
42 if w.lines > 0 {
4343 err = w.clearLines()
4444 if err != nil {
4545 return
4646 }
4747 }
48 w.lineCount = lineCount
48 w.lines = lines
4949 _, err = w.buf.WriteTo(w.out)
5050 return
5151 }
7575 return tw, err
7676 }
7777
78 func (w *Writer) ansiCuuAndEd() (err error) {
78 func (w *Writer) ansiCuuAndEd() error {
7979 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
8383 }
2525 return err
2626 }
2727
28 info.CursorPosition.Y -= int16(w.lineCount)
28 info.CursorPosition.Y -= int16(w.lines)
2929 if info.CursorPosition.Y < 0 {
3030 info.CursorPosition.Y = 0
3131 }
3939 X: info.Window.Left,
4040 Y: info.CursorPosition.Y,
4141 }
42 count := uint32(info.Size.X) * uint32(w.lineCount)
42 count := uint32(info.Size.X) * uint32(w.lines)
4343 _, _, _ = procFillConsoleOutputCharacter.Call(
4444 uintptr(w.fd),
4545 uintptr(' '),
5252 Current int64
5353 Refill int64
5454 Completed bool
55 Aborted bool
5556 }
5657
5758 // 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")))
1918 package decor
1616 // +----+--------+---------+--------+
1717 //
1818 func Merge(decorator Decorator, placeholders ...WC) Decorator {
19 if decorator == nil {
20 return nil
21 }
1922 if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 {
2023 return decorator
2124 }
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 }
00 package decor
11
2 // OnComplete returns decorator, which wraps provided decorator, with
2 // OnComplete returns decorator, which wraps provided decorator with
33 // sole purpose to display provided message on complete event.
44 //
55 // `decorator` Decorator to wrap
77 // `message` message to display on complete event
88 //
99 func OnComplete(decorator Decorator, message string) Decorator {
10 if decorator == nil {
11 return nil
12 }
1013 d := &onCompleteWrapper{
1114 Decorator: decorator,
1215 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 }
11
22 import (
33 "fmt"
4 "io"
54 "strconv"
65
7 "github.com/vbauerster/mpb/v6/internal"
6 "github.com/vbauerster/mpb/v7/internal"
87 )
98
109 type percentageType float64
2322 }
2423 }
2524
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))
2827 if st.Flag(' ') {
29 io.WriteString(st, " ")
28 osw(" ")
3029 }
31 io.WriteString(st, "%")
30 osw("%")
3231 }
3332
3433 // Percentage returns percentage decorator. It's a wrapper of NewPercentage.
11
22 import (
33 "fmt"
4 "io"
5 "math"
64 "strconv"
75 )
86
4644 unit = _iMiB
4745 case self < _iTiB:
4846 unit = _iGiB
49 case self <= math.MaxInt64:
47 default:
5048 unit = _iTiB
5149 }
5250
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))
5553 if st.Flag(' ') {
56 io.WriteString(st, " ")
54 osw(" ")
5755 }
58 io.WriteString(st, unit.String())
56 osw(unit.String())
5957 }
6058
6159 const (
9593 unit = _MB
9694 case self < _TB:
9795 unit = _GB
98 case self <= math.MaxInt64:
96 default:
9997 unit = _TB
10098 }
10199
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))
104102 if st.Flag(' ') {
105 io.WriteString(st, " ")
103 osw(" ")
106104 }
107 io.WriteString(st, unit.String())
105 osw(unit.String())
108106 }
11
22 import (
33 "fmt"
4 "io"
54 "math"
65 "time"
76
2322
2423 func (self *speedFormatter) Format(st fmt.State, verb rune) {
2524 self.Formatter.Format(st, verb)
26 io.WriteString(st, "/s")
25 optimisticStringWriter(st)("/s")
2726 }
2827
2928 // EwmaSpeed exponential-weighted-moving-average based speed decorator.
33 "sync"
44 "testing"
55
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"
88 )
99
1010 func TestNameDecorator(t *testing.T) {
55 "unicode/utf8"
66 )
77
8 func TestDraw(t *testing.T) {
8 func TestDrawDefault(t *testing.T) {
99 // key is termWidth
1010 testSuite := map[int][]struct {
11 style BarStyleComposer
1112 name string
12 style string
1313 total int64
1414 current int64
1515 refill int64
1616 barWidth int
1717 trim bool
18 reverse bool
1918 want string
2019 }{
2120 0: {
2221 {
22 style: BarStyle(),
2323 name: "t,c{60,20}",
2424 total: 60,
2525 current: 20,
26 want: "… ",
27 },
28 {
26 want: "",
27 },
28 {
29 style: BarStyle(),
2930 name: "t,c{60,20}trim",
3031 total: 60,
3132 current: 20,
3536 },
3637 1: {
3738 {
39 style: BarStyle(),
3840 name: "t,c{60,20}",
3941 total: 60,
4042 current: 20,
41 want: "… ",
42 },
43 {
43 want: "",
44 },
45 {
46 style: BarStyle(),
4447 name: "t,c{60,20}trim",
4548 total: 60,
4649 current: 20,
5053 },
5154 2: {
5255 {
56 style: BarStyle(),
5357 name: "t,c{60,20}",
5458 total: 60,
5559 current: 20,
5660 want: " ",
5761 },
5862 {
63 style: BarStyle(),
5964 name: "t,c{60,20}trim",
6065 total: 60,
6166 current: 20,
6570 },
6671 3: {
6772 {
73 style: BarStyle(),
6874 name: "t,c{60,20}",
6975 total: 60,
7076 current: 20,
7177 want: " ",
7278 },
7379 {
80 style: BarStyle(),
7481 name: "t,c{60,20}trim",
7582 total: 60,
7683 current: 20,
7784 trim: true,
7885 want: "[-]",
7986 },
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 },
80117 },
81118 4: {
82119 {
120 style: BarStyle(),
83121 name: "t,c{60,20}",
84122 total: 60,
85123 current: 20,
86124 want: " [] ",
87125 },
88126 {
127 style: BarStyle(),
89128 name: "t,c{60,20}trim",
90129 total: 60,
91130 current: 20,
92131 trim: true,
93132 want: "[>-]",
94133 },
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 },
95164 },
96165 5: {
97166 {
167 style: BarStyle(),
98168 name: "t,c{60,20}",
99169 total: 60,
100170 current: 20,
101171 want: " [-] ",
102172 },
103173 {
174 style: BarStyle(),
104175 name: "t,c{60,20}trim",
105176 total: 60,
106177 current: 20,
107178 trim: true,
108179 want: "[>--]",
109180 },
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 },
110211 },
111212 6: {
112213 {
214 style: BarStyle(),
113215 name: "t,c{60,20}",
114216 total: 60,
115217 current: 20,
116218 want: " [>-] ",
117219 },
118220 {
221 style: BarStyle(),
119222 name: "t,c{60,20}trim",
120223 total: 60,
121224 current: 20,
122225 trim: true,
123226 want: "[>---]",
124227 },
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 },
125258 },
126259 7: {
127260 {
261 style: BarStyle(),
128262 name: "t,c{60,20}",
129263 total: 60,
130264 current: 20,
131265 want: " [>--] ",
132266 },
133267 {
268 style: BarStyle(),
134269 name: "t,c{60,20}trim",
135270 total: 60,
136271 current: 20,
137272 trim: true,
138273 want: "[=>---]",
139274 },
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 },
140305 },
141306 8: {
142307 {
308 style: BarStyle(),
143309 name: "t,c{60,20}",
144310 total: 60,
145311 current: 20,
146312 want: " [>---] ",
147313 },
148314 {
315 style: BarStyle(),
149316 name: "t,c{60,20}trim",
150317 total: 60,
151318 current: 20,
152319 trim: true,
153320 want: "[=>----]",
154321 },
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 },
155352 },
156353 80: {
157354 {
355 style: BarStyle(),
158356 name: "t,c{60,20}",
159357 total: 60,
160358 current: 20,
161359 want: " [========================>---------------------------------------------------] ",
162360 },
163361 {
362 style: BarStyle(),
164363 name: "t,c{60,20}trim",
165364 total: 60,
166365 current: 20,
168367 want: "[=========================>----------------------------------------------------]",
169368 },
170369 {
370 style: BarStyle(),
171371 name: "t,c,bw{60,20,60}",
172372 total: 60,
173373 current: 20,
175375 want: " [==================>---------------------------------------] ",
176376 },
177377 {
378 style: BarStyle(),
178379 name: "t,c,bw{60,20,60}trim",
179380 total: 60,
180381 current: 20,
182383 trim: true,
183384 want: "[==================>---------------------------------------]",
184385 },
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 },
185599 },
186600 100: {
187601 {
602 style: BarStyle(),
188603 name: "t,c{100,0}",
189604 total: 100,
190605 current: 0,
191606 want: " [------------------------------------------------------------------------------------------------] ",
192607 },
193608 {
609 style: BarStyle(),
194610 name: "t,c{100,0}trim",
195611 total: 100,
196612 current: 0,
198614 want: "[--------------------------------------------------------------------------------------------------]",
199615 },
200616 {
617 style: BarStyle(),
201618 name: "t,c{100,1}",
202619 total: 100,
203620 current: 1,
204621 want: " [>-----------------------------------------------------------------------------------------------] ",
205622 },
206623 {
624 style: BarStyle(),
207625 name: "t,c{100,1}trim",
208626 total: 100,
209627 current: 1,
211629 want: "[>-------------------------------------------------------------------------------------------------]",
212630 },
213631 {
632 style: BarStyle(),
214633 name: "t,c{100,99}",
215634 total: 100,
216635 current: 99,
217636 want: " [==============================================================================================>-] ",
218637 },
219638 {
639 style: BarStyle(),
220640 name: "t,c{100,99}trim",
221641 total: 100,
222642 current: 99,
224644 want: "[================================================================================================>-]",
225645 },
226646 {
647 style: BarStyle(),
227648 name: "t,c{100,100}",
228649 total: 100,
229650 current: 100,
230651 want: " [================================================================================================] ",
231652 },
232653 {
654 style: BarStyle(),
233655 name: "t,c{100,100}trim",
234656 total: 100,
235657 current: 100,
237659 want: "[==================================================================================================]",
238660 },
239661 {
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(),
240688 name: "t,c,r{100,100,100}trim",
241689 total: 100,
242690 current: 100,
245693 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
246694 },
247695 {
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(),
293731 name: "t,c,r{100,40,33}",
294732 total: 100,
295733 current: 40,
297735 want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ",
298736 },
299737 {
738 style: BarStyle(),
300739 name: "t,c,r{100,40,33}trim",
301740 total: 100,
302741 current: 40,
305744 want: "[++++++++++++++++++++++++++++++++======>-----------------------------------------------------------]",
306745 },
307746 {
747 style: BarStyle().Tip("<").Reverse(),
308748 name: "t,c,r{100,40,33},rev",
309749 total: 100,
310750 current: 40,
311751 refill: 33,
312 reverse: true,
313752 want: " [----------------------------------------------------------<=====++++++++++++++++++++++++++++++++] ",
314753 },
315754 {
755 style: BarStyle().Tip("<").Reverse(),
316756 name: "t,c,r{100,40,33}trim,rev",
317757 total: 100,
318758 current: 40,
319759 refill: 33,
320760 trim: true,
321 reverse: true,
322761 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: "[+>--------------------------------------------------------]",
341762 },
342763 },
343764 }
345766 var tmpBuf bytes.Buffer
346767 for tw, cases := range testSuite {
347768 for _, tc := range cases {
348 s := newTestState(tc.style, tc.reverse)
769 s := newTestState(tc.style.Build())
349770 s.reqWidth = tc.barWidth
350771 s.total = tc.total
351772 s.current = tc.current
352773 s.trimSpace = tc.trim
353774 s.refill = tc.refill
354775 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 }
356780 by := tmpBuf.Bytes()
357781
358782 got := string(by[:len(by)-1])
366790 }
367791 }
368792
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 },
3751214 }
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 }
3771241 }
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 }
66 "math/rand"
77 "time"
88
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"
1111 )
1212
1313 func Example() {
1616
1717 total := 100
1818 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("╟"),
2323 mpb.PrependDecorators(
2424 // display our name with one space on the right
2525 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
7777 defer proxyReader.Close()
7878
7979 // and copy from reader, ignoring errors
80 io.Copy(ioutil.Discard, proxyReader)
80 _, _ = io.Copy(ioutil.Discard, proxyReader)
8181
8282 p.Wait()
8383 }
0 module github.com/vbauerster/mpb/v6
0 module github.com/vbauerster/mpb/v7
11
22 require (
3 github.com/VividCortex/ewma v1.1.1
3 github.com/VividCortex/ewma v1.2.0
44 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
87 )
98
109 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=
22 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
33 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=
76 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
87 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=
22 import "math"
33
44 // 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 {
66 if total <= 0 {
77 return 0
88 }
1313 }
1414
1515 // 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 {
1717 return math.Round(Percentage(total, current, width))
1818 }
33
44 func TestPercentage(t *testing.T) {
55 // key is barWidth
6 testSuite := map[int][]struct {
6 testSuite := map[uint][]struct {
77 name string
88 total int64
99 current int64
+0
-6
internal/predicate.go less more
0 package internal
1
2 // Predicate helper for internal use.
3 func Predicate(pick bool) func() bool {
4 return func() bool { return pick }
5 }
22 // CheckRequestedWidth checks that requested width doesn't overflow
33 // available width
44 func CheckRequestedWidth(requested, available int) int {
5 if requested <= 0 || requested >= available {
5 if requested < 1 || requested >= available {
66 return available
77 }
88 return requested
55 "context"
66 "fmt"
77 "io"
8 "io/ioutil"
9 "log"
108 "math"
119 "os"
1210 "sync"
1311 "time"
1412
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"
1715 )
1816
1917 const (
2018 // default RefreshRate
21 prr = 120 * time.Millisecond
19 prr = 150 * time.Millisecond
2220 )
2321
2422 // Progress represents a container that renders one or more progress
3230 done chan struct{}
3331 refreshCh chan time.Time
3432 once sync.Once
35 dlogger *log.Logger
3633 }
3734
3835 // pState holds bars in its priorityQueue. It gets passed to
7471 rr: prr,
7572 parkedBars: make(map[*Bar]*Bar),
7673 output: os.Stdout,
77 debugOut: ioutil.Discard,
7874 }
7975
8076 for _, opt := range options {
9086 bwg: new(sync.WaitGroup),
9187 operateState: make(chan func(*pState)),
9288 done: make(chan struct{}),
93 dlogger: log.New(s.debugOut, "[mpb] ", log.Lshortfile),
9489 }
9590
9691 p.cwg.Add(1)
9893 return p
9994 }
10095
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.
10397 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...)
112109 }
113110
114111 // Add creates a bar which renders itself by provided filler.
116113 // Panics if *Progress instance is done, i.e. called after *Progress.Wait().
117114 func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
118115 if filler == nil {
119 filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
116 filler = NopStyle().Build()
120117 }
121118 p.bwg.Add(1)
122119 result := make(chan *Bar)
156153 }
157154 }
158155
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) {
160175 select {
161176 case p.operateState <- func(s *pState) {
162177 if b.index < 0 {
169184 }
170185 }
171186
172 // UpdateBarPriority same as *Bar.SetPriority(int).
173 func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
174 p.setBarPriority(b, priority)
175 }
176
177187 // BarCount returns bars count.
178188 func (p *Progress) BarCount() int {
179 result := make(chan int, 1)
189 result := make(chan int)
180190 select {
181191 case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
182192 return <-result
218228 op(s)
219229 case <-p.refreshCh:
220230 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 }
222239 }
223240 case <-s.shutdownNotifier:
224 if s.heapUpdated {
241 for s.heapUpdated {
225242 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 }
227251 }
228252 }
229253 return
290314 }
291315
292316 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())
295319 for s.bHeap.Len() > 0 {
296320 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 }
298326 if b.toShutdown {
299327 if b.recoveredPanic != nil {
300328 s.barShutdownQueue = append(s.barShutdownQueue, b)
307335 }()
308336 }
309337 }
310 lineCount += b.extendedLines + 1
311 bm[b] = struct{}{}
338 bm[b] = frame.lines
339 totalLines += frame.lines
312340 }
313341
314342 for _, b := range s.barShutdownQueue {
319347 b.toDrop = true
320348 }
321349 if s.popCompleted && !b.noPop {
322 lineCount -= b.extendedLines + 1
350 totalLines -= bm[b]
323351 b.toDrop = true
324352 }
325353 if b.toDrop {
334362 heap.Push(&s.bHeap, b)
335363 }
336364
337 return cw.Flush(lineCount)
365 return cw.Flush(totalLines)
338366 }
339367
340368 func (s *pState) updateSyncMatrix() {
385413 bs.priority = -(math.MaxInt32 - s.idCount)
386414 }
387415
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 }
391419
392420 return bs
393421 }
88 "testing"
99 "time"
1010
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"
1313 )
1414
1515 func init() {
6868 wg.Wait()
6969 count := p.BarCount()
7070 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)
7272 }
7373 bars[1].Abort(true)
7474 bars[2].Abort(true)
7676 }
7777
7878 func TestWithContext(t *testing.T) {
79 shutdown := make(chan struct{})
7980 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))
8682
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 }()
10095
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
102106 cancel()
103
104 p.Wait()
105107 select {
106108 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")
109111 }
110112 }
111113
126128 *mpb.MaxWidthDistributor = makeWrapper(*mpb.MaxWidthDistributor, start, end)
127129
128130 total := 80
129 numBars := 3
131 numBars := 6
130132 p := mpb.New(mpb.WithOutput(ioutil.Discard))
131133 for i := 0; i < numBars; i++ {
132134 bar := p.AddBar(int64(total),
140142 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
141143 for i := 0; i < total; i++ {
142144 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 }
145151 }
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)
148154 bar.DecoratorEwmaUpdate(time.Since(start))
149155 }
150156 }()
1010 bar *Bar
1111 }
1212
13 func (x *proxyReader) Read(p []byte) (int, error) {
13 func (x proxyReader) Read(p []byte) (int, error) {
1414 n, err := x.ReadCloser.Read(p)
1515 x.bar.IncrBy(n)
1616 if err == io.EOF {
17 go x.bar.SetTotal(0, true)
17 go x.bar.SetTotal(-1, true)
1818 }
1919 return n, err
2020 }
2121
2222 type proxyWriterTo struct {
23 io.ReadCloser // *proxyReader
24 wt io.WriterTo
25 bar *Bar
23 proxyReader
24 wt io.WriterTo
2625 }
2726
28 func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
27 func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
2928 n, err := x.wt.WriteTo(w)
3029 x.bar.IncrInt64(n)
3130 if err == io.EOF {
32 go x.bar.SetTotal(0, true)
31 go x.bar.SetTotal(-1, true)
3332 }
3433 return n, err
3534 }
3635
3736 type ewmaProxyReader struct {
38 io.ReadCloser // *proxyReader
39 bar *Bar
40 iT time.Time
37 proxyReader
4138 }
4239
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)
4543 if n > 0 {
46 x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
47 x.iT = time.Now()
44 x.bar.DecoratorEwmaUpdate(time.Since(start))
4845 }
4946 return n, err
5047 }
5148
5249 type ewmaProxyWriterTo struct {
53 io.ReadCloser // *ewmaProxyReader
54 wt io.WriterTo // *proxyWriterTo
55 bar *Bar
56 iT time.Time
50 ewmaProxyReader
51 wt proxyWriterTo
5752 }
5853
59 func (x *ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
54 func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
55 start := time.Now()
6056 n, err := x.wt.WriteTo(w)
6157 if n > 0 {
62 x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
63 x.iT = time.Now()
58 x.bar.DecoratorEwmaUpdate(time.Since(start))
6459 }
6560 return n, err
6661 }
6762
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
7771 }
78 } else if isWriterTo {
79 rc = &proxyWriterTo{rc, wt, bar}
72 } else if b.hasEwmaDecorators {
73 rc = ewmaProxyReader{pr}
74 } else {
75 rc = pr
8076 }
8177 return rc
8278 }
66 "strings"
77 "testing"
88
9 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v7"
1010 )
1111
1212 const content = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
6666 p := mpb.New(mpb.WithOutput(ioutil.Discard))
6767
6868 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}
7170
7271 bar := p.AddBar(int64(len(content)), mpb.BarFillerTrim())
7372