Codebase list golang-github-vbauerster-mpb / 739601b
New upstream version 8.6.1 Reinhard Tartler 2 years ago
109 changed file(s) with 5289 addition(s) and 2625 deletion(s). Raw diff Collapse all Expand all
0 name: golangci-lint
1
2 on:
3 push:
4 tags:
5 - v*
6 branches:
7 - master
8 - main
9 pull_request:
10
11 permissions:
12 contents: read
13 # Optional: allow read access to pull request. Use with `only-new-issues` option.
14 pull-requests: read
15
16 jobs:
17 golangci:
18 strategy:
19 matrix:
20 go-version: ['stable']
21 os: [ubuntu-latest, macos-latest]
22 runs-on: ${{ matrix.os }}
23 steps:
24 - uses: actions/checkout@v3
25 - uses: actions/setup-go@v4
26 with:
27 go-version: ${{ matrix.go-version }}
28 cache: false
29 - uses: golangci/golangci-lint-action@v3
30 with:
31 # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
32 version: latest
33 # Optional: working directory, useful for monorepos
34 # working-directory: somedir
35
36 # Optional: golangci-lint command line arguments.
37 # args: --issues-exit-code=0
38
39 # Optional: show only new issues if it's a pull request. The default value is `false`.
40 only-new-issues: true
0 name: test
1
2 on:
3 push:
4 tags:
5 - v*
6 branches:
7 - master
8 - v*
9 pull_request:
10
11 permissions:
12 contents: read
13 pull-requests: read
14
15 jobs:
16 test:
17 strategy:
18 matrix:
19 go-version: ['stable', 'oldstable']
20 os: [ubuntu-latest, macos-latest, windows-latest]
21 runs-on: ${{ matrix.os }}
22 steps:
23 - uses: actions/checkout@v3
24 - uses: actions/setup-go@v4
25 with:
26 go-version: ${{ matrix.go-version }}
27 - 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
0 When contributing your first changes, please include an empty commit for
1 copyright waiver using the following message (replace 'John Doe' with
2 your name or nickname):
3
4 John Doe Copyright Waiver
5
6 I dedicate any and all copyright interest in this software to the
7 public domain. I make this dedication for the benefit of the public at
8 large and to the detriment of my heirs and successors. I intend this
9 dedication to be an overt act of relinquishment in perpetuity of all
10 present and future rights to this software under copyright law.
11
12 The command to create an empty commit from the command-line is:
13
14 git commit --allow-empty
00 # Multi Progress Bar
11
2 [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
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)
5 [![Donate with PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/vbauerster)
2 [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v8)
3 [![Test status](https://github.com/vbauerster/mpb/actions/workflows/test.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
4 [![Lint status](https://github.com/vbauerster/mpb/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/golangci-lint.yml)
65
76 **mpb** is a Go lib for rendering progress bars in terminal applications.
87
2625 "math/rand"
2726 "time"
2827
29 "github.com/vbauerster/mpb/v7"
30 "github.com/vbauerster/mpb/v7/decor"
28 "github.com/vbauerster/mpb/v8"
29 "github.com/vbauerster/mpb/v8/decor"
3130 )
3231
3332 func main() {
3635
3736 total := 100
3837 name := "Single Bar:"
39 // adding a single bar, which will inherit container's width
40 bar := p.Add(int64(total),
41 // progress bar filler with customized style
42 mpb.NewBarFiller(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
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("╟"),
4342 mpb.PrependDecorators(
4443 // display our name with one space on the right
4544 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
6564
6665 ```go
6766 var wg sync.WaitGroup
68 // passed &wg will be accounted at p.Wait() call
67 // passed wg will be accounted at p.Wait() call
6968 p := mpb.New(mpb.WithWaitGroup(&wg))
7069 total, numBars := 100, 3
7170 wg.Add(numBars)
8281 mpb.AppendDecorators(
8382 // replace ETA decorator with "done" message, OnComplete event
8483 decor.OnComplete(
85 // ETA decorator with ewma age of 60
86 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
84 // ETA decorator with ewma age of 30
85 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
8786 ),
8887 ),
8988 )
9796 // EWMA's unit of measure is an iteration's duration
9897 start := time.Now()
9998 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
100 bar.Increment()
101 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
102 bar.DecoratorEwmaUpdate(time.Since(start))
99 // we need to call EwmaIncrement to fulfill ewma decorator's contract
100 bar.EwmaIncrement(time.Since(start))
103101 }
104102 }()
105103 }
106 // Waiting for passed &wg and for all bars to complete and flush
104 // wait for passed wg and for all bars to complete and flush
107105 p.Wait()
108106 ```
109107
00 module github.com/vbauerster/mpb/_examples/barExtender
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
66 "sync"
77 "time"
88
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
9 "github.com/vbauerster/mpb/v8"
10 "github.com/vbauerster/mpb/v8/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)
1819
1920 for i := 0; i < numBars; i++ {
2021 name := fmt.Sprintf("Bar#%d:", i)
21 efn := func(w io.Writer, _ int, s decor.Statistics) {
22 efn := func(w io.Writer, s decor.Statistics) (err error) {
2223 if s.Completed {
23 fmt.Fprintf(w, "Bar id: %d has been completed\n", s.ID)
24 _, err = fmt.Fprintf(w, "Bar id: %d has been completed\n", s.ID)
2425 }
26 return err
2527 }
26 bar := p.AddBar(int64(total), mpb.BarExtender(mpb.BarFillerFunc(efn)),
28 bar := p.AddBar(int64(total),
29 mpb.BarExtender(mpb.BarFillerFunc(efn), false),
2730 mpb.PrependDecorators(
2831 // simple name decorator
2932 decor.Name(name),
3336 mpb.AppendDecorators(
3437 // replace ETA decorator with "done" message, OnComplete event
3538 decor.OnComplete(
36 // ETA decorator with ewma age of 60
37 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
39 // ETA decorator with ewma age of 30
40 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done",
3841 ),
3942 ),
4043 )
4851 // EWMA's unit of measure is an iteration's duration
4952 start := time.Now()
5053 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
51 bar.Increment()
52 // since EWMA based decorator is used, DecoratorEwmaUpdate should be called
53 bar.DecoratorEwmaUpdate(time.Since(start))
54 // we need to call EwmaIncrement to fulfill ewma decorator's contract
55 bar.EwmaIncrement(time.Since(start))
5456 }
5557 }()
5658 }
57 // wait for all bars to complete and flush
59 // wait for passed wg and for all bars to complete and flush
5860 p.Wait()
5961 }
0 module github.com/vbauerster/mpb/_examples/barExtenderRev
1
2 go 1.17
3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
0 package main
1
2 import (
3 "fmt"
4 "io"
5 "math/rand"
6 "sync/atomic"
7 "time"
8
9 "github.com/vbauerster/mpb/v8"
10 "github.com/vbauerster/mpb/v8/decor"
11 )
12
13 var curTask uint32
14 var doneTasks uint32
15
16 type task struct {
17 id uint32
18 total int64
19 bar *mpb.Bar
20 }
21
22 func main() {
23 numTasks := 4
24
25 var total int64
26 var filler mpb.BarFiller
27 tasks := make([]*task, numTasks)
28
29 for i := 0; i < numTasks; i++ {
30 task := &task{
31 id: uint32(i),
32 total: rand.Int63n(666) + 100,
33 }
34 total += task.total
35 filler = middleware(filler, task.id)
36 tasks[i] = task
37 }
38
39 filler = newLineMiddleware(filler)
40
41 p := mpb.New()
42
43 for i := 0; i < numTasks; i++ {
44 bar := p.AddBar(tasks[i].total,
45 mpb.BarExtender(filler, true), // all bars share same extender filler
46 mpb.BarFuncOptional(func() mpb.BarOption {
47 return mpb.BarQueueAfter(tasks[i-1].bar)
48 }, i != 0),
49 mpb.PrependDecorators(
50 decor.Name("current:", decor.WCSyncWidthR),
51 ),
52 mpb.AppendDecorators(
53 decor.Percentage(decor.WCSyncWidth),
54 ),
55 )
56 tasks[i].bar = bar
57 }
58
59 tb := p.AddBar(0,
60 mpb.PrependDecorators(
61 decor.Any(func(st decor.Statistics) string {
62 return fmt.Sprintf("TOTAL(%d/%d)", atomic.LoadUint32(&doneTasks), len(tasks))
63 }, decor.WCSyncWidthR),
64 ),
65 mpb.AppendDecorators(
66 decor.Percentage(decor.WCSyncWidth),
67 ),
68 )
69
70 tb.SetTotal(total, false)
71
72 for _, t := range tasks {
73 atomic.StoreUint32(&curTask, t.id)
74 complete(t.bar, tb)
75 atomic.AddUint32(&doneTasks, 1)
76 }
77
78 tb.EnableTriggerComplete()
79
80 p.Wait()
81 }
82
83 func middleware(base mpb.BarFiller, id uint32) mpb.BarFiller {
84 var done bool
85 fn := func(w io.Writer, st decor.Statistics) error {
86 if !done {
87 if atomic.LoadUint32(&curTask) != id {
88 _, err := fmt.Fprintf(w, " Taksk %02d\n", id)
89 return err
90 }
91 if !st.Completed {
92 _, err := fmt.Fprintf(w, "=> Taksk %02d\n", id)
93 return err
94 }
95 done = true
96 }
97 _, err := fmt.Fprintf(w, " Taksk %02d: Done!\n", id)
98 return err
99 }
100 if base == nil {
101 return mpb.BarFillerFunc(fn)
102 }
103 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
104 err := fn(w, st)
105 if err != nil {
106 return err
107 }
108 return base.Fill(w, st)
109 })
110 }
111
112 func newLineMiddleware(base mpb.BarFiller) mpb.BarFiller {
113 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
114 _, err := fmt.Fprintln(w)
115 if err != nil {
116 return err
117 }
118 return base.Fill(w, st)
119 })
120 }
121
122 func complete(bar, totalBar *mpb.Bar) {
123 max := 100 * time.Millisecond
124 for !bar.Completed() {
125 n := rand.Int63n(10) + 1
126 incrementBars(n, bar, totalBar)
127 time.Sleep(time.Duration(n) * max / 10)
128 }
129 bar.Wait()
130 }
131
132 func incrementBars(n int64, bb ...*mpb.Bar) {
133 for _, b := range bb {
134 b.IncrInt64(n)
135 }
136 }
00 module github.com/vbauerster/mpb/_examples/cancel
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
66 "sync"
77 "time"
88
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
9 "github.com/vbauerster/mpb/v8"
10 "github.com/vbauerster/mpb/v8/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
2122 wg.Add(numBars)
2223
2324 for i := 0; i < numBars; i++ {
24 name := fmt.Sprintf("Bar#%d:", i)
25 name := fmt.Sprintf("Bar#%02d: ", i)
2526 bar := p.AddBar(int64(total),
2627 mpb.PrependDecorators(
27 decor.Name(name),
28 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace),
28 decor.Name(name, decor.WCSyncWidthR),
29 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth),
2930 ),
3031 mpb.AppendDecorators(
3132 // note that OnComplete will not be fired, because of cancel
3738 defer wg.Done()
3839 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
3940 max := 100 * time.Millisecond
40 for !bar.Completed() {
41 for bar.IsRunning() {
4142 // start variable is solely for EWMA calculation
4243 // EWMA's unit of measure is an iteration's duration
4344 start := time.Now()
4445 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
45 bar.Increment()
46 // since EWMA based decorator is used, DecoratorEwmaUpdate should be called
47 bar.DecoratorEwmaUpdate(time.Since(start))
46 // we need to call EwmaIncrement to fulfill ewma decorator's contract
47 bar.EwmaIncrement(time.Since(start))
4848 }
4949 }()
5050 }
51
51 // wait for passed wg and for all bars to complete and flush
5252 p.Wait()
5353 }
00 module github.com/vbauerster/mpb/_examples/complex
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require (
5 github.com/fatih/color v1.15.0
6 github.com/vbauerster/mpb/v8 v8.6.1
7 )
8
9 require (
10 github.com/VividCortex/ewma v1.2.0 // indirect
11 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
12 github.com/mattn/go-colorable v0.1.13 // indirect
13 github.com/mattn/go-isatty v0.0.19 // indirect
14 github.com/mattn/go-runewidth v0.0.15 // indirect
15 github.com/rivo/uniseg v0.4.4 // indirect
16 golang.org/x/sys v0.11.0 // indirect
17 )
22 import (
33 "fmt"
44 "math/rand"
5 "sync"
65 "time"
76
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/fatih/color"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
12 func init() {
13 rand.Seed(time.Now().UnixNano())
14 }
12 func main() {
13 numBars := 4
14 // to support color in Windows following both options are required
15 p := mpb.New(
16 mpb.WithOutput(color.Output),
17 mpb.WithAutoRefresh(),
18 )
1519
16 func main() {
17 doneWg := new(sync.WaitGroup)
18 p := mpb.New(mpb.WithWaitGroup(doneWg))
19 numBars := 4
20 red, green := color.New(color.FgRed), color.New(color.FgGreen)
2021
21 var bars []*mpb.Bar
22 var downloadWgg []*sync.WaitGroup
2322 for i := 0; i < numBars; i++ {
24 wg := new(sync.WaitGroup)
25 wg.Add(1)
26 downloadWgg = append(downloadWgg, wg)
2723 task := fmt.Sprintf("Task#%02d:", i)
28 job := "downloading"
29 b := p.AddBar(rand.Int63n(201)+100,
24 queue := make([]*mpb.Bar, 2)
25 queue[0] = p.AddBar(rand.Int63n(201)+100,
3026 mpb.PrependDecorators(
3127 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
32 decor.Name(job, decor.WCSyncSpaceR),
28 decor.Name("downloading", decor.WCSyncSpaceR),
3329 decor.CountersNoUnit("%d / %d", decor.WCSyncWidth),
3430 ),
35 mpb.AppendDecorators(decor.Percentage(decor.WC{W: 5})),
31 mpb.AppendDecorators(
32 decor.OnComplete(decor.Percentage(decor.WC{W: 5}), "done"),
33 ),
3634 )
37 go newTask(wg, b, i+1)
38 bars = append(bars, b)
39 }
35 queue[1] = p.AddBar(rand.Int63n(101)+100,
36 mpb.BarQueueAfter(queue[0]), // this bar is queued
37 mpb.BarFillerClearOnComplete(),
38 mpb.PrependDecorators(
39 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
40 decor.OnCompleteMeta(
41 decor.OnComplete(
42 decor.Meta(decor.Name("installing", decor.WCSyncSpaceR), toMetaFunc(red)),
43 "done!",
44 ),
45 toMetaFunc(green),
46 ),
47 decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_MMSS, 0, decor.WCSyncWidth), ""),
48 ),
49 mpb.AppendDecorators(
50 decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""),
51 ),
52 )
4053
41 for i := 0; i < numBars; i++ {
42 doneWg.Add(1)
43 i := i
4454 go func() {
45 task := fmt.Sprintf("Task#%02d:", i)
46 // ANSI escape sequences are not supported on Windows OS
47 job := "\x1b[31;1;4mつのだ☆HIRO\x1b[0m"
48 // preparing delayed bars
49 b := p.AddBar(rand.Int63n(101)+100,
50 mpb.BarQueueAfter(bars[i]),
51 mpb.BarFillerClearOnComplete(),
52 mpb.PrependDecorators(
53 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
54 decor.OnComplete(decor.Name(job, decor.WCSyncSpaceR), "done!"),
55 decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_MMSS, 0, decor.WCSyncWidth), ""),
56 ),
57 mpb.AppendDecorators(
58 decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""),
59 ),
60 )
61 // waiting for download to complete, before starting install job
62 downloadWgg[i].Wait()
63 go newTask(doneWg, b, numBars-i)
55 for _, b := range queue {
56 complete(b)
57 }
6458 }()
6559 }
6660
6761 p.Wait()
6862 }
6963
70 func newTask(wg *sync.WaitGroup, bar *mpb.Bar, incrBy int) {
71 defer wg.Done()
64 func complete(bar *mpb.Bar) {
7265 max := 100 * time.Millisecond
7366 for !bar.Completed() {
7467 // start variable is solely for EWMA calculation
7568 // EWMA's unit of measure is an iteration's duration
7669 start := time.Now()
7770 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
78 bar.IncrBy(incrBy)
79 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
80 bar.DecoratorEwmaUpdate(time.Since(start))
71 // we need to call EwmaIncrement to fulfill ewma decorator's contract
72 bar.EwmaIncrInt64(rand.Int63n(5)+1, time.Since(start))
8173 }
8274 }
75
76 func toMetaFunc(c *color.Color) func(string) string {
77 return func(s string) string {
78 return c.Sprint(s)
79 }
80 }
00 module github.com/vbauerster/mpb/_examples/decoratorsOnTop
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v7"
8 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v8"
8 "github.com/vbauerster/mpb/v8/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().Build()), false), // 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(mpb.BarStyle()))),
2728 )
2829 // simulating some work
2930 max := 100 * time.Millisecond
3536 p.Wait()
3637 }
3738
38 func nlBarFiller(filler mpb.BarFiller) mpb.BarFiller {
39 return mpb.BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
40 filler.Fill(w, reqWidth, st)
41 w.Write([]byte("\n"))
39 func extended(base mpb.BarFiller) mpb.BarFiller {
40 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
41 err := base.Fill(w, st)
42 if err != nil {
43 return err
44 }
45 _, err = io.WriteString(w, "\n")
46 return err
4247 })
4348 }
00 module github.com/vbauerster/mpb/_examples/differentWidth
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/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
3333 mpb.AppendDecorators(
3434 // replace ETA decorator with "done" message, OnComplete event
3535 decor.OnComplete(
36 // ETA decorator with ewma age of 60
37 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
36 // ETA decorator with ewma age of 30
37 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done",
3838 ),
3939 ),
4040 )
4848 // EWMA's unit of measure is an iteration's duration
4949 start := time.Now()
5050 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
51 bar.Increment()
52 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
53 bar.DecoratorEwmaUpdate(time.Since(start))
51 // we need to call EwmaIncrement to fulfill ewma decorator's contract
52 bar.EwmaIncrement(time.Since(start))
5453 }
5554 }()
5655 }
57 // wait for all bars to complete and flush
56 // wait for passed wg and for all bars to complete and flush
5857 p.Wait()
5958 }
00 module github.com/vbauerster/mpb/_examples/dynTotal
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v7"
8 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v8"
8 "github.com/vbauerster/mpb/v8/decor"
99 )
10
11 func init() {
12 rand.Seed(time.Now().UnixNano())
13 }
1410
1511 func main() {
1612 p := mpb.New(mpb.WithWidth(64))
1713
18 var total int64
1914 // new bar with 'trigger complete event' disabled, because total is zero
20 bar := p.AddBar(total,
21 mpb.PrependDecorators(decor.Counters(decor.UnitKiB, "% .1f / % .1f")),
15 bar := p.AddBar(0,
16 mpb.PrependDecorators(decor.Counters(decor.SizeB1024(0), "% .1f / % .1f")),
2217 mpb.AppendDecorators(decor.Percentage()),
2318 )
2419
2621 read := makeStream(200)
2722 for {
2823 n, err := read()
29 total += int64(n)
3024 if err == io.EOF {
25 // triggering complete event now
26 bar.SetTotal(-1, true)
3127 break
3228 }
33 // while total is unknown,
34 // set it to a positive number which is greater than current total,
35 // to make sure no complete event is triggered by next IncrBy call.
36 bar.SetTotal(total+2048, false)
29 // increment methods won't trigger complete event because bar was constructed with total = 0
3730 bar.IncrBy(n)
31 // following call is not required, it's called to show some progress instead of an empty bar
32 bar.SetTotal(bar.Current()+2048, false)
3833 time.Sleep(time.Duration(rand.Intn(10)+1) * maxSleep / 10)
3934 }
40
41 // force bar complete event, note true flag
42 bar.SetTotal(total, true)
4335
4436 p.Wait()
4537 }
0 #!/bin/sh
1
2 for d in *; do
3 [ ! -d "$d" ] && continue
4 pushd "$d" >/dev/null 2>&1
5 go mod tidy
6 popd >/dev/null 2>&1
7 done
00 module github.com/vbauerster/mpb/_examples/io
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "io/ioutil"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
1818 mpb.WithRefreshRate(180*time.Millisecond),
1919 )
2020
21 bar := p.Add(total,
22 mpb.NewBarFiller(mpb.BarStyle().Rbound("|")),
21 bar := p.New(total,
22 mpb.BarStyle().Rbound("|"),
2323 mpb.PrependDecorators(
24 decor.CountersKibiByte("% .2f / % .2f"),
24 decor.Counters(decor.SizeB1024(0), "% .2f / % .2f"),
2525 ),
2626 mpb.AppendDecorators(
27 decor.EwmaETA(decor.ET_STYLE_GO, 90),
27 decor.EwmaETA(decor.ET_STYLE_GO, 30),
2828 decor.Name(" ] "),
29 decor.EwmaSpeed(decor.UnitKiB, "% .2f", 60),
29 decor.EwmaSpeed(decor.SizeB1024(0), "% .2f", 30),
3030 ),
3131 )
3232
3535 defer proxyReader.Close()
3636
3737 // copy from proxyReader, ignoring errors
38 io.Copy(ioutil.Discard, proxyReader)
38 _, _ = io.Copy(ioutil.Discard, proxyReader)
3939
4040 p.Wait()
4141 }
+0
-5
_examples/merge/go.mod less more
0 module github.com/vbauerster/mpb/_examples/merge
1
2 go 1.14
3
4 require github.com/vbauerster/mpb/v7 v7.0.3
+0
-72
_examples/merge/main.go less more
0 package main
1
2 import (
3 "math/rand"
4 "strings"
5 "sync"
6 "time"
7
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
10 )
11
12 func main() {
13 var wg sync.WaitGroup
14 // pass &wg (optional), so p will wait for it eventually
15 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(60))
16 total, numBars := 100, 3
17 wg.Add(numBars)
18
19 for i := 0; i < numBars; i++ {
20 var pdecorators mpb.BarOption
21 if i == 0 {
22 pdecorators = mpb.PrependDecorators(
23 decor.Merge(
24 decor.OnComplete(
25 newVariadicSpinner(decor.WCSyncSpace),
26 "done",
27 ),
28 decor.WCSyncSpace, // Placeholder
29 decor.WCSyncSpace, // Placeholder
30 ),
31 )
32 } else {
33 pdecorators = mpb.PrependDecorators(
34 decor.CountersNoUnit("% .1d / % .1d", decor.WCSyncSpace),
35 decor.OnComplete(decor.Spinner(nil, decor.WCSyncSpace), "done"),
36 decor.OnComplete(decor.Spinner(nil, decor.WCSyncSpace), "done"),
37 )
38 }
39 bar := p.AddBar(int64(total),
40 pdecorators,
41 mpb.AppendDecorators(
42 decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_GO, 60), "done"),
43 ),
44 )
45 // simulating some work
46 go func() {
47 defer wg.Done()
48 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
49 max := 100 * time.Millisecond
50 for i := 0; i < total; i++ {
51 // start variable is solely for EWMA calculation
52 // EWMA's unit of measure is an iteration's duration
53 start := time.Now()
54 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
55 bar.Increment()
56 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
57 bar.DecoratorEwmaUpdate(time.Since(start))
58 }
59 }()
60 }
61 // Waiting for passed &wg and for all bars to complete and flush
62 p.Wait()
63 }
64
65 func newVariadicSpinner(wc decor.WC) decor.Decorator {
66 spinner := decor.Spinner(nil)
67 fn := func(s decor.Statistics) string {
68 return strings.Repeat(spinner.Decor(s), int(s.Current/3))
69 }
70 return decor.Any(fn, wc)
71 }
00 module github.com/vbauerster/mpb/_examples/mexicanBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
33 "math/rand"
44 "time"
55
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
6 "github.com/vbauerster/mpb/v8"
7 "github.com/vbauerster/mpb/v8/decor"
88 )
99
1010 func main() {
1414 total := 100
1515 name := "Complex Filler:"
1616 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.Add(int64(total),
23 mpb.NewBarFiller(bs),
17 bs = bs.LboundMeta(func(s string) string {
18 return "\033[34m" + s + "\033[0m" // blue
19 })
20 bs = bs.Filler("_").FillerMeta(func(s string) string {
21 return "\033[36m" + s + "\033[0m" // cyan
22 })
23 bs = bs.Tip("⛵").TipMeta(func(s string) string {
24 return "\033[31m" + s + "\033[0m" // red
25 })
26 bs = bs.TipOnComplete() // leave tip on complete
27 bs = bs.Padding("_").PaddingMeta(func(s string) string {
28 return "\033[36m" + s + "\033[0m" // cyan
29 })
30 bs = bs.RboundMeta(func(s string) string {
31 return "\033[34m" + s + "\033[0m" // blue
32 })
33 bar := p.New(int64(total), bs,
2434 mpb.PrependDecorators(decor.Name(name)),
2535 mpb.AppendDecorators(decor.Percentage()),
2636 )
00 module github.com/vbauerster/mpb/_examples/multiBars
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // passed &wg will be accounted at p.Wait() call
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)
2828 mpb.AppendDecorators(
2929 // replace ETA decorator with "done" message, OnComplete event
3030 decor.OnComplete(
31 // ETA decorator with ewma age of 60
32 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
31 // ETA decorator with ewma age of 30
32 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
3333 ),
3434 ),
3535 )
4343 // EWMA's unit of measure is an iteration's duration
4444 start := time.Now()
4545 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
46 bar.Increment()
47 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
48 bar.DecoratorEwmaUpdate(time.Since(start))
46 // we need to call EwmaIncrement to fulfill ewma decorator's contract
47 bar.EwmaIncrement(time.Since(start))
4948 }
5049 }()
5150 }
52 // Waiting for passed &wg and for all bars to complete and flush
51 // wait for passed wg and for all bars to complete and flush
5352 p.Wait()
5453 }
+0
-5
_examples/panic/go.mod less more
0 module github.com/vbauerster/mpb/_examples/panic
1
2 go 1.14
3
4 require github.com/vbauerster/mpb/v7 v7.0.3
+0
-45
_examples/panic/main.go less more
0 package main
1
2 import (
3 "fmt"
4 "os"
5 "strings"
6 "sync"
7 "time"
8
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
11 )
12
13 func main() {
14 var wg sync.WaitGroup
15 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithDebugOutput(os.Stderr))
16
17 wantPanic := strings.Repeat("Panic ", 64)
18 numBars := 3
19 wg.Add(numBars)
20
21 for i := 0; i < numBars; i++ {
22 name := fmt.Sprintf("b#%02d:", i)
23 bar := p.AddBar(100, mpb.BarID(i), mpb.PrependDecorators(panicDecorator(name, wantPanic)))
24
25 go func() {
26 defer wg.Done()
27 for i := 0; i < 100; i++ {
28 time.Sleep(50 * time.Millisecond)
29 bar.Increment()
30 }
31 }()
32 }
33
34 p.Wait()
35 }
36
37 func panicDecorator(name, panicMsg string) decor.Decorator {
38 return decor.Any(func(st decor.Statistics) string {
39 if st.ID == 1 && st.Current >= 42 {
40 panic(panicMsg)
41 }
42 return name
43 })
44 }
00 module github.com/vbauerster/mpb/_examples/poplog
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v7"
8 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v8"
8 "github.com/vbauerster/mpb/v8/decor"
99 )
1010
1111 func main() {
12 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
1213 p := mpb.New(mpb.PopCompletedMode())
13
1414 total, numBars := 100, 4
1515 for i := 0; i < numBars; i++ {
1616 name := fmt.Sprintf("Bar#%d:", i)
2727 ),
2828 )
2929 // simulating some work
30 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
3130 max := 100 * time.Millisecond
3231 for i := 0; i < total; i++ {
3332 // start variable is solely for EWMA calculation
3433 // EWMA's unit of measure is an iteration's duration
3534 start := time.Now()
3635 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
37 bar.Increment()
38 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
39 bar.DecoratorEwmaUpdate(time.Since(start))
36 // we need to call EwmaIncrement to fulfill ewma decorator's contract
37 bar.EwmaIncrement(time.Since(start))
4038 }
4139 }
4240
0 module github.com/vbauerster/mpb/_examples/progressAsWriter
1
2 go 1.17
3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
0 package main
1
2 import (
3 "fmt"
4 "log"
5 "math/rand"
6 "sync"
7 "time"
8
9 "github.com/vbauerster/mpb/v8"
10 "github.com/vbauerster/mpb/v8/decor"
11 )
12
13 func main() {
14 total, numBars := 100, 2
15 var wg sync.WaitGroup
16 wg.Add(numBars)
17 done := make(chan interface{})
18 p := mpb.New(
19 mpb.WithWidth(64),
20 mpb.WithWaitGroup(&wg),
21 mpb.WithShutdownNotifier(done),
22 )
23
24 log.SetOutput(p)
25
26 for i := 0; i < numBars; i++ {
27 name := fmt.Sprintf("Bar#%d:", i)
28 bar := p.AddBar(int64(total),
29 mpb.PrependDecorators(
30 decor.Name(name),
31 decor.Percentage(decor.WCSyncSpace),
32 ),
33 mpb.AppendDecorators(
34 decor.OnComplete(
35 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
36 ),
37 ),
38 )
39 // simulating some work
40 go func() {
41 defer wg.Done()
42 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
43 max := 100 * time.Millisecond
44 for i := 0; i < total; i++ {
45 // start variable is solely for EWMA calculation
46 // EWMA's unit of measure is an iteration's duration
47 start := time.Now()
48 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
49 // we need to call EwmaIncrement to fulfill ewma decorator's contract
50 bar.EwmaIncrement(time.Since(start))
51 }
52 log.Println(name, "done")
53 }()
54 }
55
56 var qwg sync.WaitGroup
57 qwg.Add(1)
58 go func() {
59 quit:
60 for {
61 select {
62 case <-done:
63 // after done, underlying io.Writer returns mpb.DoneError
64 // so following isn't printed
65 log.Println("all done")
66 break quit
67 default:
68 log.Println("waiting for done")
69 time.Sleep(100 * time.Millisecond)
70 }
71 }
72 qwg.Done()
73 }()
74
75 p.Wait()
76 qwg.Wait()
77 }
00 module github.com/vbauerster/mpb/_examples/quietMode
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
22 import (
33 "flag"
44 "fmt"
5 "io"
56 "math/rand"
67 "sync"
78 "time"
89
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
10 "github.com/vbauerster/mpb/v8"
11 "github.com/vbauerster/mpb/v8/decor"
1112 )
1213
1314 var quietMode bool
1920 func main() {
2021 flag.Parse()
2122 var wg sync.WaitGroup
22 // pass &wg (optional), so p will wait for it eventually
23 // passed wg will be accounted at p.Wait() call
2324 p := mpb.New(
2425 mpb.WithWaitGroup(&wg),
25 mpb.ContainerOptional(
26 // setting to nil will:
27 // set output to ioutil.Discard and disable refresh rate cycle, in
28 // order not to consume much CPU. Hovewer a single refresh still will
29 // be triggered on bar complete event, per each bar.
30 mpb.WithOutput(nil),
31 quietMode,
32 ),
26 mpb.ContainerOptional(mpb.WithOutput(io.Discard), quietMode),
3327 )
3428 total, numBars := 100, 3
3529 wg.Add(numBars)
4640 mpb.AppendDecorators(
4741 // replace ETA decorator with "done" message, OnComplete event
4842 decor.OnComplete(
49 // ETA decorator with ewma age of 60
50 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
43 // ETA decorator with ewma age of 30
44 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done",
5145 ),
5246 ),
5347 )
6155 // EWMA's unit of measure is an iteration's duration
6256 start := time.Now()
6357 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
64 bar.Increment()
65 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
66 bar.DecoratorEwmaUpdate(time.Since(start))
58 // we need to call EwmaIncrement to fulfill ewma decorator's contract
59 bar.EwmaIncrement(time.Since(start))
6760 }
6861 }()
6962 }
70 // Waiting for passed &wg and for all bars to complete and flush
63 // wait for passed wg and for all bars to complete and flush
7164 p.Wait()
7265 fmt.Println("done")
7366 }
00 module github.com/vbauerster/mpb/_examples/remove
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/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
34 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()
43 for i := 0; bar.IsRunning(); i++ {
3844 if bar.ID() == 2 && i >= 42 {
39 // aborting and removing while bar is running
40 bar.Abort(true)
45 go 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 }
00 module github.com/vbauerster/mpb/_examples/reverseBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/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
19 condFillerBuilder := func(cond bool) mpb.BarFillerBuilder {
20 if cond { // reverse Bar on cond
21 return mpb.BarStyle().Tip("<").Reverse()
22 }
23 return mpb.BarStyle()
24 }
25
1926 for i := 0; i < numBars; i++ {
2027 name := fmt.Sprintf("Bar#%d:", i)
21 bs := mpb.BarStyle()
22 if i == 1 {
23 // reverse Bar#1
24 bs = bs.Tip("<").Reverse()
25 }
26 bar := p.Add(int64(total),
27 mpb.NewBarFiller(bs),
28 bar := p.New(int64(total),
29 condFillerBuilder(i == 1),
2830 mpb.PrependDecorators(
2931 // simple name decorator
3032 decor.Name(name),
3436 mpb.AppendDecorators(
3537 // replace ETA decorator with "done" message, OnComplete event
3638 decor.OnComplete(
37 // ETA decorator with ewma age of 60
38 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
39 // ETA decorator with ewma age of 30
40 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done",
3941 ),
4042 ),
4143 )
4951 // EWMA's unit of measure is an iteration's duration
5052 start := time.Now()
5153 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
52 bar.Increment()
53 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
54 bar.DecoratorEwmaUpdate(time.Since(start))
54 // we need to call EwmaIncrement to fulfill ewma decorator's contract
55 bar.EwmaIncrement(time.Since(start))
5556 }
5657 }()
5758 }
58 // Waiting for passed &wg and for all bars to complete and flush
59 // wait for passed wg and for all bars to complete and flush
5960 p.Wait()
6061 }
00 module github.com/vbauerster/mpb/_examples/singleBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
33 "math/rand"
44 "time"
55
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
6 "github.com/vbauerster/mpb/v8"
7 "github.com/vbauerster/mpb/v8/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(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
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}),
00 module github.com/vbauerster/mpb/_examples/spinTipBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
33 "math/rand"
44 "time"
55
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
6 "github.com/vbauerster/mpb/v8"
7 "github.com/vbauerster/mpb/v8/decor"
88 )
99
1010 func main() {
1313
1414 total := 100
1515 name := "Single Bar:"
16 bar := p.Add(int64(total),
17 mpb.NewBarFiller(mpb.BarStyle().Tip(`-`, `\`, `|`, `/`)),
16 bar := p.New(int64(total),
17 mpb.BarStyle().Tip(`-`, `\`, `|`, `/`).TipMeta(func(s string) string {
18 return "\033[31m" + s + "\033[0m" // red
19 }),
1820 mpb.PrependDecorators(decor.Name(name)),
1921 mpb.AppendDecorators(decor.Percentage()),
2022 )
00 module github.com/vbauerster/mpb/_examples/spinnerBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/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 condFillerBuilder := func(cond bool) mpb.BarFillerBuilder {
23 if cond {
24 s := mpb.SpinnerStyle("∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙")
25 return s.Meta(func(s string) string {
26 return "\033[31m" + s + "\033[0m" // red
27 })
28 }
29 return mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")
30 }
2231
2332 for i := 0; i < numBars; i++ {
2433 name := fmt.Sprintf("Bar#%d:", i)
25 var bar *mpb.Bar
26 if i == 0 {
27 bar = p.Add(int64(total),
28 mpb.NewBarFiller(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
29 mpb.PrependDecorators(
30 // simple name decorator
31 decor.Name(name),
34 bar := p.New(int64(total),
35 condFillerBuilder(i != 0),
36 mpb.PrependDecorators(
37 // simple name decorator
38 decor.Name(name),
39 ),
40 mpb.AppendDecorators(
41 // replace ETA decorator with "done" message, OnComplete event
42 decor.OnComplete(
43 // ETA decorator with ewma age of 30
44 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done",
3245 ),
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.NewBarFiller(mpb.SpinnerStyle(spinnerStyle...)),
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
46 ),
47 )
5848 // simulating some work
5949 go func() {
6050 defer wg.Done()
6555 // EWMA's unit of measure is an iteration's duration
6656 start := time.Now()
6757 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
68 bar.Increment()
69 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
70 bar.DecoratorEwmaUpdate(time.Since(start))
58 // we need to call EwmaIncrement to fulfill ewma decorator's contract
59 bar.EwmaIncrement(time.Since(start))
7160 }
7261 }()
7362 }
74 // wait for all bars to complete and flush
63 // wait for passed wg and for all bars to complete and flush
7564 p.Wait()
7665 }
00 module github.com/vbauerster/mpb/_examples/spinnerDecorator
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/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 }
00 module github.com/vbauerster/mpb/_examples/stress
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require (
5 github.com/pkg/profile v1.7.0
6 github.com/vbauerster/mpb/v8 v8.6.1
7 )
8
9 require (
10 github.com/VividCortex/ewma v1.2.0 // indirect
11 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
12 github.com/felixge/fgprof v0.9.3 // indirect
13 github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
14 github.com/mattn/go-runewidth v0.0.15 // indirect
15 github.com/rivo/uniseg v0.4.4 // indirect
16 golang.org/x/sys v0.11.0 // indirect
17 )
00 package main
11
22 import (
3 "flag"
34 "fmt"
45 "math/rand"
6 "os"
57 "sync"
68 "time"
79
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
10 "github.com/pkg/profile"
11 "github.com/vbauerster/mpb/v8"
12 "github.com/vbauerster/mpb/v8/decor"
1013 )
1114
1215 const (
1316 totalBars = 32
1417 )
1518
19 var proftype = flag.String("prof", "", "profile type (cpu, mem)")
20
1621 func main() {
22 flag.Parse()
23 switch *proftype {
24 case "cpu":
25 defer profile.Start(profile.CPUProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop()
26 case "mem":
27 defer profile.Start(profile.MemProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop()
28 }
1729 var wg sync.WaitGroup
18 p := mpb.New(
19 mpb.WithWaitGroup(&wg),
20 mpb.WithRefreshRate(50*time.Millisecond),
21 )
30 // passed wg will be accounted at p.Wait() call
31 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithDebugOutput(os.Stderr))
2232 wg.Add(totalBars)
2333
2434 for i := 0; i < totalBars; i++ {
2636 total := rand.Intn(320) + 10
2737 bar := p.AddBar(int64(total),
2838 mpb.PrependDecorators(
29 decor.Name(name),
30 decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncSpace),
39 decor.Name(name, decor.WCSyncWidthR),
40 decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncWidth),
3141 ),
3242 mpb.AppendDecorators(
3343 decor.OnComplete(
4656 }
4757 }()
4858 }
49
59 // wait for passed wg and for all bars to complete and flush
5060 p.Wait()
5161 }
00 module github.com/vbauerster/mpb/_examples/suppressBar
11
2 go 1.14
2 go 1.17
33
44 require (
5 github.com/mattn/go-runewidth v0.0.13
6 github.com/vbauerster/mpb/v7 v7.0.3
5 github.com/mattn/go-runewidth v0.0.15
6 github.com/vbauerster/mpb/v8 v8.6.1
77 )
8
9 require (
10 github.com/VividCortex/ewma v1.2.0 // indirect
11 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
12 github.com/rivo/uniseg v0.4.4 // indirect
13 golang.org/x/sys v0.11.0 // indirect
14 )
88 "time"
99
1010 "github.com/mattn/go-runewidth"
11 "github.com/vbauerster/mpb/v7"
12 "github.com/vbauerster/mpb/v7/decor"
11 "github.com/vbauerster/mpb/v8"
12 "github.com/vbauerster/mpb/v8/decor"
1313 )
1414
1515 func main() {
1919 msgCh := make(chan string)
2020 resumeCh := make(chan struct{})
2121 nextCh := make(chan struct{}, 1)
22 ew := &errorWrapper{}
23 timer := time.AfterFunc(2*time.Second, func() {
24 ew.reset(errors.New("timeout"))
25 })
26 defer timer.Stop()
2227 bar := p.AddBar(int64(total),
2328 mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller {
2429 var msg *string
25 return mpb.BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
26 select {
27 case m := <-msgCh:
30 var times int
31 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
32 if msg == nil {
33 select {
34 case m := <-msgCh:
35 msg = &m
36 times = 10
37 nextCh <- struct{}{}
38 default:
39 }
40 return base.Fill(w, st)
41 }
42 switch {
43 case times == 0, st.Completed, st.Aborted:
2844 defer func() {
29 msg = &m
45 msg = nil
3046 }()
31 nextCh <- struct{}{}
32 case <-resumeCh:
33 msg = nil
47 resumeCh <- struct{}{}
3448 default:
49 times--
3550 }
36 if msg != nil {
37 io.WriteString(w, runewidth.Truncate(*msg, st.AvailableWidth, "…"))
38 nextCh <- struct{}{}
39 } else {
40 base.Fill(w, reqWidth, st)
41 }
51 _, err := io.WriteString(w, runewidth.Truncate(*msg, st.AvailableWidth, "…"))
52 nextCh <- struct{}{}
53 return err
4254 })
4355 }),
4456 mpb.PrependDecorators(decor.Name("my bar:")),
4557 mpb.AppendDecorators(newCustomPercentage(nextCh)),
4658 )
47 ew := &errorWrapper{}
48 time.AfterFunc(2*time.Second, func() {
49 ew.reset(errors.New("timeout"))
50 })
5159 // simulating some work
5260 go func() {
5361 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
5664 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
5765 if ew.isErr() {
5866 msgCh <- fmt.Sprintf("%s at %d, retrying...", ew.Error(), i)
59 go ew.reset(nil)
6067 i--
6168 bar.SetRefill(int64(i))
62 time.Sleep(3 * time.Second)
63 resumeCh <- struct{}{}
69 ew.reset(nil)
70 <-resumeCh
6471 continue
6572 }
6673 bar.Increment()
93100 ew.Unlock()
94101 }
95102
103 type percentage struct {
104 decor.Decorator
105 suspend <-chan struct{}
106 }
107
108 func (d percentage) Decor(s decor.Statistics) (string, int) {
109 select {
110 case <-d.suspend:
111 return d.Format("")
112 default:
113 return d.Decorator.Decor(s)
114 }
115 }
116
96117 func newCustomPercentage(nextCh <-chan struct{}) decor.Decorator {
97 base := decor.Percentage()
98 fn := func(s decor.Statistics) string {
99 select {
100 case <-nextCh:
101 return ""
102 default:
103 return base.Decor(s)
104 }
118 return percentage{
119 Decorator: decor.Percentage(),
120 suspend: nextCh,
105121 }
106 return decor.Any(fn)
107122 }
0 module github.com/vbauerster/mpb/_examples/tipOnComplete
1
2 go 1.17
3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
0 package main
1
2 import (
3 "math/rand"
4 "time"
5
6 "github.com/vbauerster/mpb/v8"
7 "github.com/vbauerster/mpb/v8/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 }
+493
-323
bar.go less more
22 import (
33 "bytes"
44 "context"
5 "fmt"
65 "io"
7 "log"
8 "runtime/debug"
96 "strings"
7 "sync"
108 "time"
119
1210 "github.com/acarl005/stripansi"
1311 "github.com/mattn/go-runewidth"
14 "github.com/vbauerster/mpb/v7/decor"
12 "github.com/vbauerster/mpb/v8/decor"
1513 )
1614
1715 // Bar represents a progress bar.
1816 type Bar struct {
19 priority int // used by heap
20 index int // used by heap
21
22 extendedLines int
23 toShutdown bool
24 toDrop bool
25 noPop bool
26 hasEwmaDecorators bool
27 operateState chan func(*bState)
28 frameCh chan io.Reader
29 syncTableCh chan [][]chan int
30 completed chan bool
31
32 // cancel is called either by user or on complete event
33 cancel func()
34 // done is closed after cacheState is assigned
35 done chan struct{}
36 // cacheState is populated, right after close(shutdown)
37 cacheState *bState
38
39 container *Progress
40 dlogger *log.Logger
41 recoveredPanic interface{}
42 }
43
44 type extenderFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int)
45
46 // bState is actual bar state. It gets passed to *Bar.serve(...) monitor
47 // goroutine.
17 index int // used by heap
18 priority int // used by heap
19 frameCh chan *renderFrame
20 operateState chan func(*bState)
21 done chan struct{}
22 container *Progress
23 bs *bState
24 cancel func()
25 }
26
27 type syncTable [2][]chan int
28 type extenderFunc func([]io.Reader, decor.Statistics) ([]io.Reader, error)
29
30 // bState is actual bar's state.
4831 type bState struct {
4932 id int
5033 priority int
5134 reqWidth int
35 shutdown int
5236 total int64
5337 current int64
5438 refill int64
55 lastN int64
56 iterated bool
5739 trimSpace bool
5840 completed bool
59 completeFlushed bool
41 aborted bool
6042 triggerComplete bool
61 dropOnComplete bool
43 rmOnComplete bool
6244 noPop bool
45 autoRefresh bool
6346 aDecorators []decor.Decorator
6447 pDecorators []decor.Decorator
6548 averageDecorators []decor.AverageDecorator
6649 ewmaDecorators []decor.EwmaDecorator
6750 shutdownListeners []decor.ShutdownListener
68 bufP, bufB, bufA *bytes.Buffer
51 buffers [3]*bytes.Buffer
6952 filler BarFiller
70 middleware func(BarFiller) BarFiller
7153 extender extenderFunc
72
73 // runningBar is a key for *pState.parkedBars
74 runningBar *Bar
75
76 debugOut io.Writer
77 }
78
79 func newBar(container *Progress, bs *bState) *Bar {
80 logPrefix := fmt.Sprintf("%sbar#%02d ", container.dlogger.Prefix(), bs.id)
81 ctx, cancel := context.WithCancel(container.ctx)
54 renderReq chan<- time.Time
55 waitBar *Bar // key for (*pState).queueBars
56 }
57
58 type renderFrame struct {
59 rows []io.Reader
60 shutdown int
61 rmOnComplete bool
62 noPop bool
63 done bool
64 err error
65 }
66
67 func newBar(ctx context.Context, container *Progress, bs *bState) *Bar {
68 ctx, cancel := context.WithCancel(ctx)
8269
8370 bar := &Bar{
71 priority: bs.priority,
72 frameCh: make(chan *renderFrame, 1),
73 operateState: make(chan func(*bState)),
74 done: make(chan struct{}),
8475 container: container,
85 priority: bs.priority,
86 toDrop: bs.dropOnComplete,
87 noPop: bs.noPop,
88 operateState: make(chan func(*bState)),
89 frameCh: make(chan io.Reader, 1),
90 syncTableCh: make(chan [][]chan int, 1),
91 completed: make(chan bool, 1),
92 done: make(chan struct{}),
9376 cancel: cancel,
94 dlogger: log.New(bs.debugOut, logPrefix, log.Lshortfile),
95 }
96
77 }
78
79 container.bwg.Add(1)
9780 go bar.serve(ctx, bs)
9881 return bar
9982 }
10083
101 // ProxyReader wraps r with metrics required for progress tracking.
102 // Panics if r is nil.
84 // ProxyReader wraps io.Reader with metrics required for progress tracking.
85 // If `r` is 'unknown total/size' reader it's mandatory to call
86 // (*Bar).SetTotal(-1, true) method after (io.Reader).Read returns io.EOF.
87 // If bar is already completed or aborted, returns nil.
88 // Panics if `r` is nil.
10389 func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
10490 if r == nil {
10591 panic("expected non nil io.Reader")
10692 }
107 return newProxyReader(r, b)
93 result := make(chan bool)
94 select {
95 case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }:
96 return newProxyReader(r, b, <-result)
97 case <-b.done:
98 return nil
99 }
100 }
101
102 // ProxyWriter wraps io.Writer with metrics required for progress tracking.
103 // If bar is already completed or aborted, returns nil.
104 // Panics if `w` is nil.
105 func (b *Bar) ProxyWriter(w io.Writer) io.WriteCloser {
106 if w == nil {
107 panic("expected non nil io.Writer")
108 }
109 result := make(chan bool)
110 select {
111 case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }:
112 return newProxyWriter(w, b, <-result)
113 case <-b.done:
114 return nil
115 }
108116 }
109117
110118 // ID returs id of the bar.
114122 case b.operateState <- func(s *bState) { result <- s.id }:
115123 return <-result
116124 case <-b.done:
117 return b.cacheState.id
118 }
119 }
120
121 // Current returns bar's current number, in other words sum of all increments.
125 return b.bs.id
126 }
127 }
128
129 // Current returns bar's current value, in other words sum of all increments.
122130 func (b *Bar) Current() int64 {
123131 result := make(chan int64)
124132 select {
125133 case b.operateState <- func(s *bState) { result <- s.current }:
126134 return <-result
127135 case <-b.done:
128 return b.cacheState.current
136 return b.bs.current
129137 }
130138 }
131139
136144 func (b *Bar) SetRefill(amount int64) {
137145 select {
138146 case b.operateState <- func(s *bState) {
139 s.refill = amount
147 if amount < s.current {
148 s.refill = amount
149 } else {
150 s.refill = s.current
151 }
140152 }:
141153 case <-b.done:
142154 }
144156
145157 // TraverseDecorators traverses all available decorators and calls cb func on each.
146158 func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
147 select {
148 case b.operateState <- func(s *bState) {
149 for _, decorators := range [...][]decor.Decorator{
159 iter := make(chan decor.Decorator)
160 select {
161 case b.operateState <- func(s *bState) {
162 for _, decorators := range [][]decor.Decorator{
150163 s.pDecorators,
151164 s.aDecorators,
152165 } {
153166 for _, d := range decorators {
154 cb(extractBaseDecorator(d))
167 iter <- d
155168 }
156169 }
157 }:
158 case <-b.done:
159 }
160 }
161
162 // SetTotal sets total dynamically.
163 // If total is less than or equal to zero it takes progress' current value.
164 func (b *Bar) SetTotal(total int64, triggerComplete bool) {
165 select {
166 case b.operateState <- func(s *bState) {
167 s.triggerComplete = triggerComplete
168 if total <= 0 {
170 close(iter)
171 }:
172 for d := range iter {
173 cb(unwrap(d))
174 }
175 case <-b.done:
176 }
177 }
178
179 // EnableTriggerComplete enables triggering complete event. It's
180 // effective only for bars which were constructed with `total <= 0` and
181 // after total has been set with (*Bar).SetTotal(int64, false). If bar
182 // has been incremented to the total, complete event is triggered right
183 // away.
184 func (b *Bar) EnableTriggerComplete() {
185 select {
186 case b.operateState <- func(s *bState) {
187 if s.triggerComplete || s.total <= 0 {
188 return
189 }
190 if s.current >= s.total {
191 s.current = s.total
192 s.completed = true
193 b.triggerCompletion(s)
194 } else {
195 s.triggerComplete = true
196 }
197 }:
198 case <-b.done:
199 }
200 }
201
202 // SetTotal sets total to an arbitrary value. It's effective only for
203 // bar which was constructed with `total <= 0`. Setting total to negative
204 // value is equivalent to (*Bar).SetTotal((*Bar).Current(), bool) but faster.
205 // If triggerCompletion is true, total value is set to current and
206 // complete event is triggered right away.
207 func (b *Bar) SetTotal(total int64, triggerCompletion bool) {
208 select {
209 case b.operateState <- func(s *bState) {
210 if s.triggerComplete {
211 return
212 }
213 if total < 0 {
169214 s.total = s.current
170215 } else {
171216 s.total = total
172217 }
173 if s.triggerComplete && !s.completed {
218 if triggerCompletion {
174219 s.current = s.total
175220 s.completed = true
176 go b.refreshTillShutdown()
221 b.triggerCompletion(s)
177222 }
178223 }:
179224 case <-b.done:
181226 }
182227
183228 // SetCurrent sets progress' current to an arbitrary value.
184 // Setting a negative value will cause a panic.
185229 func (b *Bar) SetCurrent(current int64) {
186 select {
187 case b.operateState <- func(s *bState) {
188 s.iterated = true
189 s.lastN = current - s.current
230 if current < 0 {
231 return
232 }
233 select {
234 case b.operateState <- func(s *bState) {
190235 s.current = current
191236 if s.triggerComplete && s.current >= s.total {
192237 s.current = s.total
193238 s.completed = true
194 go b.refreshTillShutdown()
239 b.triggerCompletion(s)
240 }
241 }:
242 case <-b.done:
243 }
244 }
245
246 // EwmaSetCurrent sets progress' current to an arbitrary value and updates
247 // EWMA based decorators by dur of a single iteration.
248 func (b *Bar) EwmaSetCurrent(current int64, iterDur time.Duration) {
249 if current < 0 {
250 return
251 }
252 select {
253 case b.operateState <- func(s *bState) {
254 if n := current - s.current; n > 0 {
255 s.decoratorEwmaUpdate(n, iterDur)
256 }
257 s.current = current
258 if s.triggerComplete && s.current >= s.total {
259 s.current = s.total
260 s.completed = true
261 b.triggerCompletion(s)
195262 }
196263 }:
197264 case <-b.done:
210277
211278 // IncrInt64 increments progress by amount of n.
212279 func (b *Bar) IncrInt64(n int64) {
213 select {
214 case b.operateState <- func(s *bState) {
215 s.iterated = true
216 s.lastN = n
280 if n <= 0 {
281 return
282 }
283 select {
284 case b.operateState <- func(s *bState) {
217285 s.current += n
218286 if s.triggerComplete && s.current >= s.total {
219287 s.current = s.total
220288 s.completed = true
221 go b.refreshTillShutdown()
222 }
223 }:
224 case <-b.done:
225 }
226 }
227
228 // DecoratorEwmaUpdate updates all EWMA based decorators. Should be
229 // called on each iteration, because EWMA's unit of measure is an
230 // iteration's duration. Panics if called before *Bar.Incr... family
231 // methods.
232 func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
233 select {
234 case b.operateState <- func(s *bState) {
235 ewmaIterationUpdate(false, s, dur)
236 }:
237 case <-b.done:
238 ewmaIterationUpdate(true, b.cacheState, dur)
289 b.triggerCompletion(s)
290 }
291 }:
292 case <-b.done:
293 }
294 }
295
296 // EwmaIncrement is a shorthand for b.EwmaIncrInt64(1, iterDur).
297 func (b *Bar) EwmaIncrement(iterDur time.Duration) {
298 b.EwmaIncrInt64(1, iterDur)
299 }
300
301 // EwmaIncrBy is a shorthand for b.EwmaIncrInt64(int64(n), iterDur).
302 func (b *Bar) EwmaIncrBy(n int, iterDur time.Duration) {
303 b.EwmaIncrInt64(int64(n), iterDur)
304 }
305
306 // EwmaIncrInt64 increments progress by amount of n and updates EWMA based
307 // decorators by dur of a single iteration.
308 func (b *Bar) EwmaIncrInt64(n int64, iterDur time.Duration) {
309 if n <= 0 {
310 return
311 }
312 select {
313 case b.operateState <- func(s *bState) {
314 s.decoratorEwmaUpdate(n, iterDur)
315 s.current += n
316 if s.triggerComplete && s.current >= s.total {
317 s.current = s.total
318 s.completed = true
319 b.triggerCompletion(s)
320 }
321 }:
322 case <-b.done:
239323 }
240324 }
241325
244328 // or after progress resume.
245329 func (b *Bar) DecoratorAverageAdjust(start time.Time) {
246330 select {
247 case b.operateState <- func(s *bState) {
248 for _, d := range s.averageDecorators {
249 d.AverageAdjust(start)
250 }
251 }:
331 case b.operateState <- func(s *bState) { s.decoratorAverageAdjust(start) }:
252332 case <-b.done:
253333 }
254334 }
257337 // priority, i.e. bar will be on top. If you don't need to set priority
258338 // dynamically, better use BarPriority option.
259339 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.
340 b.container.UpdateBarPriority(b, priority, false)
341 }
342
343 // Abort interrupts bar's running goroutine. Abort won't be engaged
344 // if bar is already in complete state. If drop is true bar will be
345 // removed as well. To make sure that bar has been removed call
346 // (*Bar).Wait method.
270347 func (b *Bar) Abort(drop bool) {
271348 select {
272 case <-b.done:
273 default:
274 if drop {
275 b.container.dropBar(b)
276 }
277 b.cancel()
349 case b.operateState <- func(s *bState) {
350 if s.completed || s.aborted {
351 return
352 }
353 s.aborted = true
354 s.rmOnComplete = drop
355 b.triggerCompletion(s)
356 }:
357 case <-b.done:
358 }
359 }
360
361 // Aborted reports whether the bar is in aborted state.
362 func (b *Bar) Aborted() bool {
363 result := make(chan bool)
364 select {
365 case b.operateState <- func(s *bState) { result <- s.aborted }:
366 return <-result
367 case <-b.done:
368 return b.bs.aborted
278369 }
279370 }
280371
281372 // Completed reports whether the bar is in completed state.
282373 func (b *Bar) Completed() bool {
283 select {
284 case b.operateState <- func(s *bState) { b.completed <- s.completed }:
285 return <-b.completed
286 case <-b.done:
287 return true
288 }
289 }
290
291 func (b *Bar) serve(ctx context.Context, s *bState) {
374 result := make(chan bool)
375 select {
376 case b.operateState <- func(s *bState) { result <- s.completed }:
377 return <-result
378 case <-b.done:
379 return b.bs.completed
380 }
381 }
382
383 // IsRunning reports whether the bar is running, i.e. not yet completed
384 // and not yet aborted.
385 func (b *Bar) IsRunning() bool {
386 result := make(chan bool)
387 select {
388 case b.operateState <- func(s *bState) { result <- !s.completed && !s.aborted }:
389 return <-result
390 case <-b.done:
391 return false
392 }
393 }
394
395 // Wait blocks until bar is completed or aborted.
396 func (b *Bar) Wait() {
397 <-b.done
398 }
399
400 func (b *Bar) serve(ctx context.Context, bs *bState) {
292401 defer b.container.bwg.Done()
293402 for {
294403 select {
295404 case op := <-b.operateState:
296 op(s)
405 op(bs)
297406 case <-ctx.Done():
298 b.cacheState = s
407 bs.aborted = !bs.completed
408 bs.decoratorShutdownNotify()
409 b.bs = bs
299410 close(b.done)
300 // Notifying decorators about shutdown event
301 for _, sl := range s.shutdownListeners {
302 sl.Shutdown()
411 return
412 }
413 }
414 }
415
416 func (b *Bar) render(tw int) {
417 var done bool
418 fn := func(s *bState) {
419 var rows []io.Reader
420 stat := newStatistics(tw, s)
421 r, err := s.draw(stat)
422 if err != nil {
423 b.frameCh <- &renderFrame{err: err}
424 return
425 }
426 rows = append(rows, r)
427 if s.extender != nil {
428 rows, err = s.extender(rows, stat)
429 if err != nil {
430 b.frameCh <- &renderFrame{err: err}
431 return
303432 }
304 return
305 }
306 }
307 }
308
309 func (b *Bar) render(tw int) {
310 select {
311 case b.operateState <- func(s *bState) {
312 stat := newStatistics(tw, s)
313 defer func() {
314 // recovering if user defined decorator panics for example
315 if p := recover(); p != nil {
316 if b.recoveredPanic == nil {
317 s.extender = makePanicExtender(p)
318 b.toShutdown = !b.toShutdown
319 b.recoveredPanic = p
320 }
321 frame, lines := s.extender(nil, s.reqWidth, stat)
322 b.extendedLines = lines
323 b.frameCh <- frame
324 b.dlogger.Println(p)
433 }
434 frame := &renderFrame{
435 rows: rows,
436 shutdown: s.shutdown,
437 rmOnComplete: s.rmOnComplete,
438 noPop: s.noPop,
439 done: done,
440 }
441 if s.completed || s.aborted {
442 // post increment makes sure OnComplete decorators are rendered
443 s.shutdown++
444 }
445 b.frameCh <- frame
446 }
447 select {
448 case b.operateState <- fn:
449 case <-b.done:
450 done = true
451 fn(b.bs)
452 }
453 }
454
455 func (b *Bar) triggerCompletion(s *bState) {
456 if s.autoRefresh {
457 // Technically this call isn't required, but if refresh rate is set to
458 // one hour for example and bar completes within a few minutes p.Wait()
459 // will wait for one hour. This call helps to avoid unnecessary waiting.
460 go b.tryEarlyRefresh(s.renderReq)
461 } else {
462 b.cancel()
463 }
464 }
465
466 func (b *Bar) tryEarlyRefresh(renderReq chan<- time.Time) {
467 var anyOtherRunning bool
468 b.container.traverseBars(func(bar *Bar) bool {
469 anyOtherRunning = b != bar && bar.IsRunning()
470 return anyOtherRunning
471 })
472 if !anyOtherRunning {
473 for {
474 select {
475 case renderReq <- time.Now():
476 case <-b.done:
477 return
325478 }
326 s.completeFlushed = s.completed
327 }()
328 frame, lines := s.extender(s.draw(stat), s.reqWidth, stat)
329 b.extendedLines = lines
330 b.toShutdown = s.completed && !s.completeFlushed
331 b.frameCh <- frame
332 }:
333 case <-b.done:
334 s := b.cacheState
335 stat := newStatistics(tw, s)
336 var r io.Reader
337 if b.recoveredPanic == nil {
338 r = s.draw(stat)
339 }
340 frame, lines := s.extender(r, s.reqWidth, stat)
341 b.extendedLines = lines
342 b.frameCh <- frame
343 }
344 }
345
346 func (b *Bar) subscribeDecorators() {
347 var averageDecorators []decor.AverageDecorator
348 var ewmaDecorators []decor.EwmaDecorator
349 var shutdownListeners []decor.ShutdownListener
350 b.TraverseDecorators(func(d decor.Decorator) {
351 if d, ok := d.(decor.AverageDecorator); ok {
352 averageDecorators = append(averageDecorators, d)
353 }
354 if d, ok := d.(decor.EwmaDecorator); ok {
355 ewmaDecorators = append(ewmaDecorators, d)
356 }
357 if d, ok := d.(decor.ShutdownListener); ok {
358 shutdownListeners = append(shutdownListeners, d)
359 }
360 })
361 select {
362 case b.operateState <- func(s *bState) {
363 s.averageDecorators = averageDecorators
364 s.ewmaDecorators = ewmaDecorators
365 s.shutdownListeners = shutdownListeners
366 }:
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
378 }
379 }
380 }
381
382 func (b *Bar) wSyncTable() [][]chan int {
383 select {
384 case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
385 return <-b.syncTableCh
386 case <-b.done:
387 return b.cacheState.wSyncTable()
388 }
389 }
390
391 func (s *bState) draw(stat decor.Statistics) io.Reader {
392 nlr := strings.NewReader("\n")
393 tw := stat.AvailableWidth
394 for _, d := range s.pDecorators {
395 str := d.Decor(stat)
396 stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
397 s.bufP.WriteString(str)
398 }
399 if stat.AvailableWidth < 1 {
400 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufP.String()), tw, "…"))
401 s.bufP.Reset()
402 return io.MultiReader(trunc, nlr)
403 }
404
405 if !s.trimSpace && stat.AvailableWidth > 1 {
479 }
480 }
481 }
482
483 func (b *Bar) wSyncTable() syncTable {
484 result := make(chan syncTable)
485 select {
486 case b.operateState <- func(s *bState) { result <- s.wSyncTable() }:
487 return <-result
488 case <-b.done:
489 return b.bs.wSyncTable()
490 }
491 }
492
493 func (s *bState) draw(stat decor.Statistics) (io.Reader, error) {
494 r, err := s.drawImpl(stat)
495 if err != nil {
496 for _, b := range s.buffers {
497 b.Reset()
498 }
499 return nil, err
500 }
501 return io.MultiReader(r, strings.NewReader("\n")), nil
502 }
503
504 func (s *bState) drawImpl(stat decor.Statistics) (io.Reader, error) {
505 decorFiller := func(buf *bytes.Buffer, decorators []decor.Decorator) (err error) {
506 for _, d := range decorators {
507 // need to call Decor in any case becase of width synchronization
508 str, width := d.Decor(stat)
509 if err != nil {
510 continue
511 }
512 if w := stat.AvailableWidth - width; w >= 0 {
513 _, err = buf.WriteString(str)
514 stat.AvailableWidth = w
515 } else if stat.AvailableWidth > 0 {
516 trunc := runewidth.Truncate(stripansi.Strip(str), stat.AvailableWidth, "…")
517 _, err = buf.WriteString(trunc)
518 stat.AvailableWidth = 0
519 }
520 }
521 return err
522 }
523
524 bufP, bufB, bufA := s.buffers[0], s.buffers[1], s.buffers[2]
525
526 err := eitherError(decorFiller(bufP, s.pDecorators), decorFiller(bufA, s.aDecorators))
527 if err != nil {
528 return nil, err
529 }
530
531 if !s.trimSpace && stat.AvailableWidth >= 2 {
406532 stat.AvailableWidth -= 2
407 s.bufB.WriteByte(' ')
408 defer s.bufB.WriteByte(' ')
409 }
410
411 tw = stat.AvailableWidth
412 for _, d := range s.aDecorators {
413 str := d.Decor(stat)
414 stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
415 s.bufA.WriteString(str)
416 }
417 if stat.AvailableWidth < 1 {
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)
426 }
427
428 func (s *bState) wSyncTable() [][]chan int {
429 columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
430 var pCount int
431 for _, d := range s.pDecorators {
432 if ch, ok := d.Sync(); ok {
433 columns = append(columns, ch)
434 pCount++
435 }
436 }
437 var aCount int
438 for _, d := range s.aDecorators {
439 if ch, ok := d.Sync(); ok {
440 columns = append(columns, ch)
441 aCount++
442 }
443 }
444 table := make([][]chan int, 2)
445 table[0] = columns[0:pCount]
446 table[1] = columns[pCount : pCount+aCount : pCount+aCount]
533 writeFiller := func(buf *bytes.Buffer) error {
534 return s.filler.Fill(buf, stat)
535 }
536 for _, fn := range []func(*bytes.Buffer) error{
537 writeSpace,
538 writeFiller,
539 writeSpace,
540 } {
541 if err := fn(bufB); err != nil {
542 return nil, err
543 }
544 }
545 } else {
546 err := s.filler.Fill(bufB, stat)
547 if err != nil {
548 return nil, err
549 }
550 }
551
552 return io.MultiReader(bufP, bufB, bufA), nil
553 }
554
555 func (s *bState) wSyncTable() (table syncTable) {
556 var count int
557 var row []chan int
558
559 for i, decorators := range [][]decor.Decorator{
560 s.pDecorators,
561 s.aDecorators,
562 } {
563 for _, d := range decorators {
564 if ch, ok := d.Sync(); ok {
565 row = append(row, ch)
566 count++
567 }
568 }
569 switch i {
570 case 0:
571 table[i] = row[0:count]
572 default:
573 table[i] = row[len(table[i-1]):count]
574 }
575 }
447576 return table
577 }
578
579 func (s bState) decoratorEwmaUpdate(n int64, dur time.Duration) {
580 var wg sync.WaitGroup
581 for i := 0; i < len(s.ewmaDecorators); i++ {
582 switch d := s.ewmaDecorators[i]; i {
583 case len(s.ewmaDecorators) - 1:
584 d.EwmaUpdate(n, dur)
585 default:
586 wg.Add(1)
587 go func() {
588 d.EwmaUpdate(n, dur)
589 wg.Done()
590 }()
591 }
592 }
593 wg.Wait()
594 }
595
596 func (s bState) decoratorAverageAdjust(start time.Time) {
597 var wg sync.WaitGroup
598 for i := 0; i < len(s.averageDecorators); i++ {
599 switch d := s.averageDecorators[i]; i {
600 case len(s.averageDecorators) - 1:
601 d.AverageAdjust(start)
602 default:
603 wg.Add(1)
604 go func() {
605 d.AverageAdjust(start)
606 wg.Done()
607 }()
608 }
609 }
610 wg.Wait()
611 }
612
613 func (s bState) decoratorShutdownNotify() {
614 var wg sync.WaitGroup
615 for i := 0; i < len(s.shutdownListeners); i++ {
616 switch d := s.shutdownListeners[i]; i {
617 case len(s.shutdownListeners) - 1:
618 d.OnShutdown()
619 default:
620 wg.Add(1)
621 go func() {
622 d.OnShutdown()
623 wg.Done()
624 }()
625 }
626 }
627 wg.Wait()
448628 }
449629
450630 func newStatistics(tw int, s *bState) decor.Statistics {
451631 return decor.Statistics{
632 AvailableWidth: tw,
633 RequestedWidth: s.reqWidth,
452634 ID: s.id,
453 AvailableWidth: tw,
454635 Total: s.total,
455636 Current: s.current,
456637 Refill: s.refill,
457 Completed: s.completeFlushed,
458 }
459 }
460
461 func extractBaseDecorator(d decor.Decorator) decor.Decorator {
638 Completed: s.completed,
639 Aborted: s.aborted,
640 }
641 }
642
643 func unwrap(d decor.Decorator) decor.Decorator {
462644 if d, ok := d.(decor.Wrapper); ok {
463 return extractBaseDecorator(d.Base())
645 return unwrap(d.Unwrap())
464646 }
465647 return d
466648 }
467649
468 func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) {
469 if !done && !s.iterated {
470 panic("increment required before ewma iteration update")
471 } else {
472 s.iterated = false
473 }
474 for _, d := range s.ewmaDecorators {
475 d.EwmaUpdate(s.lastN, dur)
476 }
477 }
478
479 func makePanicExtender(p interface{}) extenderFunc {
480 pstr := fmt.Sprint(p)
481 stack := debug.Stack()
482 stackLines := bytes.Count(stack, []byte("\n"))
483 return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) {
484 mr := io.MultiReader(
485 strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")),
486 strings.NewReader(fmt.Sprintf("\n%#v\n", st)),
487 bytes.NewReader(stack),
488 )
489 return mr, stackLines + 1
490 }
491 }
650 func writeSpace(buf *bytes.Buffer) error {
651 return buf.WriteByte(' ')
652 }
653
654 func eitherError(errors ...error) error {
655 for _, err := range errors {
656 if err != nil {
657 return err
658 }
659 }
660 return nil
661 }
22 import (
33 "io"
44
5 "github.com/vbauerster/mpb/v7/decor"
5 "github.com/vbauerster/mpb/v8/decor"
66 )
77
88 // BarFiller interface.
99 // Bar (without decorators) renders itself by calling BarFiller's Fill method.
10 //
11 // reqWidth is requested width, set by `func WithWidth(int) ContainerOption`.
12 // If not set, it defaults to terminal width.
13 //
14 // Default implementations can be obtained via:
15 //
16 // func NewBarFiller(BarStyle()) BarFiller
17 // func NewBarFiller(SpinnerStyle()) BarFiller
18 //
1910 type BarFiller interface {
20 Fill(w io.Writer, reqWidth int, stat decor.Statistics)
11 Fill(io.Writer, decor.Statistics) error
2112 }
2213
2314 // BarFillerBuilder interface.
15 // Default implementations are:
16 //
17 // BarStyle()
18 // SpinnerStyle()
19 // NopStyle()
2420 type BarFillerBuilder interface {
2521 Build() BarFiller
2622 }
2723
28 // BarFillerFunc is function type adapter to convert function into BarFiller.
29 type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
24 // BarFillerFunc is function type adapter to convert compatible function
25 // into BarFiller interface.
26 type BarFillerFunc func(io.Writer, decor.Statistics) error
3027
31 func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
32 f(w, reqWidth, stat)
28 func (f BarFillerFunc) Fill(w io.Writer, stat decor.Statistics) error {
29 return f(w, stat)
3330 }
34
35 // NewBarFiller constructs a BarFiller from provided BarFillerBuilder.
36 func NewBarFiller(b BarFillerBuilder) BarFiller {
37 return b.Build()
38 }
22 import (
33 "io"
44
5 "github.com/acarl005/stripansi"
65 "github.com/mattn/go-runewidth"
7 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v7/internal"
6 "github.com/vbauerster/mpb/v8/decor"
7 "github.com/vbauerster/mpb/v8/internal"
98 )
109
1110 const (
1211 iLbound = iota
1312 iRbound
13 iRefiller
1414 iFiller
15 iRefiller
15 iTip
1616 iPadding
1717 components
1818 )
19
20 var defaultBarStyle = [components]string{"[", "]", "+", "=", ">", "-"}
1921
2022 // BarStyleComposer interface.
2123 type BarStyleComposer interface {
2224 BarFillerBuilder
2325 Lbound(string) BarStyleComposer
26 LboundMeta(func(string) string) BarStyleComposer
2427 Rbound(string) BarStyleComposer
28 RboundMeta(func(string) string) BarStyleComposer
2529 Filler(string) BarStyleComposer
30 FillerMeta(func(string) string) BarStyleComposer
2631 Refiller(string) BarStyleComposer
32 RefillerMeta(func(string) string) BarStyleComposer
2733 Padding(string) BarStyleComposer
28 Tip(...string) BarStyleComposer
34 PaddingMeta(func(string) string) BarStyleComposer
35 Tip(frames ...string) BarStyleComposer
36 TipMeta(func(string) string) BarStyleComposer
37 TipOnComplete() BarStyleComposer
2938 Reverse() BarStyleComposer
30 }
31
32 type bFiller struct {
33 components [components]*component
34 tip struct {
35 count uint
36 frames []*component
37 }
38 flush func(dst io.Writer, filling, padding [][]byte)
3939 }
4040
4141 type component struct {
4343 bytes []byte
4444 }
4545
46 type flushSection struct {
47 meta func(io.Writer, []byte) error
48 bytes []byte
49 }
50
51 type bFiller struct {
52 components [components]component
53 meta [components]func(io.Writer, []byte) error
54 flush func(io.Writer, ...flushSection) error
55 tipOnComplete bool
56 tip struct {
57 frames []component
58 count uint
59 }
60 }
61
4662 type barStyle struct {
47 lbound string
48 rbound string
49 filler string
50 refiller string
51 padding string
52 tip []string
53 rev bool
63 style [components]string
64 metaFuncs [components]func(io.Writer, []byte) error
65 tipFrames []string
66 tipOnComplete bool
67 rev bool
5468 }
5569
5670 // BarStyle constructs default bar style which can be altered via
5771 // BarStyleComposer interface.
5872 func BarStyle() BarStyleComposer {
59 return &barStyle{
60 lbound: "[",
61 rbound: "]",
62 filler: "=",
63 refiller: "+",
64 padding: "-",
65 tip: []string{">"},
66 }
67 }
68
69 func (s *barStyle) Lbound(bound string) BarStyleComposer {
70 s.lbound = bound
71 return s
72 }
73
74 func (s *barStyle) Rbound(bound string) BarStyleComposer {
75 s.rbound = bound
76 return s
77 }
78
79 func (s *barStyle) Filler(filler string) BarStyleComposer {
80 s.filler = filler
81 return s
82 }
83
84 func (s *barStyle) Refiller(refiller string) BarStyleComposer {
85 s.refiller = refiller
86 return s
87 }
88
89 func (s *barStyle) Padding(padding string) BarStyleComposer {
90 s.padding = padding
91 return s
92 }
93
94 func (s *barStyle) Tip(tip ...string) BarStyleComposer {
95 if len(tip) != 0 {
96 s.tip = append(s.tip[:0], tip...)
97 }
98 return s
99 }
100
101 func (s *barStyle) Reverse() BarStyleComposer {
73 bs := barStyle{
74 style: defaultBarStyle,
75 tipFrames: []string{defaultBarStyle[iTip]},
76 }
77 for i := range bs.metaFuncs {
78 bs.metaFuncs[i] = defaultMeta
79 }
80 return bs
81 }
82
83 func (s barStyle) Lbound(bound string) BarStyleComposer {
84 s.style[iLbound] = bound
85 return s
86 }
87
88 func (s barStyle) LboundMeta(fn func(string) string) BarStyleComposer {
89 s.metaFuncs[iLbound] = makeMetaFunc(fn)
90 return s
91 }
92
93 func (s barStyle) Rbound(bound string) BarStyleComposer {
94 s.style[iRbound] = bound
95 return s
96 }
97
98 func (s barStyle) RboundMeta(fn func(string) string) BarStyleComposer {
99 s.metaFuncs[iRbound] = makeMetaFunc(fn)
100 return s
101 }
102
103 func (s barStyle) Filler(filler string) BarStyleComposer {
104 s.style[iFiller] = filler
105 return s
106 }
107
108 func (s barStyle) FillerMeta(fn func(string) string) BarStyleComposer {
109 s.metaFuncs[iFiller] = makeMetaFunc(fn)
110 return s
111 }
112
113 func (s barStyle) Refiller(refiller string) BarStyleComposer {
114 s.style[iRefiller] = refiller
115 return s
116 }
117
118 func (s barStyle) RefillerMeta(fn func(string) string) BarStyleComposer {
119 s.metaFuncs[iRefiller] = makeMetaFunc(fn)
120 return s
121 }
122
123 func (s barStyle) Padding(padding string) BarStyleComposer {
124 s.style[iPadding] = padding
125 return s
126 }
127
128 func (s barStyle) PaddingMeta(fn func(string) string) BarStyleComposer {
129 s.metaFuncs[iPadding] = makeMetaFunc(fn)
130 return s
131 }
132
133 func (s barStyle) Tip(frames ...string) BarStyleComposer {
134 if len(frames) != 0 {
135 s.tipFrames = frames
136 }
137 return s
138 }
139
140 func (s barStyle) TipMeta(fn func(string) string) BarStyleComposer {
141 s.metaFuncs[iTip] = makeMetaFunc(fn)
142 return s
143 }
144
145 func (s barStyle) TipOnComplete() BarStyleComposer {
146 s.tipOnComplete = true
147 return s
148 }
149
150 func (s barStyle) Reverse() BarStyleComposer {
102151 s.rev = true
103152 return s
104153 }
105154
106 func (s *barStyle) Build() BarFiller {
107 bf := new(bFiller)
155 func (s barStyle) Build() BarFiller {
156 bf := &bFiller{
157 meta: s.metaFuncs,
158 tipOnComplete: s.tipOnComplete,
159 }
160 bf.components[iLbound] = component{
161 width: runewidth.StringWidth(s.style[iLbound]),
162 bytes: []byte(s.style[iLbound]),
163 }
164 bf.components[iRbound] = component{
165 width: runewidth.StringWidth(s.style[iRbound]),
166 bytes: []byte(s.style[iRbound]),
167 }
168 bf.components[iFiller] = component{
169 width: runewidth.StringWidth(s.style[iFiller]),
170 bytes: []byte(s.style[iFiller]),
171 }
172 bf.components[iRefiller] = component{
173 width: runewidth.StringWidth(s.style[iRefiller]),
174 bytes: []byte(s.style[iRefiller]),
175 }
176 bf.components[iPadding] = component{
177 width: runewidth.StringWidth(s.style[iPadding]),
178 bytes: []byte(s.style[iPadding]),
179 }
180 bf.tip.frames = make([]component, len(s.tipFrames))
181 for i, t := range s.tipFrames {
182 bf.tip.frames[i] = component{
183 width: runewidth.StringWidth(t),
184 bytes: []byte(t),
185 }
186 }
108187 if s.rev {
109 bf.flush = func(dst io.Writer, filling, padding [][]byte) {
110 flush(dst, padding, filling)
188 bf.flush = func(w io.Writer, sections ...flushSection) error {
189 for i := len(sections) - 1; i >= 0; i-- {
190 if s := sections[i]; len(s.bytes) != 0 {
191 err := s.meta(w, s.bytes)
192 if err != nil {
193 return err
194 }
195 }
196 }
197 return nil
111198 }
112199 } else {
113 bf.flush = flush
114 }
115 bf.components[iLbound] = &component{
116 width: runewidth.StringWidth(stripansi.Strip(s.lbound)),
117 bytes: []byte(s.lbound),
118 }
119 bf.components[iRbound] = &component{
120 width: runewidth.StringWidth(stripansi.Strip(s.rbound)),
121 bytes: []byte(s.rbound),
122 }
123 bf.components[iFiller] = &component{
124 width: runewidth.StringWidth(stripansi.Strip(s.filler)),
125 bytes: []byte(s.filler),
126 }
127 bf.components[iRefiller] = &component{
128 width: runewidth.StringWidth(stripansi.Strip(s.refiller)),
129 bytes: []byte(s.refiller),
130 }
131 bf.components[iPadding] = &component{
132 width: runewidth.StringWidth(stripansi.Strip(s.padding)),
133 bytes: []byte(s.padding),
134 }
135 bf.tip.frames = make([]*component, len(s.tip))
136 for i, t := range s.tip {
137 bf.tip.frames[i] = &component{
138 width: runewidth.StringWidth(stripansi.Strip(t)),
139 bytes: []byte(t),
200 bf.flush = func(w io.Writer, sections ...flushSection) error {
201 for _, s := range sections {
202 if len(s.bytes) != 0 {
203 err := s.meta(w, s.bytes)
204 if err != nil {
205 return err
206 }
207 }
208 }
209 return nil
140210 }
141211 }
142212 return bf
143213 }
144214
145 func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
146 width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
147 brackets := s.components[iLbound].width + s.components[iRbound].width
148 if width < brackets {
149 return
150 }
215 func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) error {
216 width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
151217 // don't count brackets as progress
152 width -= brackets
153
154 w.Write(s.components[iLbound].bytes)
155 defer w.Write(s.components[iRbound].bytes)
156
157 curWidth := int(internal.PercentageRound(stat.Total, stat.Current, width))
158 refWidth, filled := 0, curWidth
159 filling := make([][]byte, 0, curWidth)
160
161 if curWidth > 0 && curWidth != width {
162 tipFrame := s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
163 filling = append(filling, tipFrame.bytes)
164 curWidth -= tipFrame.width
165 s.tip.count++
166 }
167
168 if stat.Refill > 0 && curWidth > 0 {
169 refWidth = int(internal.PercentageRound(stat.Total, int64(stat.Refill), width))
170 if refWidth > curWidth {
171 refWidth = curWidth
172 }
173 curWidth -= refWidth
174 }
175
176 for curWidth > 0 && curWidth >= s.components[iFiller].width {
177 filling = append(filling, s.components[iFiller].bytes)
178 curWidth -= s.components[iFiller].width
179 if s.components[iFiller].width == 0 {
180 break
181 }
182 }
183
184 for refWidth > 0 && refWidth >= s.components[iRefiller].width {
185 filling = append(filling, s.components[iRefiller].bytes)
186 refWidth -= s.components[iRefiller].width
187 if s.components[iRefiller].width == 0 {
188 break
189 }
190 }
191
192 filled -= curWidth + refWidth
193 padWidth := width - filled
194 padding := make([][]byte, 0, padWidth)
195 for padWidth > 0 && padWidth >= s.components[iPadding].width {
196 padding = append(padding, s.components[iPadding].bytes)
197 padWidth -= s.components[iPadding].width
198 if s.components[iPadding].width == 0 {
199 break
200 }
201 }
202
203 for padWidth > 0 {
204 padding = append(padding, []byte("…"))
205 padWidth--
206 }
207
208 s.flush(w, filling, padding)
209 }
210
211 func flush(dst io.Writer, filling, padding [][]byte) {
212 for i := len(filling) - 1; i >= 0; i-- {
213 dst.Write(filling[i])
214 }
215 for i := 0; i < len(padding); i++ {
216 dst.Write(padding[i])
217 }
218 }
218 width -= (s.components[iLbound].width + s.components[iRbound].width)
219 if width < 0 {
220 return nil
221 }
222
223 err := s.meta[iLbound](w, s.components[iLbound].bytes)
224 if err != nil {
225 return err
226 }
227
228 if width == 0 {
229 return s.meta[iRbound](w, s.components[iRbound].bytes)
230 }
231
232 var tip component
233 var refilling, filling, padding []byte
234 var fillCount int
235 curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width)))
236
237 if curWidth != 0 {
238 if !stat.Completed || s.tipOnComplete {
239 tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
240 s.tip.count++
241 fillCount += tip.width
242 }
243 if stat.Refill != 0 {
244 refWidth := int(internal.PercentageRound(stat.Total, stat.Refill, uint(width)))
245 curWidth -= refWidth
246 refWidth += curWidth
247 for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w {
248 filling = append(filling, s.components[iFiller].bytes...)
249 }
250 for w := s.components[iRefiller].width; refWidth-fillCount >= w; fillCount += w {
251 refilling = append(refilling, s.components[iRefiller].bytes...)
252 }
253 } else {
254 for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w {
255 filling = append(filling, s.components[iFiller].bytes...)
256 }
257 }
258 }
259
260 for w := s.components[iPadding].width; width-fillCount >= w; fillCount += w {
261 padding = append(padding, s.components[iPadding].bytes...)
262 }
263
264 for w := 1; width-fillCount >= w; fillCount += w {
265 padding = append(padding, "…"...)
266 }
267
268 err = s.flush(w,
269 flushSection{s.meta[iRefiller], refilling},
270 flushSection{s.meta[iFiller], filling},
271 flushSection{s.meta[iTip], tip.bytes},
272 flushSection{s.meta[iPadding], padding},
273 )
274 if err != nil {
275 return err
276 }
277 return s.meta[iRbound](w, s.components[iRbound].bytes)
278 }
279
280 func makeMetaFunc(fn func(string) string) func(io.Writer, []byte) error {
281 return func(w io.Writer, p []byte) (err error) {
282 _, err = io.WriteString(w, fn(string(p)))
283 return err
284 }
285 }
286
287 func defaultMeta(w io.Writer, p []byte) (err error) {
288 _, err = w.Write(p)
289 return err
290 }
0 package mpb
1
2 import (
3 "io"
4
5 "github.com/vbauerster/mpb/v8/decor"
6 )
7
8 // barFillerBuilderFunc is function type adapter to convert compatible
9 // function into BarFillerBuilder interface.
10 type barFillerBuilderFunc func() BarFiller
11
12 func (f barFillerBuilderFunc) Build() BarFiller {
13 return f()
14 }
15
16 // NopStyle provides BarFillerBuilder which builds NOP BarFiller.
17 func NopStyle() BarFillerBuilder {
18 return barFillerBuilderFunc(func() BarFiller {
19 return BarFillerFunc(func(io.Writer, decor.Statistics) error {
20 return nil
21 })
22 })
23 }
33 "io"
44 "strings"
55
6 "github.com/acarl005/stripansi"
76 "github.com/mattn/go-runewidth"
8 "github.com/vbauerster/mpb/v7/decor"
9 "github.com/vbauerster/mpb/v7/internal"
7 "github.com/vbauerster/mpb/v8/decor"
8 "github.com/vbauerster/mpb/v8/internal"
109 )
1110
1211 const (
1413 positionRight
1514 )
1615
16 var defaultSpinnerStyle = [...]string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
17
1718 // SpinnerStyleComposer interface.
1819 type SpinnerStyleComposer interface {
1920 BarFillerBuilder
2021 PositionLeft() SpinnerStyleComposer
2122 PositionRight() SpinnerStyleComposer
23 Meta(func(string) string) SpinnerStyleComposer
2224 }
2325
2426 type sFiller struct {
27 frames []string
2528 count uint
26 position uint
27 frames []string
29 meta func(string) string
30 position func(string, int) string
2831 }
2932
3033 type spinnerStyle struct {
3134 position uint
3235 frames []string
36 meta func(string) string
3337 }
3438
3539 // SpinnerStyle constructs default spinner style which can be altered via
3640 // SpinnerStyleComposer interface.
3741 func SpinnerStyle(frames ...string) SpinnerStyleComposer {
38 ss := new(spinnerStyle)
42 ss := spinnerStyle{
43 meta: func(s string) string { return s },
44 }
3945 if len(frames) != 0 {
40 ss.frames = append(ss.frames, frames...)
46 ss.frames = frames
4147 } else {
42 ss.frames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
48 ss.frames = defaultSpinnerStyle[:]
4349 }
4450 return ss
4551 }
4652
47 func (s *spinnerStyle) PositionLeft() SpinnerStyleComposer {
53 func (s spinnerStyle) PositionLeft() SpinnerStyleComposer {
4854 s.position = positionLeft
4955 return s
5056 }
5157
52 func (s *spinnerStyle) PositionRight() SpinnerStyleComposer {
58 func (s spinnerStyle) PositionRight() SpinnerStyleComposer {
5359 s.position = positionRight
5460 return s
5561 }
5662
57 func (s *spinnerStyle) Build() BarFiller {
63 func (s spinnerStyle) Meta(fn func(string) string) SpinnerStyleComposer {
64 s.meta = fn
65 return s
66 }
67
68 func (s spinnerStyle) Build() BarFiller {
5869 sf := &sFiller{
59 position: s.position,
60 frames: s.frames,
70 frames: s.frames,
71 meta: s.meta,
72 }
73 switch s.position {
74 case positionLeft:
75 sf.position = func(frame string, padWidth int) string {
76 return frame + strings.Repeat(" ", padWidth)
77 }
78 case positionRight:
79 sf.position = func(frame string, padWidth int) string {
80 return strings.Repeat(" ", padWidth) + frame
81 }
82 default:
83 sf.position = func(frame string, padWidth int) string {
84 return strings.Repeat(" ", padWidth/2) + frame + strings.Repeat(" ", padWidth/2+padWidth%2)
85 }
6186 }
6287 return sf
6388 }
6489
65 func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
66 width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
67
90 func (s *sFiller) Fill(w io.Writer, stat decor.Statistics) error {
91 width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
6892 frame := s.frames[s.count%uint(len(s.frames))]
69 frameWidth := runewidth.StringWidth(stripansi.Strip(frame))
93 frameWidth := runewidth.StringWidth(frame)
94 s.count++
7095
7196 if width < frameWidth {
72 return
97 return nil
7398 }
7499
75 rest := width - frameWidth
76 switch s.position {
77 case positionLeft:
78 io.WriteString(w, frame+strings.Repeat(" ", rest))
79 case positionRight:
80 io.WriteString(w, strings.Repeat(" ", rest)+frame)
81 default:
82 str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
83 io.WriteString(w, str)
84 }
85 s.count++
100 _, err := io.WriteString(w, s.position(s.meta(frame), width-frameWidth))
101 return err
86102 }
33 "bytes"
44 "io"
55
6 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v7/internal"
6 "github.com/vbauerster/mpb/v8/decor"
87 )
98
109 // BarOption is a func option to alter default behavior of a bar.
1110 type BarOption func(*bState)
1211
13 func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
14 type mergeWrapper interface {
15 MergeUnwrap() []decor.Decorator
16 }
12 func inspect(decorators []decor.Decorator) (dest []decor.Decorator) {
1713 for _, decorator := range decorators {
18 if mw, ok := decorator.(mergeWrapper); ok {
19 *dest = append(*dest, mw.MergeUnwrap()...)
20 }
21 *dest = append(*dest, decorator)
22 }
14 if decorator == nil {
15 continue
16 }
17 dest = append(dest, decorator)
18 }
19 return
2320 }
2421
2522 // AppendDecorators let you inject decorators to the bar's right side.
2623 func AppendDecorators(decorators ...decor.Decorator) BarOption {
27 return func(s *bState) {
28 s.addDecorators(&s.aDecorators, decorators...)
24 decorators = inspect(decorators)
25 return func(s *bState) {
26 s.aDecorators = decorators
2927 }
3028 }
3129
3230 // PrependDecorators let you inject decorators to the bar's left side.
3331 func PrependDecorators(decorators ...decor.Decorator) BarOption {
34 return func(s *bState) {
35 s.addDecorators(&s.pDecorators, decorators...)
32 decorators = inspect(decorators)
33 return func(s *bState) {
34 s.pDecorators = decorators
3635 }
3736 }
3837
5049 }
5150 }
5251
53 // BarQueueAfter queues this (being constructed) bar to relplace
54 // runningBar after it has been completed.
55 func BarQueueAfter(runningBar *Bar) BarOption {
56 if runningBar == nil {
57 return nil
58 }
59 return func(s *bState) {
60 s.runningBar = runningBar
52 // BarQueueAfter puts this (being constructed) bar into the queue.
53 // BarPriority will be inherited from the argument bar.
54 // When argument bar completes or aborts queued bar replaces its place.
55 func BarQueueAfter(bar *Bar) BarOption {
56 return func(s *bState) {
57 s.waitBar = bar
6158 }
6259 }
6360
6562 // on complete event.
6663 func BarRemoveOnComplete() BarOption {
6764 return func(s *bState) {
68 s.dropOnComplete = true
65 s.rmOnComplete = true
6966 }
7067 }
7168
7875 // BarFillerOnComplete replaces bar's filler with message, on complete event.
7976 func BarFillerOnComplete(message string) BarOption {
8077 return BarFillerMiddleware(func(base BarFiller) BarFiller {
81 return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
78 return BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
8279 if st.Completed {
83 io.WriteString(w, message)
84 } else {
85 base.Fill(w, reqWidth, st)
80 _, err := io.WriteString(w, message)
81 return err
8682 }
83 return base.Fill(w, st)
8784 })
8885 })
8986 }
9087
9188 // BarFillerMiddleware provides a way to augment the underlying BarFiller.
9289 func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption {
93 return func(s *bState) {
94 s.middleware = middle
90 if middle == nil {
91 return nil
92 }
93 return func(s *bState) {
94 s.filler = middle(s.filler)
9595 }
9696 }
9797
9898 // BarPriority sets bar's priority. Zero is highest priority, i.e. bar
99 // will be on top. If `BarReplaceOnComplete` option is supplied, this
100 // option is ignored.
99 // will be on top. This option isn't effective with `BarQueueAfter` option.
101100 func BarPriority(priority int) BarOption {
102101 return func(s *bState) {
103102 s.priority = priority
104103 }
105104 }
106105
107 // BarExtender provides a way to extend bar to the next new line.
108 func BarExtender(filler BarFiller) BarOption {
106 // BarExtender extends bar with arbitrary lines. Provided BarFiller will be
107 // called at each render/flush cycle. Any lines written to the underlying
108 // io.Writer will extend the bar either in above (rev = true) or below
109 // (rev = false) direction.
110 func BarExtender(filler BarFiller, rev bool) BarOption {
109111 if filler == nil {
110112 return nil
111113 }
112 return func(s *bState) {
113 s.extender = makeExtenderFunc(filler)
114 }
115 }
116
117 func makeExtenderFunc(filler BarFiller) extenderFunc {
114 fn := makeExtenderFunc(filler, rev)
115 return func(s *bState) {
116 s.extender = fn
117 }
118 }
119
120 func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc {
118121 buf := new(bytes.Buffer)
119 return func(r io.Reader, reqWidth int, st decor.Statistics) (io.Reader, int) {
120 filler.Fill(buf, reqWidth, st)
121 return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), []byte("\n"))
122 base := func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
123 err := filler.Fill(buf, stat)
124 if err != nil {
125 buf.Reset()
126 return rows, err
127 }
128 for {
129 b, err := buf.ReadBytes('\n')
130 if err != nil {
131 break
132 }
133 rows = append(rows, bytes.NewReader(b))
134 }
135 buf.Reset()
136 return rows, err
137 }
138
139 if !rev {
140 return base
141 }
142 return func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
143 rows, err := base(rows, stat)
144 if err != nil {
145 return rows, err
146 }
147 for left, right := 0, len(rows)-1; left < right; left, right = left+1, right-1 {
148 rows[left], rows[right] = rows[right], rows[left]
149 }
150 return rows, err
122151 }
123152 }
124153
137166 }
138167 }
139168
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))
143 }
144
145 // BarOptOn will invoke provided option only when higher order predicate
146 // evaluates to true.
169 // BarOptional will return provided option only when cond is true.
170 func BarOptional(option BarOption, cond bool) BarOption {
171 if cond {
172 return option
173 }
174 return nil
175 }
176
177 // BarOptOn will return provided option only when predicate evaluates to true.
147178 func BarOptOn(option BarOption, predicate func() bool) BarOption {
148179 if predicate() {
149180 return option
150181 }
151182 return nil
152183 }
184
185 // BarFuncOptional will call option and return its value only when cond is true.
186 func BarFuncOptional(option func() BarOption, cond bool) BarOption {
187 if cond {
188 return option()
189 }
190 return nil
191 }
192
193 // BarFuncOptOn will call option and return its value only when predicate evaluates to true.
194 func BarFuncOptOn(option func() BarOption, predicate func() bool) BarOption {
195 if predicate() {
196 return option()
197 }
198 return nil
199 }
11
22 import (
33 "bytes"
4 "context"
45 "fmt"
5 "io/ioutil"
6 "io"
67 "strings"
7 "sync/atomic"
88 "testing"
99 "time"
1010 "unicode/utf8"
1111
12 "github.com/vbauerster/mpb/v7"
13 "github.com/vbauerster/mpb/v7/decor"
12 "github.com/vbauerster/mpb/v8"
13 "github.com/vbauerster/mpb/v8/decor"
1414 )
1515
1616 func TestBarCompleted(t *testing.T) {
17 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(ioutil.Discard))
17 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
1818 total := 80
1919 bar := p.AddBar(int64(total))
2020
21 var count int
22 for !bar.Completed() {
23 time.Sleep(10 * time.Millisecond)
24 bar.Increment()
25 count++
26 }
27
28 p.Wait()
29 if count != total {
30 t.Errorf("got count: %d, expected %d\n", count, total)
31 }
21 if bar.Completed() {
22 t.Fail()
23 }
24
25 bar.IncrBy(total)
26
27 if !bar.Completed() {
28 t.Error("bar isn't completed after increment")
29 }
30
31 p.Wait()
32 }
33
34 func TestBarAborted(t *testing.T) {
35 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
36 total := 80
37 bar := p.AddBar(int64(total))
38
39 if bar.Aborted() {
40 t.Fail()
41 }
42
43 bar.Abort(false)
44
45 if !bar.Aborted() {
46 t.Error("bar isn't aborted after abort call")
47 }
48
49 p.Wait()
50 }
51
52 func TestBarSetTotal(t *testing.T) {
53 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
54 bar := p.AddBar(0)
55
56 bar.SetTotal(0, false)
57 if bar.Completed() {
58 t.Error("expected bar not to complete")
59 }
60
61 bar.SetTotal(0, true)
62 if !bar.Completed() {
63 t.Error("expected bar to complete")
64 }
65
66 p.Wait()
67 }
68
69 func TestBarEnableTriggerCompleteAndIncrementBefore(t *testing.T) {
70 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
71 bar := p.AddBar(0) // never complete bar
72
73 for _, f := range []func(){
74 func() { bar.SetTotal(40, false) },
75 func() { bar.IncrBy(60) },
76 func() { bar.SetTotal(80, false) },
77 func() { bar.IncrBy(20) },
78 } {
79 f()
80 if bar.Completed() {
81 t.Fail()
82 }
83 }
84
85 bar.EnableTriggerComplete()
86
87 if !bar.Completed() {
88 t.Fail()
89 }
90
91 p.Wait()
92 }
93
94 func TestBarEnableTriggerCompleteAndIncrementAfter(t *testing.T) {
95 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
96 bar := p.AddBar(0) // never complete bar
97
98 for _, f := range []func(){
99 func() { bar.SetTotal(40, false) },
100 func() { bar.IncrBy(60) },
101 func() { bar.SetTotal(80, false) },
102 func() { bar.EnableTriggerComplete() },
103 } {
104 f()
105 if bar.Completed() {
106 t.Fail()
107 }
108 }
109
110 bar.IncrBy(20)
111
112 if !bar.Completed() {
113 t.Fail()
114 }
115
116 p.Wait()
32117 }
33118
34119 func TestBarID(t *testing.T) {
35 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(ioutil.Discard))
120 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
36121 total := 100
37122 wantID := 11
38123 bar := p.AddBar(int64(total), mpb.BarID(wantID))
39124
40 go func() {
41 for i := 0; i < total; i++ {
42 time.Sleep(50 * time.Millisecond)
43 bar.Increment()
44 }
45 }()
46
47125 gotID := bar.ID()
48126 if gotID != wantID {
49 t.Errorf("Expected bar id: %d, got %d\n", wantID, gotID)
50 }
51
52 bar.Abort(true)
127 t.Errorf("Expected bar id: %d, got %d", wantID, gotID)
128 }
129
130 bar.IncrBy(total)
131
53132 p.Wait()
54133 }
55134
56135 func TestBarSetRefill(t *testing.T) {
57136 var buf bytes.Buffer
58
59 p := mpb.New(mpb.WithOutput(&buf), mpb.WithWidth(100))
137 p := mpb.New(
138 mpb.WithWidth(100),
139 mpb.WithOutput(&buf),
140 mpb.WithAutoRefresh(),
141 )
60142
61143 total := 100
62144 till := 30
63145 refiller := "+"
64146
65 bar := p.Add(int64(total), mpb.NewBarFiller(mpb.BarStyle().Refiller(refiller)), mpb.BarFillerTrim())
66
147 bar := p.New(int64(total), mpb.BarStyle().Refiller(refiller), mpb.BarFillerTrim())
148
149 bar.IncrBy(till)
67150 bar.SetRefill(int64(till))
68 bar.IncrBy(till)
69
70 for i := 0; i < total-till; i++ {
71 bar.Increment()
72 time.Sleep(10 * time.Millisecond)
73 }
151 bar.IncrBy(total - till)
74152
75153 p.Wait()
76154
79157 strings.Repeat("=", total-till-1),
80158 )
81159
82 got := string(getLastLine(buf.Bytes()))
160 got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
83161
84162 if !strings.Contains(got, wantBar) {
85 t.Errorf("Want bar: %q, got bar: %q\n", wantBar, got)
86 }
87 }
88
89 func TestBarHas100PercentWithOnCompleteDecorator(t *testing.T) {
90 var buf bytes.Buffer
91
92 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(&buf))
93
94 total := 50
95
96 bar := p.AddBar(int64(total),
97 mpb.AppendDecorators(
98 decor.OnComplete(
99 decor.Percentage(), "done",
100 ),
101 ),
102 )
103
104 for i := 0; i < total; i++ {
105 bar.Increment()
106 time.Sleep(10 * time.Millisecond)
107 }
108
109 p.Wait()
110
111 hundred := "100 %"
112 if !bytes.Contains(buf.Bytes(), []byte(hundred)) {
113 t.Errorf("Bar's buffer does not contain: %q\n", hundred)
163 t.Errorf("Want bar: %q, got bar: %q", wantBar, got)
114164 }
115165 }
116166
117167 func TestBarHas100PercentWithBarRemoveOnComplete(t *testing.T) {
118168 var buf bytes.Buffer
119
120 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(&buf))
169 p := mpb.New(
170 mpb.WithWidth(80),
171 mpb.WithOutput(&buf),
172 mpb.WithAutoRefresh(),
173 )
121174
122175 total := 50
123176
126179 mpb.AppendDecorators(decor.Percentage()),
127180 )
128181
129 for i := 0; i < total; i++ {
130 bar.Increment()
131 time.Sleep(10 * time.Millisecond)
132 }
182 bar.IncrBy(total)
133183
134184 p.Wait()
135185
136186 hundred := "100 %"
137187 if !bytes.Contains(buf.Bytes(), []byte(hundred)) {
138 t.Errorf("Bar's buffer does not contain: %q\n", hundred)
188 t.Errorf("Bar's buffer does not contain: %q", hundred)
139189 }
140190 }
141191
144194 customFormat := "╢▌▌░╟"
145195 runes := []rune(customFormat)
146196 total := 80
147 p := mpb.New(mpb.WithWidth(total), mpb.WithOutput(&buf))
197 p := mpb.New(
198 mpb.WithWidth(80),
199 mpb.WithOutput(&buf),
200 mpb.WithAutoRefresh(),
201 )
148202 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.Add(int64(total), mpb.NewBarFiller(bs), mpb.BarFillerTrim())
155
156 for i := 0; i < total; i++ {
157 bar.Increment()
158 time.Sleep(10 * time.Millisecond)
159 }
203 bs = bs.Lbound(string(runes[0]))
204 bs = bs.Filler(string(runes[1]))
205 bs = bs.Tip(string(runes[2]))
206 bs = bs.Padding(string(runes[3]))
207 bs = bs.Rbound(string(runes[4]))
208 bar := p.New(int64(total), bs, mpb.BarFillerTrim())
209
210 bar.IncrBy(total)
160211
161212 p.Wait()
162213
166217 string(runes[2]),
167218 string(runes[4]),
168219 )
169 got := string(getLastLine(buf.Bytes()))
220 got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
170221
171222 if !strings.Contains(got, wantBar) {
172 t.Errorf("Want bar: %q:%d, got bar: %q:%d\n", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got))
173 }
174 }
175
176 func TestBarPanicBeforeComplete(t *testing.T) {
177 var buf bytes.Buffer
178 p := mpb.New(
179 mpb.WithWidth(80),
180 mpb.WithDebugOutput(&buf),
181 mpb.WithOutput(ioutil.Discard),
182 )
183
184 total := 100
185 panicMsg := "Upps!!!"
186 var pCount uint32
187 bar := p.AddBar(int64(total),
188 mpb.PrependDecorators(panicDecorator(panicMsg,
189 func(st decor.Statistics) bool {
190 if st.Current >= 42 {
191 atomic.AddUint32(&pCount, 1)
192 return true
193 }
194 return false
195 },
196 )),
197 )
198
199 for i := 0; i < total; i++ {
200 time.Sleep(10 * time.Millisecond)
201 bar.Increment()
202 }
203
204 p.Wait()
205
206 if pCount != 1 {
207 t.Errorf("Decor called after panic %d times\n", pCount-1)
208 }
209
210 barStr := buf.String()
211 if !strings.Contains(barStr, panicMsg) {
212 t.Errorf("%q doesn't contain %q\n", barStr, panicMsg)
213 }
214 }
215
216 func TestBarPanicAfterComplete(t *testing.T) {
217 var buf bytes.Buffer
218 p := mpb.New(
219 mpb.WithWidth(80),
220 mpb.WithDebugOutput(&buf),
221 mpb.WithOutput(ioutil.Discard),
222 )
223
224 total := 100
225 panicMsg := "Upps!!!"
226 var pCount uint32
227 bar := p.AddBar(int64(total),
228 mpb.PrependDecorators(panicDecorator(panicMsg,
229 func(st decor.Statistics) bool {
230 if st.Completed {
231 atomic.AddUint32(&pCount, 1)
232 return true
233 }
234 return false
235 },
236 )),
237 )
238
239 for i := 0; i < total; i++ {
240 time.Sleep(10 * time.Millisecond)
241 bar.Increment()
242 }
243
244 p.Wait()
245
246 if pCount > 2 {
247 t.Error("Decor called after panic more than 2 times\n")
248 }
249
250 barStr := buf.String()
251 if !strings.Contains(barStr, panicMsg) {
252 t.Errorf("%q doesn't contain %q\n", barStr, panicMsg)
223 t.Errorf("Want bar: %q:%d, got bar: %q:%d", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got))
253224 }
254225 }
255226
256227 func TestDecorStatisticsAvailableWidth(t *testing.T) {
228 ch := make(chan int, 2)
257229 td1 := func(s decor.Statistics) string {
258 if s.AvailableWidth != 80 {
259 t.Errorf("expected AvailableWidth %d got %d\n", 80, s.AvailableWidth)
260 }
261 return fmt.Sprintf("\x1b[31;1;4m%s\x1b[0m", strings.Repeat("0", 20))
262 }
263 checkDone := make(chan struct{})
230 ch <- s.AvailableWidth
231 return strings.Repeat("0", 20)
232 }
264233 td2 := func(s decor.Statistics) string {
265 defer func() {
266 checkDone <- struct{}{}
267 }()
268 if s.AvailableWidth != 40 {
269 t.Errorf("expected AvailableWidth %d got %d\n", 40, s.AvailableWidth)
270 }
234 ch <- s.AvailableWidth
271235 return ""
272236 }
273 total := 100
274 down := make(chan struct{})
275 p := mpb.New(
237 ctx, cancel := context.WithCancel(context.Background())
238 refresh := make(chan interface{})
239 p := mpb.NewWithContext(ctx,
276240 mpb.WithWidth(100),
277 mpb.WithShutdownNotifier(down),
278 mpb.WithOutput(ioutil.Discard),
279 )
280 bar := p.AddBar(int64(total),
241 mpb.WithManualRefresh(refresh),
242 mpb.WithOutput(io.Discard),
243 )
244 _ = p.AddBar(0,
281245 mpb.BarFillerTrim(),
282246 mpb.PrependDecorators(
283247 decor.Name(strings.Repeat("0", 20)),
284 decor.Any(td1),
248 decor.Meta(
249 decor.Any(td1),
250 func(s string) string {
251 return "\x1b[31;1m" + s + "\x1b[0m"
252 },
253 ),
285254 ),
286255 mpb.AppendDecorators(
287256 decor.Name(strings.Repeat("0", 20)),
288257 decor.Any(td2),
289258 ),
290259 )
260 refresh <- time.Now()
291261 go func() {
292 for {
293 select {
294 case <-checkDone:
295 bar.Abort(false)
296 case <-down:
297 return
298 }
299 }
262 time.Sleep(10 * time.Millisecond)
263 cancel()
300264 }()
301 for !bar.Completed() {
302 bar.Increment()
303 }
304 p.Wait()
305 }
306
307 func panicDecorator(panicMsg string, cond func(decor.Statistics) bool) decor.Decorator {
308 return decor.Any(func(st decor.Statistics) string {
309 if cond(st) {
310 panic(panicMsg)
311 }
312 return ""
313 })
314 }
265 p.Wait()
266
267 if availableWidth := <-ch; availableWidth != 80 {
268 t.Errorf("expected AvailableWidth %d got %d", 80, availableWidth)
269 }
270
271 if availableWidth := <-ch; availableWidth != 40 {
272 t.Errorf("expected AvailableWidth %d got %d", 40, availableWidth)
273 }
274 }
275
276 func TestBarQueueAfterBar(t *testing.T) {
277 shutdown := make(chan interface{})
278 ctx, cancel := context.WithCancel(context.Background())
279 p := mpb.NewWithContext(ctx,
280 mpb.WithOutput(io.Discard),
281 mpb.WithAutoRefresh(),
282 mpb.WithShutdownNotifier(shutdown),
283 )
284 a := p.AddBar(100)
285 b := p.AddBar(100, mpb.BarQueueAfter(a))
286 identity := map[*mpb.Bar]string{
287 a: "a",
288 b: "b",
289 }
290
291 a.IncrBy(100)
292 a.Wait()
293 cancel()
294
295 bars := (<-shutdown).([]*mpb.Bar)
296 if l := len(bars); l != 1 {
297 t.Errorf("Expected len of bars: %d, got: %d", 1, l)
298 }
299
300 p.Wait()
301 if bars[0] != b {
302 t.Errorf("Expected bars[0] == b, got: %s", identity[bars[0]])
303 }
304 }
0 package mpb
0 package mpb_test
11
22 import (
3 "io/ioutil"
3 "io"
4 "sync"
45 "testing"
56
6 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v8"
78 )
89
9 func BenchmarkIncrSingleBar(b *testing.B) {
10 p := New(WithOutput(ioutil.Discard), WithWidth(80))
11 bar := p.AddBar(int64(b.N))
10 const total = 1000
11
12 func BenchmarkNopStyle1Bar(b *testing.B) {
13 bench(b, mpb.NopStyle(), false, 1)
14 }
15
16 func BenchmarkNopStyle1BarWithAutoRefresh(b *testing.B) {
17 bench(b, mpb.NopStyle(), true, 1)
18 }
19
20 func BenchmarkNopStyle2Bars(b *testing.B) {
21 bench(b, mpb.NopStyle(), false, 2)
22 }
23
24 func BenchmarkNopStyle2BarsWithAutoRefresh(b *testing.B) {
25 bench(b, mpb.NopStyle(), true, 2)
26 }
27
28 func BenchmarkNopStyle3Bars(b *testing.B) {
29 bench(b, mpb.NopStyle(), false, 3)
30 }
31
32 func BenchmarkNopStyle3BarsWithAutoRefresh(b *testing.B) {
33 bench(b, mpb.NopStyle(), true, 3)
34 }
35
36 func BenchmarkBarStyle1Bar(b *testing.B) {
37 bench(b, mpb.BarStyle(), false, 1)
38 }
39
40 func BenchmarkBarStyle1BarWithAutoRefresh(b *testing.B) {
41 bench(b, mpb.BarStyle(), true, 1)
42 }
43
44 func BenchmarkBarStyle2Bars(b *testing.B) {
45 bench(b, mpb.BarStyle(), false, 2)
46 }
47
48 func BenchmarkBarStyle2BarsWithAutoRefresh(b *testing.B) {
49 bench(b, mpb.BarStyle(), true, 2)
50 }
51
52 func BenchmarkBarStyle3Bars(b *testing.B) {
53 bench(b, mpb.BarStyle(), false, 3)
54 }
55
56 func BenchmarkBarStyle3BarsWithAutoRefresh(b *testing.B) {
57 bench(b, mpb.BarStyle(), true, 3)
58 }
59
60 func bench(b *testing.B, builder mpb.BarFillerBuilder, autoRefresh bool, n int) {
61 var wg sync.WaitGroup
62 p := mpb.New(
63 mpb.WithWidth(100),
64 mpb.WithOutput(io.Discard),
65 mpb.ContainerOptional(mpb.WithAutoRefresh(), autoRefresh),
66 )
67 b.ResetTimer()
1268 for i := 0; i < b.N; i++ {
69 for j := 0; j < n; j++ {
70 bar := p.New(total, builder)
71 switch j {
72 case n - 1:
73 complete(b, bar)
74 default:
75 wg.Add(1)
76 go func() {
77 complete(b, bar)
78 wg.Done()
79 }()
80 }
81 }
82 wg.Wait()
83 }
84 p.Wait()
85 }
86
87 func complete(b *testing.B, bar *mpb.Bar) {
88 for i := 0; i < total; i++ {
1389 bar.Increment()
1490 }
91 bar.Wait()
1592 }
16
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 }
23 }
24
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 }
31 }
32
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 )
39 for i := 0; i < b.N; i++ {
40 bar.Increment()
41 }
42 }
11
22 import (
33 "io"
4 "io/ioutil"
54 "sync"
65 "time"
7
8 "github.com/vbauerster/mpb/v7/internal"
96 )
107
118 // ContainerOption is a func option to alter default behavior of a bar
3229 }
3330 }
3431
35 // WithRefreshRate overrides default 120ms refresh rate.
32 // WithRefreshRate overrides default 150ms refresh rate.
3633 func WithRefreshRate(d time.Duration) ContainerOption {
3734 return func(s *pState) {
38 s.rr = d
35 s.refreshRate = d
3936 }
4037 }
4138
4340 // Refresh will occur upon receive value from provided ch.
4441 func WithManualRefresh(ch <-chan interface{}) ContainerOption {
4542 return func(s *pState) {
46 s.externalRefresh = ch
43 s.manualRC = ch
4744 }
4845 }
4946
5350 // rendering will start as soon as provided chan is closed.
5451 func WithRenderDelay(ch <-chan struct{}) ContainerOption {
5552 return func(s *pState) {
56 s.renderDelay = ch
53 s.delayRC = ch
5754 }
5855 }
5956
60 // WithShutdownNotifier provided chanel will be closed, after all bars
61 // have been rendered.
62 func WithShutdownNotifier(ch chan struct{}) ContainerOption {
57 // WithShutdownNotifier value of type `[]*mpb.Bar` will be send into provided
58 // channel upon container shutdown.
59 func WithShutdownNotifier(ch chan<- interface{}) ContainerOption {
6360 return func(s *pState) {
6461 s.shutdownNotifier = ch
6562 }
6663 }
6764
68 // WithOutput overrides default os.Stdout output. Setting it to nil
69 // will effectively disable auto refresh rate and discard any output,
70 // useful if you want to disable progress bars with little overhead.
65 // WithOutput overrides default os.Stdout output. If underlying io.Writer
66 // is not a terminal then auto refresh is disabled unless WithAutoRefresh
67 // option is set.
7168 func WithOutput(w io.Writer) ContainerOption {
69 if w == nil {
70 w = io.Discard
71 }
7272 return func(s *pState) {
73 if w == nil {
74 s.output = ioutil.Discard
75 s.outputDiscarded = true
76 return
77 }
7873 s.output = w
7974 }
8075 }
8277 // WithDebugOutput sets debug output.
8378 func WithDebugOutput(w io.Writer) ContainerOption {
8479 if w == nil {
85 return nil
80 w = io.Discard
8681 }
8782 return func(s *pState) {
8883 s.debugOut = w
8984 }
9085 }
9186
92 // PopCompletedMode will pop and stop rendering completed bars.
87 // WithAutoRefresh force auto refresh regardless of what output is set to.
88 // Applicable only if not WithManualRefresh set.
89 func WithAutoRefresh() ContainerOption {
90 return func(s *pState) {
91 s.autoRefresh = true
92 }
93 }
94
95 // PopCompletedMode will pop completed bars to the top.
96 // To stop rendering bar after it has been popped, use
97 // mpb.BarRemoveOnComplete() option on that bar.
9398 func PopCompletedMode() ContainerOption {
9499 return func(s *pState) {
95100 s.popCompleted = true
96101 }
97102 }
98103
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))
104 // ContainerOptional will return provided option only when cond is true.
105 func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
106 if cond {
107 return option
108 }
109 return nil
102110 }
103111
104 // ContainerOptOn will invoke provided option only when higher order
105 // predicate evaluates to true.
112 // ContainerOptOn will return provided option only when predicate evaluates to true.
106113 func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption {
107114 if predicate() {
108115 return option
109116 }
110117 return nil
111118 }
119
120 // ContainerFuncOptional will call option and return its value only when cond is true.
121 func ContainerFuncOptional(option func() ContainerOption, cond bool) ContainerOption {
122 if cond {
123 return option()
124 }
125 return nil
126 }
127
128 // ContainerFuncOptOn will call option and return its value only when predicate evaluates to true.
129 func ContainerFuncOptOn(option func() ContainerOption, predicate func() bool) ContainerOption {
130 if predicate() {
131 return option()
132 }
133 return nil
134 }
22 import (
33 "bytes"
44 "fmt"
5 "io/ioutil"
5 "io"
66 "strconv"
77 "testing"
88 )
99
10 var (
11 out = io.Discard
12 lines = 99
13 )
14
1015 func BenchmarkWithFprintf(b *testing.B) {
11 cuuAndEd := "\x1b[%dA\x1b[J"
16 verb := fmt.Sprintf("%s%%d%s", escOpen, cuuAndEd)
17 b.ResetTimer()
1218 for i := 0; i < b.N; i++ {
13 fmt.Fprintf(ioutil.Discard, cuuAndEd, 4)
19 fmt.Fprintf(out, verb, lines)
1420 }
1521 }
1622
1723 func BenchmarkWithJoin(b *testing.B) {
18 bCuuAndEd := [][]byte{[]byte("\x1b["), []byte("A\x1b[J")}
24 bCuuAndEd := [][]byte{[]byte(escOpen), []byte(cuuAndEd)}
1925 for i := 0; i < b.N; i++ {
20 ioutil.Discard.Write(bytes.Join(bCuuAndEd, []byte(strconv.Itoa(4))))
26 _, _ = out.Write(bytes.Join(bCuuAndEd, []byte(strconv.Itoa(lines))))
2127 }
2228 }
2329
2430 func BenchmarkWithAppend(b *testing.B) {
25 escOpen := []byte("\x1b[")
26 cuuAndEd := []byte("A\x1b[J")
31 escOpen := []byte(escOpen)
32 cuuAndEd := []byte(cuuAndEd)
2733 for i := 0; i < b.N; i++ {
28 ioutil.Discard.Write(append(strconv.AppendInt(escOpen, 4, 10), cuuAndEd...))
34 _, _ = out.Write(append(strconv.AppendInt(escOpen, int64(lines), 10), cuuAndEd...))
2935 }
3036 }
3137
32 func BenchmarkWithCopy(b *testing.B) {
33 w := New(ioutil.Discard)
34 w.lineCount = 4
38 func BenchmarkWithCurrentImpl(b *testing.B) {
39 w := New(out)
40 b.ResetTimer()
3541 for i := 0; i < b.N; i++ {
36 w.ansiCuuAndEd()
42 _ = w.ew.ansiCuuAndEd(out, lines)
3743 }
3844 }
0 // +build darwin dragonfly freebsd netbsd openbsd
0 //go:build darwin || dragonfly || freebsd || netbsd || openbsd
11
22 package cwriter
33
0 // +build aix linux
0 //go:build aix || linux
11
22 package cwriter
33
0 // +build solaris
0 //go:build solaris
11
22 package cwriter
33
0 // +build zos
0 //go:build zos
11
22 package cwriter
33
77 "strconv"
88 )
99
10 // ErrNotTTY not a TeleTYpewriter error.
11 var ErrNotTTY = errors.New("not a terminal")
12
13 // http://ascii-table.com/ansi-escape-sequences.php
10 // https://github.com/dylanaraps/pure-sh-bible#cursor-movement
1411 const (
1512 escOpen = "\x1b["
1613 cuuAndEd = "A\x1b[J"
1714 )
1815
19 // Writer is a buffered the writer that updates the terminal. The
20 // contents of writer will be flushed when Flush is called.
21 type Writer struct {
22 out io.Writer
23 buf bytes.Buffer
24 lineCount int
25 fd int
26 isTerminal bool
27 }
16 // ErrNotTTY not a TeleTYpewriter error.
17 var ErrNotTTY = errors.New("not a terminal")
2818
2919 // New returns a new Writer with defaults.
3020 func New(out io.Writer) *Writer {
31 w := &Writer{out: out}
21 w := &Writer{
22 Buffer: new(bytes.Buffer),
23 out: out,
24 termSize: func(_ int) (int, int, error) {
25 return -1, -1, ErrNotTTY
26 },
27 }
3228 if f, ok := out.(*os.File); ok {
3329 w.fd = int(f.Fd())
34 w.isTerminal = IsTerminal(w.fd)
30 if IsTerminal(w.fd) {
31 w.terminal = true
32 w.termSize = func(fd int) (int, int, error) {
33 return GetSize(fd)
34 }
35 }
3536 }
37 bb := make([]byte, 16)
38 w.ew = escWriter(bb[:copy(bb, []byte(escOpen))])
3639 return w
3740 }
3841
39 // Flush flushes the underlying buffer.
40 func (w *Writer) Flush(lineCount int) (err error) {
41 // some terminals interpret 'cursor up 0' as 'cursor up 1'
42 if w.lineCount > 0 {
43 err = w.clearLines()
44 if err != nil {
45 return
46 }
47 }
48 w.lineCount = lineCount
49 _, err = w.buf.WriteTo(w.out)
50 return
42 // IsTerminal tells whether underlying io.Writer is terminal.
43 func (w *Writer) IsTerminal() bool {
44 return w.terminal
5145 }
5246
53 // Write appends the contents of p to the underlying buffer.
54 func (w *Writer) Write(p []byte) (n int, err error) {
55 return w.buf.Write(p)
47 // GetTermSize returns WxH of underlying terminal.
48 func (w *Writer) GetTermSize() (width, height int, err error) {
49 return w.termSize(w.fd)
5650 }
5751
58 // WriteString writes string to the underlying buffer.
59 func (w *Writer) WriteString(s string) (n int, err error) {
60 return w.buf.WriteString(s)
52 type escWriter []byte
53
54 func (b escWriter) ansiCuuAndEd(out io.Writer, n int) error {
55 b = strconv.AppendInt(b, int64(n), 10)
56 _, err := out.Write(append(b, []byte(cuuAndEd)...))
57 return err
6158 }
62
63 // ReadFrom reads from the provided io.Reader and writes to the
64 // underlying buffer.
65 func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
66 return w.buf.ReadFrom(r)
67 }
68
69 // GetWidth returns width of underlying terminal.
70 func (w *Writer) GetWidth() (int, error) {
71 if !w.isTerminal {
72 return -1, ErrNotTTY
73 }
74 tw, _, err := GetSize(w.fd)
75 return tw, err
76 }
77
78 func (w *Writer) ansiCuuAndEd() (err error) {
79 buf := make([]byte, 8)
80 buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lineCount), 10)
81 _, err = w.out.Write(append(buf, cuuAndEd...))
82 return
83 }
0 // +build !windows
0 //go:build !windows
11
22 package cwriter
33
44 import (
5 "bytes"
6 "io"
7
58 "golang.org/x/sys/unix"
69 )
710
8 func (w *Writer) clearLines() error {
9 return w.ansiCuuAndEd()
11 // Writer is a buffered terminal writer, which moves cursor N lines up
12 // on each flush except the first one, where N is a number of lines of
13 // a previous flush.
14 type Writer struct {
15 *bytes.Buffer
16 out io.Writer
17 ew escWriter
18 fd int
19 terminal bool
20 termSize func(int) (int, int, error)
21 }
22
23 // Flush flushes the underlying buffer.
24 // It's caller's responsibility to pass correct number of lines.
25 func (w *Writer) Flush(lines int) error {
26 _, err := w.WriteTo(w.out)
27 // some terminals interpret 'cursor up 0' as 'cursor up 1'
28 if err == nil && lines > 0 {
29 err = w.ew.ansiCuuAndEd(w, lines)
30 }
31 return err
1032 }
1133
1234 // GetSize returns the dimensions of the given terminal.
0 // +build windows
0 //go:build windows
11
22 package cwriter
33
44 import (
5 "bytes"
6 "io"
57 "unsafe"
68
79 "golang.org/x/sys/windows"
1416 procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
1517 )
1618
17 func (w *Writer) clearLines() error {
18 if !w.isTerminal {
19 // Writer is a buffered terminal writer, which moves cursor N lines up
20 // on each flush except the first one, where N is a number of lines of
21 // a previous flush.
22 type Writer struct {
23 *bytes.Buffer
24 out io.Writer
25 ew escWriter
26 lines int
27 fd int
28 terminal bool
29 termSize func(int) (int, int, error)
30 }
31
32 // Flush flushes the underlying buffer.
33 // It's caller's responsibility to pass correct number of lines.
34 func (w *Writer) Flush(lines int) error {
35 if w.lines > 0 {
36 err := w.clearLines(w.lines)
37 if err != nil {
38 return err
39 }
40 }
41 w.lines = lines
42 _, err := w.WriteTo(w.out)
43 return err
44 }
45
46 func (w *Writer) clearLines(n int) error {
47 if !w.terminal {
1948 // hope it's cygwin or similar
20 return w.ansiCuuAndEd()
49 return w.ew.ansiCuuAndEd(w.out, n)
2150 }
2251
2352 var info windows.ConsoleScreenBufferInfo
2554 return err
2655 }
2756
28 info.CursorPosition.Y -= int16(w.lineCount)
57 info.CursorPosition.Y -= int16(n)
2958 if info.CursorPosition.Y < 0 {
3059 info.CursorPosition.Y = 0
3160 }
3968 X: info.Window.Left,
4069 Y: info.CursorPosition.Y,
4170 }
42 count := uint32(info.Size.X) * uint32(w.lineCount)
71 count := uint32(info.Size.X) * uint32(n)
4372 _, _, _ = procFillConsoleOutputCharacter.Call(
4473 uintptr(w.fd),
4574 uintptr(' '),
5180 }
5281
5382 // GetSize returns the visible dimensions of the given terminal.
54 //
5583 // These dimensions don't include any scrollback buffer height.
5684 func GetSize(fd int) (width, height int, err error) {
5785 var info windows.ConsoleScreenBufferInfo
00 package decor
11
2 // Any decorator displays text, that can be changed during decorator's
3 // lifetime via provided DecorFunc.
2 var _ Decorator = any{}
3
4 // Any decorator.
5 // Converts DecorFunc into Decorator.
46 //
57 // `fn` DecorFunc callback
6 //
78 // `wcc` optional WC config
8 //
99 func Any(fn DecorFunc, wcc ...WC) Decorator {
10 return &any{initWC(wcc...), fn}
10 return any{initWC(wcc...), fn}
1111 }
1212
1313 type any struct {
1515 fn DecorFunc
1616 }
1717
18 func (d *any) Decor(s Statistics) string {
19 return d.FormatMsg(d.fn(s))
18 func (d any) Decor(s Statistics) (string, int) {
19 return d.Format(d.fn(s))
2020 }
11
22 import (
33 "fmt"
4 "strings"
5 )
6
7 const (
8 _ = iota
9 UnitKiB
10 UnitKB
114 )
125
136 // CountersNoUnit is a wrapper around Counters with no unit param.
169 }
1710
1811 // CountersKibiByte is a wrapper around Counters with predefined unit
19 // UnitKiB (bytes/1024).
12 // as SizeB1024(0).
2013 func CountersKibiByte(pairFmt string, wcc ...WC) Decorator {
21 return Counters(UnitKiB, pairFmt, wcc...)
14 return Counters(SizeB1024(0), pairFmt, wcc...)
2215 }
2316
2417 // CountersKiloByte is a wrapper around Counters with predefined unit
25 // UnitKB (bytes/1000).
18 // as SizeB1000(0).
2619 func CountersKiloByte(pairFmt string, wcc ...WC) Decorator {
27 return Counters(UnitKB, pairFmt, wcc...)
20 return Counters(SizeB1000(0), pairFmt, wcc...)
2821 }
2922
3023 // Counters decorator with dynamic unit measure adjustment.
3124 //
32 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
33 //
34 // `pairFmt` printf compatible verbs for current and total pair
35 //
36 // `wcc` optional WC config
37 //
38 // pairFmt example if unit=UnitKB:
39 //
25 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
26 //
27 // `pairFmt` printf compatible verbs for current and total
28 //
29 // `wcc` optional WC config
30 //
31 // pairFmt example if unit=SizeB1000(0):
32 //
33 // pairFmt="%d / %d" output: "1MB / 12MB"
34 // pairFmt="% d / % d" output: "1 MB / 12 MB"
4035 // pairFmt="%.1f / %.1f" output: "1.0MB / 12.0MB"
4136 // pairFmt="% .1f / % .1f" output: "1.0 MB / 12.0 MB"
42 // pairFmt="%d / %d" output: "1MB / 12MB"
43 // pairFmt="% d / % d" output: "1 MB / 12 MB"
44 //
45 func Counters(unit int, pairFmt string, wcc ...WC) Decorator {
46 producer := func(unit int, pairFmt string) DecorFunc {
47 if pairFmt == "" {
48 pairFmt = "%d / %d"
49 } else if strings.Count(pairFmt, "%") != 2 {
50 panic("expected pairFmt with exactly 2 verbs")
51 }
52 switch unit {
53 case UnitKiB:
37 // pairFmt="%f / %f" output: "1.000000MB / 12.000000MB"
38 // pairFmt="% f / % f" output: "1.000000 MB / 12.000000 MB"
39 func Counters(unit interface{}, pairFmt string, wcc ...WC) Decorator {
40 producer := func() DecorFunc {
41 switch unit.(type) {
42 case SizeB1024:
43 if pairFmt == "" {
44 pairFmt = "% d / % d"
45 }
5446 return func(s Statistics) string {
5547 return fmt.Sprintf(pairFmt, SizeB1024(s.Current), SizeB1024(s.Total))
5648 }
57 case UnitKB:
49 case SizeB1000:
50 if pairFmt == "" {
51 pairFmt = "% d / % d"
52 }
5853 return func(s Statistics) string {
5954 return fmt.Sprintf(pairFmt, SizeB1000(s.Current), SizeB1000(s.Total))
6055 }
6156 default:
57 if pairFmt == "" {
58 pairFmt = "%d / %d"
59 }
6260 return func(s Statistics) string {
6361 return fmt.Sprintf(pairFmt, s.Current, s.Total)
6462 }
6563 }
6664 }
67 return Any(producer(unit, pairFmt), wcc...)
65 return Any(producer(), wcc...)
6866 }
6967
7068 // TotalNoUnit is a wrapper around Total with no unit param.
7371 }
7472
7573 // TotalKibiByte is a wrapper around Total with predefined unit
76 // UnitKiB (bytes/1024).
74 // as SizeB1024(0).
7775 func TotalKibiByte(format string, wcc ...WC) Decorator {
78 return Total(UnitKiB, format, wcc...)
76 return Total(SizeB1024(0), format, wcc...)
7977 }
8078
8179 // TotalKiloByte is a wrapper around Total with predefined unit
82 // UnitKB (bytes/1000).
80 // as SizeB1000(0).
8381 func TotalKiloByte(format string, wcc ...WC) Decorator {
84 return Total(UnitKB, format, wcc...)
82 return Total(SizeB1000(0), format, wcc...)
8583 }
8684
8785 // Total decorator with dynamic unit measure adjustment.
8886 //
89 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
87 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
9088 //
9189 // `format` printf compatible verb for Total
9290 //
9391 // `wcc` optional WC config
9492 //
95 // format example if unit=UnitKiB:
96 //
93 // format example if unit=SizeB1024(0):
94 //
95 // format="%d" output: "12MiB"
96 // format="% d" output: "12 MiB"
9797 // format="%.1f" output: "12.0MiB"
9898 // format="% .1f" output: "12.0 MiB"
99 // format="%d" output: "12MiB"
100 // format="% d" output: "12 MiB"
101 //
102 func Total(unit int, format string, wcc ...WC) Decorator {
103 producer := func(unit int, format string) DecorFunc {
104 if format == "" {
105 format = "%d"
106 } else if strings.Count(format, "%") != 1 {
107 panic("expected format with exactly 1 verb")
108 }
109
110 switch unit {
111 case UnitKiB:
99 // format="%f" output: "12.000000MiB"
100 // format="% f" output: "12.000000 MiB"
101 func Total(unit interface{}, format string, wcc ...WC) Decorator {
102 producer := func() DecorFunc {
103 switch unit.(type) {
104 case SizeB1024:
105 if format == "" {
106 format = "% d"
107 }
112108 return func(s Statistics) string {
113109 return fmt.Sprintf(format, SizeB1024(s.Total))
114110 }
115 case UnitKB:
111 case SizeB1000:
112 if format == "" {
113 format = "% d"
114 }
116115 return func(s Statistics) string {
117116 return fmt.Sprintf(format, SizeB1000(s.Total))
118117 }
119118 default:
119 if format == "" {
120 format = "%d"
121 }
120122 return func(s Statistics) string {
121123 return fmt.Sprintf(format, s.Total)
122124 }
123125 }
124126 }
125 return Any(producer(unit, format), wcc...)
127 return Any(producer(), wcc...)
126128 }
127129
128130 // CurrentNoUnit is a wrapper around Current with no unit param.
131133 }
132134
133135 // CurrentKibiByte is a wrapper around Current with predefined unit
134 // UnitKiB (bytes/1024).
136 // as SizeB1024(0).
135137 func CurrentKibiByte(format string, wcc ...WC) Decorator {
136 return Current(UnitKiB, format, wcc...)
138 return Current(SizeB1024(0), format, wcc...)
137139 }
138140
139141 // CurrentKiloByte is a wrapper around Current with predefined unit
140 // UnitKB (bytes/1000).
142 // as SizeB1000(0).
141143 func CurrentKiloByte(format string, wcc ...WC) Decorator {
142 return Current(UnitKB, format, wcc...)
144 return Current(SizeB1000(0), format, wcc...)
143145 }
144146
145147 // Current decorator with dynamic unit measure adjustment.
146148 //
147 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
149 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
148150 //
149151 // `format` printf compatible verb for Current
150152 //
151153 // `wcc` optional WC config
152154 //
153 // format example if unit=UnitKiB:
154 //
155 // format example if unit=SizeB1024(0):
156 //
157 // format="%d" output: "12MiB"
158 // format="% d" output: "12 MiB"
155159 // format="%.1f" output: "12.0MiB"
156160 // format="% .1f" output: "12.0 MiB"
157 // format="%d" output: "12MiB"
158 // format="% d" output: "12 MiB"
159 //
160 func Current(unit int, format string, wcc ...WC) Decorator {
161 producer := func(unit int, format string) DecorFunc {
162 if format == "" {
163 format = "%d"
164 } else if strings.Count(format, "%") != 1 {
165 panic("expected format with exactly 1 verb")
166 }
167
168 switch unit {
169 case UnitKiB:
161 // format="%f" output: "12.000000MiB"
162 // format="% f" output: "12.000000 MiB"
163 func Current(unit interface{}, format string, wcc ...WC) Decorator {
164 producer := func() DecorFunc {
165 switch unit.(type) {
166 case SizeB1024:
167 if format == "" {
168 format = "% d"
169 }
170170 return func(s Statistics) string {
171171 return fmt.Sprintf(format, SizeB1024(s.Current))
172172 }
173 case UnitKB:
173 case SizeB1000:
174 if format == "" {
175 format = "% d"
176 }
174177 return func(s Statistics) string {
175178 return fmt.Sprintf(format, SizeB1000(s.Current))
176179 }
177180 default:
181 if format == "" {
182 format = "%d"
183 }
178184 return func(s Statistics) string {
179185 return fmt.Sprintf(format, s.Current)
180186 }
181187 }
182188 }
183 return Any(producer(unit, format), wcc...)
189 return Any(producer(), wcc...)
184190 }
185191
186192 // InvertedCurrentNoUnit is a wrapper around InvertedCurrent with no unit param.
189195 }
190196
191197 // InvertedCurrentKibiByte is a wrapper around InvertedCurrent with predefined unit
192 // UnitKiB (bytes/1024).
198 // as SizeB1024(0).
193199 func InvertedCurrentKibiByte(format string, wcc ...WC) Decorator {
194 return InvertedCurrent(UnitKiB, format, wcc...)
200 return InvertedCurrent(SizeB1024(0), format, wcc...)
195201 }
196202
197203 // InvertedCurrentKiloByte is a wrapper around InvertedCurrent with predefined unit
198 // UnitKB (bytes/1000).
204 // as SizeB1000(0).
199205 func InvertedCurrentKiloByte(format string, wcc ...WC) Decorator {
200 return InvertedCurrent(UnitKB, format, wcc...)
206 return InvertedCurrent(SizeB1000(0), format, wcc...)
201207 }
202208
203209 // InvertedCurrent decorator with dynamic unit measure adjustment.
204210 //
205 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
211 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
206212 //
207213 // `format` printf compatible verb for InvertedCurrent
208214 //
209215 // `wcc` optional WC config
210216 //
211 // format example if unit=UnitKiB:
212 //
217 // format example if unit=SizeB1024(0):
218 //
219 // format="%d" output: "12MiB"
220 // format="% d" output: "12 MiB"
213221 // format="%.1f" output: "12.0MiB"
214222 // format="% .1f" output: "12.0 MiB"
215 // format="%d" output: "12MiB"
216 // format="% d" output: "12 MiB"
217 //
218 func InvertedCurrent(unit int, format string, wcc ...WC) Decorator {
219 producer := func(unit int, format string) DecorFunc {
220 if format == "" {
221 format = "%d"
222 } else if strings.Count(format, "%") != 1 {
223 panic("expected format with exactly 1 verb")
224 }
225
226 switch unit {
227 case UnitKiB:
223 // format="%f" output: "12.000000MiB"
224 // format="% f" output: "12.000000 MiB"
225 func InvertedCurrent(unit interface{}, format string, wcc ...WC) Decorator {
226 producer := func() DecorFunc {
227 switch unit.(type) {
228 case SizeB1024:
229 if format == "" {
230 format = "% d"
231 }
228232 return func(s Statistics) string {
229233 return fmt.Sprintf(format, SizeB1024(s.Total-s.Current))
230234 }
231 case UnitKB:
235 case SizeB1000:
236 if format == "" {
237 format = "% d"
238 }
232239 return func(s Statistics) string {
233240 return fmt.Sprintf(format, SizeB1000(s.Total-s.Current))
234241 }
235242 default:
243 if format == "" {
244 format = "%d"
245 }
236246 return func(s Statistics) string {
237247 return fmt.Sprintf(format, s.Total-s.Current)
238248 }
239249 }
240250 }
241 return Any(producer(unit, format), wcc...)
242 }
251 return Any(producer(), wcc...)
252 }
33 "fmt"
44 "time"
55
6 "github.com/acarl005/stripansi"
76 "github.com/mattn/go-runewidth"
87 )
98
109 const (
1110 // DidentRight bit specifies identation direction.
12 // |foo |b | With DidentRight
13 // | foo| b| Without DidentRight
11 //
12 // |foo |b | With DidentRight
13 // | foo| b| Without DidentRight
1414 DidentRight = 1 << iota
1515
1616 // DextraSpace bit adds extra space, makes sense with DSyncWidth only.
4343 ET_STYLE_MMSS
4444 )
4545
46 // Statistics consists of progress related statistics, that Decorator
47 // may need.
46 // Statistics contains fields which are necessary for implementing
47 // `decor.Decorator` and `mpb.BarFiller` interfaces.
4848 type Statistics struct {
49 AvailableWidth int // calculated width initially equal to terminal width
50 RequestedWidth int // width set by `mpb.WithWidth`
4951 ID int
50 AvailableWidth int
5152 Total int64
5253 Current int64
5354 Refill int64
5455 Completed bool
56 Aborted bool
5557 }
5658
5759 // Decorator interface.
6365 // `DecorFunc` into a `Decorator` interface by using provided
6466 // `func Any(DecorFunc, ...WC) Decorator`.
6567 type Decorator interface {
66 Configurator
6768 Synchronizer
68 Decor(Statistics) string
69 Formatter
70 Decor(Statistics) (str string, viewWidth int)
6971 }
7072
7173 // DecorFunc func type.
72 // To be used with `func Any`(DecorFunc, ...WC) Decorator`.
74 // To be used with `func Any(DecorFunc, ...WC) Decorator`.
7375 type DecorFunc func(Statistics) string
7476
7577 // Synchronizer interface.
7981 Sync() (chan int, bool)
8082 }
8183
82 // Configurator interface.
83 type Configurator interface {
84 GetConf() WC
85 SetConf(WC)
84 // Formatter interface.
85 // Format method needs to be called from within Decorator.Decor method
86 // in order to format string according to decor.WC settings.
87 // No need to implement manually as long as decor.WC is embedded.
88 type Formatter interface {
89 Format(string) (str string, viewWidth int)
8690 }
8791
8892 // Wrapper interface.
9094 // it is necessary to implement this interface to retain functionality
9195 // of built-in Decorator.
9296 type Wrapper interface {
93 Base() Decorator
97 Unwrap() Decorator
9498 }
9599
96100 // EwmaDecorator interface.
110114 // If decorator needs to be notified once upon bar shutdown event, so
111115 // this is the right interface to implement.
112116 type ShutdownListener interface {
113 Shutdown()
117 OnShutdown()
114118 }
115119
116120 // Global convenience instances of WC with sync width bit set.
132136 wsync chan int
133137 }
134138
135 // FormatMsg formats final message according to WC.W and WC.C.
136 // Should be called by any Decorator implementation.
137 func (wc *WC) FormatMsg(msg string) string {
138 pureWidth := runewidth.StringWidth(msg)
139 stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
140 maxCell := wc.W
139 // Format should be called by any Decorator implementation.
140 // Returns formatted string and its view (visual) width.
141 func (wc WC) Format(str string) (string, int) {
142 viewWidth := runewidth.StringWidth(str)
143 if wc.W > viewWidth {
144 viewWidth = wc.W
145 }
141146 if (wc.C & DSyncWidth) != 0 {
142 cellCount := stripWidth
143147 if (wc.C & DextraSpace) != 0 {
144 cellCount++
148 viewWidth++
145149 }
146 wc.wsync <- cellCount
147 maxCell = <-wc.wsync
150 wc.wsync <- viewWidth
151 viewWidth = <-wc.wsync
148152 }
149 return wc.fill(msg, maxCell+(pureWidth-stripWidth))
153 return wc.fill(str, viewWidth), viewWidth
150154 }
151155
152156 // Init initializes width related config.
153157 func (wc *WC) Init() WC {
154 wc.fill = runewidth.FillLeft
155158 if (wc.C & DidentRight) != 0 {
156159 wc.fill = runewidth.FillRight
160 } else {
161 wc.fill = runewidth.FillLeft
157162 }
158163 if (wc.C & DSyncWidth) != 0 {
159164 // it's deliberate choice to override wsync on each Init() call,
164169 }
165170
166171 // Sync is implementation of Synchronizer interface.
167 func (wc *WC) Sync() (chan int, bool) {
172 func (wc WC) Sync() (chan int, bool) {
168173 if (wc.C&DSyncWidth) != 0 && wc.wsync == nil {
169174 panic(fmt.Sprintf("%T is not initialized", wc))
170175 }
171176 return wc.wsync, (wc.C & DSyncWidth) != 0
172 }
173
174 // GetConf is implementation of Configurator interface.
175 func (wc *WC) GetConf() WC {
176 return *wc
177 }
178
179 // SetConf is implementation of Configurator interface.
180 func (wc *WC) SetConf(conf WC) {
181 *wc = conf.Init()
182177 }
183178
184179 func initWC(wcc ...WC) WC {
0 // Package decor provides common decorators for "github.com/vbauerster/mpb/v7" module.
0 // Package decor provides common decorators for "github.com/vbauerster/mpb/v8" module.
11 //
22 // Some decorators returned by this package might have a closure state. It is ok to use
33 // decorators concurrently, unless you share the same decorator among multiple
55 //
66 // Don't:
77 //
8 // p := mpb.New()
9 // name := decor.Name("bar")
10 // p.AddBar(100, mpb.AppendDecorators(name))
11 // p.AddBar(100, mpb.AppendDecorators(name))
8 // p := mpb.New()
9 // name := decor.Name("bar")
10 // p.AddBar(100, mpb.AppendDecorators(name))
11 // p.AddBar(100, mpb.AppendDecorators(name))
1212 //
1313 // Do:
1414 //
88 // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
99 //
1010 // `wcc` optional WC config
11 //
1211 func Elapsed(style TimeStyle, wcc ...WC) Decorator {
1312 return NewElapsed(style, time.Now(), wcc...)
1413 }
2019 // `startTime` start time
2120 //
2221 // `wcc` optional WC config
23 //
2422 func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
2523 var msg string
2624 producer := chooseTimeProducer(style)
77 "github.com/VividCortex/ewma"
88 )
99
10 var (
11 _ Decorator = (*movingAverageETA)(nil)
12 _ EwmaDecorator = (*movingAverageETA)(nil)
13 _ Decorator = (*averageETA)(nil)
14 _ AverageDecorator = (*averageETA)(nil)
15 )
16
1017 // TimeNormalizer interface. Implementors could be passed into
1118 // MovingAverageETA, in order to affect i.e. normalize its output.
1219 type TimeNormalizer interface {
2128 return f(src)
2229 }
2330
24 // EwmaETA exponential-weighted-moving-average based ETA decorator.
25 // For this decorator to work correctly you have to measure each
26 // iteration's duration and pass it to the
27 // *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
31 // EwmaETA exponential-weighted-moving-average based ETA decorator. For this
32 // decorator to work correctly you have to measure each iteration's duration
33 // and pass it to one of the (*Bar).EwmaIncr... family methods.
2834 func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
2935 var average ewma.MovingAverage
3036 if age == 0 {
4450 // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
4551 //
4652 // `wcc` optional WC config
47 //
4853 func MovingAverageETA(style TimeStyle, average ewma.MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
4954 d := &movingAverageETA{
5055 WC: initWC(wcc...),
6267 producer func(time.Duration) string
6368 }
6469
65 func (d *movingAverageETA) Decor(s Statistics) string {
70 func (d *movingAverageETA) Decor(s Statistics) (string, int) {
6671 v := math.Round(d.average.Value())
6772 remaining := time.Duration((s.Total - s.Current) * int64(v))
6873 if d.normalizer != nil {
6974 remaining = d.normalizer.Normalize(remaining)
7075 }
71 return d.FormatMsg(d.producer(remaining))
76 return d.Format(d.producer(remaining))
7277 }
7378
7479 func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) {
8489 // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
8590 //
8691 // `wcc` optional WC config
87 //
8892 func AverageETA(style TimeStyle, wcc ...WC) Decorator {
8993 return NewAverageETA(style, time.Now(), nil, wcc...)
9094 }
98102 // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
99103 //
100104 // `wcc` optional WC config
101 //
102105 func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator {
103106 d := &averageETA{
104107 WC: initWC(wcc...),
116119 producer func(time.Duration) string
117120 }
118121
119 func (d *averageETA) Decor(s Statistics) string {
122 func (d *averageETA) Decor(s Statistics) (string, int) {
120123 var remaining time.Duration
121124 if s.Current != 0 {
122125 durPerItem := float64(time.Since(d.startTime)) / float64(s.Current)
126129 remaining = d.normalizer.Normalize(remaining)
127130 }
128131 }
129 return d.FormatMsg(d.producer(remaining))
132 return d.Format(d.producer(remaining))
130133 }
131134
132135 func (d *averageETA) AverageAdjust(startTime time.Time) {
195198 }
196199 default:
197200 return func(remaining time.Duration) string {
198 // strip off nanoseconds
199 return ((remaining / time.Second) * time.Second).String()
200 }
201 }
202 }
201 return remaining.Truncate(time.Second).String()
202 }
203 }
204 }
+0
-107
decor/merge.go less more
0 package decor
1
2 import (
3 "strings"
4
5 "github.com/acarl005/stripansi"
6 "github.com/mattn/go-runewidth"
7 )
8
9 // Merge wraps its decorator argument with intention to sync width
10 // with several decorators of another bar. Visual example:
11 //
12 // +----+--------+---------+--------+
13 // | B1 | MERGE(D, P1, Pn) |
14 // +----+--------+---------+--------+
15 // | B2 | D0 | D1 | Dn |
16 // +----+--------+---------+--------+
17 //
18 func Merge(decorator Decorator, placeholders ...WC) Decorator {
19 if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 {
20 return decorator
21 }
22 md := &mergeDecorator{
23 Decorator: decorator,
24 wc: decorator.GetConf(),
25 placeHolders: make([]*placeHolderDecorator, len(placeholders)),
26 }
27 decorator.SetConf(WC{})
28 for i, wc := range placeholders {
29 if (wc.C & DSyncWidth) == 0 {
30 return decorator
31 }
32 md.placeHolders[i] = &placeHolderDecorator{wc.Init()}
33 }
34 return md
35 }
36
37 type mergeDecorator struct {
38 Decorator
39 wc WC
40 placeHolders []*placeHolderDecorator
41 }
42
43 func (d *mergeDecorator) GetConf() WC {
44 return d.wc
45 }
46
47 func (d *mergeDecorator) SetConf(conf WC) {
48 d.wc = conf.Init()
49 }
50
51 func (d *mergeDecorator) MergeUnwrap() []Decorator {
52 decorators := make([]Decorator, len(d.placeHolders))
53 for i, ph := range d.placeHolders {
54 decorators[i] = ph
55 }
56 return decorators
57 }
58
59 func (d *mergeDecorator) Sync() (chan int, bool) {
60 return d.wc.Sync()
61 }
62
63 func (d *mergeDecorator) Base() Decorator {
64 return d.Decorator
65 }
66
67 func (d *mergeDecorator) Decor(s Statistics) string {
68 msg := d.Decorator.Decor(s)
69 pureWidth := runewidth.StringWidth(msg)
70 stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
71 cellCount := stripWidth
72 if (d.wc.C & DextraSpace) != 0 {
73 cellCount++
74 }
75
76 total := runewidth.StringWidth(d.placeHolders[0].FormatMsg(""))
77 pw := (cellCount - total) / len(d.placeHolders)
78 rem := (cellCount - total) % len(d.placeHolders)
79
80 var diff int
81 for i := 1; i < len(d.placeHolders); i++ {
82 ph := d.placeHolders[i]
83 width := pw - diff
84 if (ph.WC.C & DextraSpace) != 0 {
85 width--
86 if width < 0 {
87 width = 0
88 }
89 }
90 max := runewidth.StringWidth(ph.FormatMsg(strings.Repeat(" ", width)))
91 total += max
92 diff = max - pw
93 }
94
95 d.wc.wsync <- pw + rem
96 max := <-d.wc.wsync
97 return d.wc.fill(msg, max+total+(pureWidth-stripWidth))
98 }
99
100 type placeHolderDecorator struct {
101 WC
102 }
103
104 func (d *placeHolderDecorator) Decor(Statistics) string {
105 return ""
106 }
0 package decor
1
2 var (
3 _ Decorator = metaWrapper{}
4 _ Wrapper = metaWrapper{}
5 )
6
7 // Meta wrap decorator.
8 // Provided fn is supposed to wrap output of given decorator
9 // with meta information like ANSI escape codes for example.
10 // Primary usage intention is to set SGR display attributes.
11 //
12 // `decorator` Decorator to wrap
13 // `fn` func to apply meta information
14 func Meta(decorator Decorator, fn func(string) string) Decorator {
15 if decorator == nil {
16 return nil
17 }
18 return metaWrapper{decorator, fn}
19 }
20
21 type metaWrapper struct {
22 Decorator
23 fn func(string) string
24 }
25
26 func (d metaWrapper) Decor(s Statistics) (string, int) {
27 str, width := d.Decorator.Decor(s)
28 return d.fn(str), width
29 }
30
31 func (d metaWrapper) Unwrap() Decorator {
32 return d.Decorator
33 }
44 "sync"
55
66 "github.com/VividCortex/ewma"
7 )
8
9 var (
10 _ ewma.MovingAverage = (*threadSafeMovingAverage)(nil)
11 _ ewma.MovingAverage = (*medianWindow)(nil)
12 _ sort.Interface = (*medianWindow)(nil)
713 )
814
915 type threadSafeMovingAverage struct {
55 // `str` string to display
66 //
77 // `wcc` optional WC config
8 //
98 func Name(str string, wcc ...WC) Decorator {
109 return Any(func(Statistics) string { return str }, wcc...)
1110 }
0 package decor
1
2 var (
3 _ Decorator = onAbortWrapper{}
4 _ Wrapper = onAbortWrapper{}
5 _ Decorator = onAbortMetaWrapper{}
6 _ Wrapper = onAbortMetaWrapper{}
7 )
8
9 // OnAbort wrap decorator.
10 // Displays provided message on abort event.
11 // Has no effect if bar.Abort(true) is called.
12 //
13 // `decorator` Decorator to wrap
14 // `message` message to display
15 func OnAbort(decorator Decorator, message string) Decorator {
16 if decorator == nil {
17 return nil
18 }
19 return onAbortWrapper{decorator, message}
20 }
21
22 type onAbortWrapper struct {
23 Decorator
24 msg string
25 }
26
27 func (d onAbortWrapper) Decor(s Statistics) (string, int) {
28 if s.Aborted {
29 return d.Format(d.msg)
30 }
31 return d.Decorator.Decor(s)
32 }
33
34 func (d onAbortWrapper) Unwrap() Decorator {
35 return d.Decorator
36 }
37
38 // OnAbortMeta wrap decorator.
39 // Provided fn is supposed to wrap output of given decorator
40 // with meta information like ANSI escape codes for example.
41 // Primary usage intention is to set SGR display attributes.
42 //
43 // `decorator` Decorator to wrap
44 // `fn` func to apply meta information
45 func OnAbortMeta(decorator Decorator, fn func(string) string) Decorator {
46 if decorator == nil {
47 return nil
48 }
49 return onAbortMetaWrapper{decorator, fn}
50 }
51
52 type onAbortMetaWrapper struct {
53 Decorator
54 fn func(string) string
55 }
56
57 func (d onAbortMetaWrapper) Decor(s Statistics) (string, int) {
58 if s.Completed {
59 str, width := d.Decorator.Decor(s)
60 return d.fn(str), width
61 }
62 return d.Decorator.Decor(s)
63 }
64
65 func (d onAbortMetaWrapper) Unwrap() Decorator {
66 return d.Decorator
67 }
0 package decor
1
2 // OnCompleteOrOnAbort wrap decorator.
3 // Displays provided message on complete or on abort event.
4 //
5 // `decorator` Decorator to wrap
6 // `message` message to display
7 func OnCompleteOrOnAbort(decorator Decorator, message string) Decorator {
8 return OnComplete(OnAbort(decorator, message), message)
9 }
10
11 // OnCompleteMetaOrOnAbortMeta wrap decorator.
12 // Provided fn is supposed to wrap output of given decorator
13 // with meta information like ANSI escape codes for example.
14 // Primary usage intention is to set SGR display attributes.
15 //
16 // `decorator` Decorator to wrap
17 // `fn` func to apply meta information
18 func OnCompleteMetaOrOnAbortMeta(decorator Decorator, fn func(string) string) Decorator {
19 return OnCompleteMeta(OnAbortMeta(decorator, fn), fn)
20 }
00 package decor
11
2 // OnComplete returns decorator, which wraps provided decorator, with
3 // sole purpose to display provided message on complete event.
2 var (
3 _ Decorator = onCompleteWrapper{}
4 _ Wrapper = onCompleteWrapper{}
5 _ Decorator = onCompleteMetaWrapper{}
6 _ Wrapper = onCompleteMetaWrapper{}
7 )
8
9 // OnComplete wrap decorator.
10 // Displays provided message on complete event.
411 //
512 // `decorator` Decorator to wrap
6 //
7 // `message` message to display on complete event
8 //
13 // `message` message to display
914 func OnComplete(decorator Decorator, message string) Decorator {
10 d := &onCompleteWrapper{
11 Decorator: decorator,
12 msg: message,
15 if decorator == nil {
16 return nil
1317 }
14 if md, ok := decorator.(*mergeDecorator); ok {
15 d.Decorator, md.Decorator = md.Decorator, d
16 return md
17 }
18 return d
18 return onCompleteWrapper{decorator, message}
1919 }
2020
2121 type onCompleteWrapper struct {
2323 msg string
2424 }
2525
26 func (d *onCompleteWrapper) Decor(s Statistics) string {
26 func (d onCompleteWrapper) Decor(s Statistics) (string, int) {
2727 if s.Completed {
28 wc := d.GetConf()
29 return wc.FormatMsg(d.msg)
28 return d.Format(d.msg)
3029 }
3130 return d.Decorator.Decor(s)
3231 }
3332
34 func (d *onCompleteWrapper) Base() Decorator {
33 func (d onCompleteWrapper) Unwrap() Decorator {
3534 return d.Decorator
3635 }
36
37 // OnCompleteMeta wrap decorator.
38 // Provided fn is supposed to wrap output of given decorator
39 // with meta information like ANSI escape codes for example.
40 // Primary usage intention is to set SGR display attributes.
41 //
42 // `decorator` Decorator to wrap
43 // `fn` func to apply meta information
44 func OnCompleteMeta(decorator Decorator, fn func(string) string) Decorator {
45 if decorator == nil {
46 return nil
47 }
48 return onCompleteMetaWrapper{decorator, fn}
49 }
50
51 type onCompleteMetaWrapper struct {
52 Decorator
53 fn func(string) string
54 }
55
56 func (d onCompleteMetaWrapper) Decor(s Statistics) (string, int) {
57 if s.Completed {
58 str, width := d.Decorator.Decor(s)
59 return d.fn(str), width
60 }
61 return d.Decorator.Decor(s)
62 }
63
64 func (d onCompleteMetaWrapper) Unwrap() Decorator {
65 return d.Decorator
66 }
0 package decor
1
2 // OnCondition applies decorator only if a condition is true.
3 //
4 // `decorator` Decorator
5 //
6 // `cond` bool
7 func OnCondition(decorator Decorator, cond bool) Decorator {
8 return Conditional(cond, decorator, nil)
9 }
10
11 // OnPredicate applies decorator only if a predicate evaluates to true.
12 //
13 // `decorator` Decorator
14 //
15 // `predicate` func() bool
16 func OnPredicate(decorator Decorator, predicate func() bool) Decorator {
17 return Predicative(predicate, decorator, nil)
18 }
19
20 // Conditional returns decorator `a` if condition is true, otherwise
21 // decorator `b`.
22 //
23 // `cond` bool
24 //
25 // `a` Decorator
26 //
27 // `b` Decorator
28 func Conditional(cond bool, a, b Decorator) Decorator {
29 if cond {
30 return a
31 } else {
32 return b
33 }
34 }
35
36 // Predicative returns decorator `a` if predicate evaluates to true,
37 // otherwise decorator `b`.
38 //
39 // `predicate` func() bool
40 //
41 // `a` Decorator
42 //
43 // `b` Decorator
44 func Predicative(predicate func() bool, a, b Decorator) Decorator {
45 if predicate() {
46 return a
47 } else {
48 return b
49 }
50 }
11
22 import (
33 "fmt"
4 "io"
54 "strconv"
65
7 "github.com/vbauerster/mpb/v7/internal"
6 "github.com/vbauerster/mpb/v8/internal"
87 )
8
9 var _ fmt.Formatter = percentageType(0)
910
1011 type percentageType float64
1112
1213 func (s percentageType) Format(st fmt.State, verb rune) {
13 var prec int
14 prec := -1
1415 switch verb {
15 case 'd':
16 case 's':
17 prec = -1
18 default:
16 case 'f', 'e', 'E':
17 prec = 6 // default prec of fmt.Printf("%f|%e|%E")
18 fallthrough
19 case 'b', 'g', 'G', 'x', 'X':
1920 if p, ok := st.Precision(); ok {
2021 prec = p
21 } else {
22 prec = 6
2322 }
23 default:
24 verb, prec = 'f', 0
2425 }
2526
26 io.WriteString(st, strconv.FormatFloat(float64(s), 'f', prec, 64))
27
27 b := strconv.AppendFloat(make([]byte, 0, 16), float64(s), byte(verb), prec, 64)
2828 if st.Flag(' ') {
29 io.WriteString(st, " ")
29 b = append(b, ' ', '%')
30 } else {
31 b = append(b, '%')
3032 }
31 io.WriteString(st, "%")
33 _, err := st.Write(b)
34 if err != nil {
35 panic(err)
36 }
3237 }
3338
3439 // Percentage returns percentage decorator. It's a wrapper of NewPercentage.
3843
3944 // NewPercentage percentage decorator with custom format string.
4045 //
46 // `format` printf compatible verb
47 //
48 // `wcc` optional WC config
49 //
4150 // format examples:
4251 //
52 // format="%d" output: "1%"
53 // format="% d" output: "1 %"
4354 // format="%.1f" output: "1.0%"
4455 // format="% .1f" output: "1.0 %"
45 // format="%d" output: "1%"
46 // format="% d" output: "1 %"
47 //
56 // format="%f" output: "1.000000%"
57 // format="% f" output: "1.000000 %"
4858 func NewPercentage(format string, wcc ...WC) Decorator {
4959 if format == "" {
5060 format = "% d"
0 package decor
1
2 import (
3 "fmt"
4 "testing"
5 )
6
7 func TestPercentageType(t *testing.T) {
8 cases := map[string]struct {
9 value float64
10 verb string
11 expected string
12 }{
13 "10 %d": {10, "%d", "10%"},
14 "10 %s": {10, "%s", "10%"},
15 "10 %f": {10, "%f", "10.000000%"},
16 "10 %.6f": {10, "%.6f", "10.000000%"},
17 "10 %.0f": {10, "%.0f", "10%"},
18 "10 %.1f": {10, "%.1f", "10.0%"},
19 "10 %.2f": {10, "%.2f", "10.00%"},
20 "10 %.3f": {10, "%.3f", "10.000%"},
21
22 "10 % d": {10, "% d", "10 %"},
23 "10 % s": {10, "% s", "10 %"},
24 "10 % f": {10, "% f", "10.000000 %"},
25 "10 % .6f": {10, "% .6f", "10.000000 %"},
26 "10 % .0f": {10, "% .0f", "10 %"},
27 "10 % .1f": {10, "% .1f", "10.0 %"},
28 "10 % .2f": {10, "% .2f", "10.00 %"},
29 "10 % .3f": {10, "% .3f", "10.000 %"},
30
31 "10.5 %d": {10.5, "%d", "10%"},
32 "10.5 %s": {10.5, "%s", "10%"},
33 "10.5 %f": {10.5, "%f", "10.500000%"},
34 "10.5 %.6f": {10.5, "%.6f", "10.500000%"},
35 "10.5 %.0f": {10.5, "%.0f", "10%"},
36 "10.5 %.1f": {10.5, "%.1f", "10.5%"},
37 "10.5 %.2f": {10.5, "%.2f", "10.50%"},
38 "10.5 %.3f": {10.5, "%.3f", "10.500%"},
39
40 "10.5 % d": {10.5, "% d", "10 %"},
41 "10.5 % s": {10.5, "% s", "10 %"},
42 "10.5 % f": {10.5, "% f", "10.500000 %"},
43 "10.5 % .6f": {10.5, "% .6f", "10.500000 %"},
44 "10.5 % .0f": {10.5, "% .0f", "10 %"},
45 "10.5 % .1f": {10.5, "% .1f", "10.5 %"},
46 "10.5 % .2f": {10.5, "% .2f", "10.50 %"},
47 "10.5 % .3f": {10.5, "% .3f", "10.500 %"},
48 }
49 for name, tc := range cases {
50 t.Run(name, func(t *testing.T) {
51 got := fmt.Sprintf(tc.verb, percentageType(tc.value))
52 if got != tc.expected {
53 t.Fatalf("expected: %q, got: %q\n", tc.expected, got)
54 }
55 })
56 }
57 }
11
22 import (
33 "fmt"
4 "io"
5 "math"
64 "strconv"
75 )
86
97 //go:generate stringer -type=SizeB1024 -trimprefix=_i
108 //go:generate stringer -type=SizeB1000 -trimprefix=_
9
10 var (
11 _ fmt.Formatter = SizeB1024(0)
12 _ fmt.Stringer = SizeB1024(0)
13 _ fmt.Formatter = SizeB1000(0)
14 _ fmt.Stringer = SizeB1000(0)
15 )
1116
1217 const (
1318 _ib SizeB1024 = iota + 1
2328 type SizeB1024 int64
2429
2530 func (self SizeB1024) Format(st fmt.State, verb rune) {
26 var prec int
31 prec := -1
2732 switch verb {
28 case 'd':
29 case 's':
30 prec = -1
31 default:
33 case 'f', 'e', 'E':
34 prec = 6 // default prec of fmt.Printf("%f|%e|%E")
35 fallthrough
36 case 'b', 'g', 'G', 'x', 'X':
3237 if p, ok := st.Precision(); ok {
3338 prec = p
34 } else {
35 prec = 6
3639 }
40 default:
41 verb, prec = 'f', 0
3742 }
3843
3944 var unit SizeB1024
4651 unit = _iMiB
4752 case self < _iTiB:
4853 unit = _iGiB
49 case self <= math.MaxInt64:
54 default:
5055 unit = _iTiB
5156 }
5257
53 io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
54
58 b := strconv.AppendFloat(make([]byte, 0, 24), float64(self)/float64(unit), byte(verb), prec, 64)
5559 if st.Flag(' ') {
56 io.WriteString(st, " ")
60 b = append(b, ' ')
5761 }
58 io.WriteString(st, unit.String())
62 b = append(b, []byte(unit.String())...)
63 _, err := st.Write(b)
64 if err != nil {
65 panic(err)
66 }
5967 }
6068
6169 const (
7280 type SizeB1000 int64
7381
7482 func (self SizeB1000) Format(st fmt.State, verb rune) {
75 var prec int
83 prec := -1
7684 switch verb {
77 case 'd':
78 case 's':
79 prec = -1
80 default:
85 case 'f', 'e', 'E':
86 prec = 6 // default prec of fmt.Printf("%f|%e|%E")
87 fallthrough
88 case 'b', 'g', 'G', 'x', 'X':
8189 if p, ok := st.Precision(); ok {
8290 prec = p
83 } else {
84 prec = 6
8591 }
92 default:
93 verb, prec = 'f', 0
8694 }
8795
8896 var unit SizeB1000
95103 unit = _MB
96104 case self < _TB:
97105 unit = _GB
98 case self <= math.MaxInt64:
106 default:
99107 unit = _TB
100108 }
101109
102 io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
103
110 b := strconv.AppendFloat(make([]byte, 0, 24), float64(self)/float64(unit), byte(verb), prec, 64)
104111 if st.Flag(' ') {
105 io.WriteString(st, " ")
112 b = append(b, ' ')
106113 }
107 io.WriteString(st, unit.String())
114 b = append(b, []byte(unit.String())...)
115 _, err := st.Write(b)
116 if err != nil {
117 panic(err)
118 }
108119 }
1010 verb string
1111 expected string
1212 }{
13 "verb %f": {12345678, "%f", "11.773756MiB"},
14 "verb %.0f": {12345678, "%.0f", "12MiB"},
15 "verb %.1f": {12345678, "%.1f", "11.8MiB"},
16 "verb %.2f": {12345678, "%.2f", "11.77MiB"},
17 "verb %.3f": {12345678, "%.3f", "11.774MiB"},
18
13 "verb %d": {12345678, "%d", "12MiB"},
14 "verb %s": {12345678, "%s", "12MiB"},
15 "verb %f": {12345678, "%f", "11.773756MiB"},
16 "verb %.6f": {12345678, "%.6f", "11.773756MiB"},
17 "verb %.0f": {12345678, "%.0f", "12MiB"},
18 "verb %.1f": {12345678, "%.1f", "11.8MiB"},
19 "verb %.2f": {12345678, "%.2f", "11.77MiB"},
20 "verb %.3f": {12345678, "%.3f", "11.774MiB"},
21 "verb % d": {12345678, "% d", "12 MiB"},
22 "verb % s": {12345678, "% s", "12 MiB"},
1923 "verb % f": {12345678, "% f", "11.773756 MiB"},
24 "verb % .6f": {12345678, "% .6f", "11.773756 MiB"},
2025 "verb % .0f": {12345678, "% .0f", "12 MiB"},
2126 "verb % .1f": {12345678, "% .1f", "11.8 MiB"},
2227 "verb % .2f": {12345678, "% .2f", "11.77 MiB"},
2328 "verb % .3f": {12345678, "% .3f", "11.774 MiB"},
2429
25 "1000 %f": {1000, "%f", "1000.000000b"},
26 "1000 %d": {1000, "%d", "1000b"},
27 "1000 %s": {1000, "%s", "1000b"},
28 "1024 %f": {1024, "%f", "1.000000KiB"},
29 "1024 %d": {1024, "%d", "1KiB"},
30 "1024 %.1f": {1024, "%.1f", "1.0KiB"},
31 "1024 %s": {1024, "%s", "1KiB"},
32 "3*MiB+140KiB %f": {3*int64(_iMiB) + 140*int64(_iKiB), "%f", "3.136719MiB"},
33 "3*MiB+140KiB %d": {3*int64(_iMiB) + 140*int64(_iKiB), "%d", "3MiB"},
34 "3*MiB+140KiB %.1f": {3*int64(_iMiB) + 140*int64(_iKiB), "%.1f", "3.1MiB"},
35 "3*MiB+140KiB %s": {3*int64(_iMiB) + 140*int64(_iKiB), "%s", "3.13671875MiB"},
36 "2*GiB %f": {2 * int64(_iGiB), "%f", "2.000000GiB"},
37 "2*GiB %d": {2 * int64(_iGiB), "%d", "2GiB"},
38 "2*GiB %.1f": {2 * int64(_iGiB), "%.1f", "2.0GiB"},
39 "2*GiB %s": {2 * int64(_iGiB), "%s", "2GiB"},
40 "4*TiB %f": {4 * int64(_iTiB), "%f", "4.000000TiB"},
41 "4*TiB %d": {4 * int64(_iTiB), "%d", "4TiB"},
42 "4*TiB %.1f": {4 * int64(_iTiB), "%.1f", "4.0TiB"},
43 "4*TiB %s": {4 * int64(_iTiB), "%s", "4TiB"},
30 "1000 %d": {1000, "%d", "1000b"},
31 "1000 %s": {1000, "%s", "1000b"},
32 "1000 %f": {1000, "%f", "1000.000000b"},
33 "1000 %.6f": {1000, "%.6f", "1000.000000b"},
34 "1000 %.0f": {1000, "%.0f", "1000b"},
35 "1000 %.1f": {1000, "%.1f", "1000.0b"},
36 "1000 %.2f": {1000, "%.2f", "1000.00b"},
37 "1000 %.3f": {1000, "%.3f", "1000.000b"},
38 "1024 %d": {1024, "%d", "1KiB"},
39 "1024 %s": {1024, "%s", "1KiB"},
40 "1024 %f": {1024, "%f", "1.000000KiB"},
41 "1024 %.6f": {1024, "%.6f", "1.000000KiB"},
42 "1024 %.0f": {1024, "%.0f", "1KiB"},
43 "1024 %.1f": {1024, "%.1f", "1.0KiB"},
44 "1024 %.2f": {1024, "%.2f", "1.00KiB"},
45 "1024 %.3f": {1024, "%.3f", "1.000KiB"},
46
47 "3*MiB+100KiB %d": {3*int64(_iMiB) + 100*int64(_iKiB), "%d", "3MiB"},
48 "3*MiB+100KiB %s": {3*int64(_iMiB) + 100*int64(_iKiB), "%s", "3MiB"},
49 "3*MiB+100KiB %f": {3*int64(_iMiB) + 100*int64(_iKiB), "%f", "3.097656MiB"},
50 "3*MiB+100KiB %.6f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.6f", "3.097656MiB"},
51 "3*MiB+100KiB %.0f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.0f", "3MiB"},
52 "3*MiB+100KiB %.1f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.1f", "3.1MiB"},
53 "3*MiB+100KiB %.2f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.2f", "3.10MiB"},
54 "3*MiB+100KiB %.3f": {3*int64(_iMiB) + 100*int64(_iKiB), "%.3f", "3.098MiB"},
55
56 "2*GiB %d": {2 * int64(_iGiB), "%d", "2GiB"},
57 "2*GiB %s": {2 * int64(_iGiB), "%s", "2GiB"},
58 "2*GiB %f": {2 * int64(_iGiB), "%f", "2.000000GiB"},
59 "2*GiB %.6f": {2 * int64(_iGiB), "%.6f", "2.000000GiB"},
60 "2*GiB %.0f": {2 * int64(_iGiB), "%.0f", "2GiB"},
61 "2*GiB %.1f": {2 * int64(_iGiB), "%.1f", "2.0GiB"},
62 "2*GiB %.2f": {2 * int64(_iGiB), "%.2f", "2.00GiB"},
63 "2*GiB %.3f": {2 * int64(_iGiB), "%.3f", "2.000GiB"},
64
65 "4*TiB %d": {4 * int64(_iTiB), "%d", "4TiB"},
66 "4*TiB %s": {4 * int64(_iTiB), "%s", "4TiB"},
67 "4*TiB %f": {4 * int64(_iTiB), "%f", "4.000000TiB"},
68 "4*TiB %.6f": {4 * int64(_iTiB), "%.6f", "4.000000TiB"},
69 "4*TiB %.0f": {4 * int64(_iTiB), "%.0f", "4TiB"},
70 "4*TiB %.1f": {4 * int64(_iTiB), "%.1f", "4.0TiB"},
71 "4*TiB %.2f": {4 * int64(_iTiB), "%.2f", "4.00TiB"},
72 "4*TiB %.3f": {4 * int64(_iTiB), "%.3f", "4.000TiB"},
4473 }
4574 for name, tc := range cases {
4675 t.Run(name, func(t *testing.T) {
5887 verb string
5988 expected string
6089 }{
61 "verb %f": {12345678, "%f", "12.345678MB"},
62 "verb %.0f": {12345678, "%.0f", "12MB"},
63 "verb %.1f": {12345678, "%.1f", "12.3MB"},
64 "verb %.2f": {12345678, "%.2f", "12.35MB"},
65 "verb %.3f": {12345678, "%.3f", "12.346MB"},
66
90 "verb %d": {12345678, "%d", "12MB"},
91 "verb %s": {12345678, "%s", "12MB"},
92 "verb %f": {12345678, "%f", "12.345678MB"},
93 "verb %.6f": {12345678, "%.6f", "12.345678MB"},
94 "verb %.0f": {12345678, "%.0f", "12MB"},
95 "verb %.1f": {12345678, "%.1f", "12.3MB"},
96 "verb %.2f": {12345678, "%.2f", "12.35MB"},
97 "verb %.3f": {12345678, "%.3f", "12.346MB"},
98 "verb % d": {12345678, "% d", "12 MB"},
99 "verb % s": {12345678, "% s", "12 MB"},
67100 "verb % f": {12345678, "% f", "12.345678 MB"},
101 "verb % .6f": {12345678, "% .6f", "12.345678 MB"},
68102 "verb % .0f": {12345678, "% .0f", "12 MB"},
69103 "verb % .1f": {12345678, "% .1f", "12.3 MB"},
70104 "verb % .2f": {12345678, "% .2f", "12.35 MB"},
71105 "verb % .3f": {12345678, "% .3f", "12.346 MB"},
72106
73 "1000 %f": {1000, "%f", "1.000000KB"},
74 "1000 %d": {1000, "%d", "1KB"},
75 "1000 %s": {1000, "%s", "1KB"},
76 "1024 %f": {1024, "%f", "1.024000KB"},
77 "1024 %d": {1024, "%d", "1KB"},
78 "1024 %.1f": {1024, "%.1f", "1.0KB"},
79 "1024 %s": {1024, "%s", "1.024KB"},
80 "3*MB+140*KB %f": {3*int64(_MB) + 140*int64(_KB), "%f", "3.140000MB"},
81 "3*MB+140*KB %d": {3*int64(_MB) + 140*int64(_KB), "%d", "3MB"},
82 "3*MB+140*KB %.1f": {3*int64(_MB) + 140*int64(_KB), "%.1f", "3.1MB"},
83 "3*MB+140*KB %s": {3*int64(_MB) + 140*int64(_KB), "%s", "3.14MB"},
84 "2*GB %f": {2 * int64(_GB), "%f", "2.000000GB"},
85 "2*GB %d": {2 * int64(_GB), "%d", "2GB"},
86 "2*GB %.1f": {2 * int64(_GB), "%.1f", "2.0GB"},
87 "2*GB %s": {2 * int64(_GB), "%s", "2GB"},
88 "4*TB %f": {4 * int64(_TB), "%f", "4.000000TB"},
89 "4*TB %d": {4 * int64(_TB), "%d", "4TB"},
90 "4*TB %.1f": {4 * int64(_TB), "%.1f", "4.0TB"},
91 "4*TB %s": {4 * int64(_TB), "%s", "4TB"},
107 "1000 %d": {1000, "%d", "1KB"},
108 "1000 %s": {1000, "%s", "1KB"},
109 "1000 %f": {1000, "%f", "1.000000KB"},
110 "1000 %.6f": {1000, "%.6f", "1.000000KB"},
111 "1000 %.0f": {1000, "%.0f", "1KB"},
112 "1000 %.1f": {1000, "%.1f", "1.0KB"},
113 "1000 %.2f": {1000, "%.2f", "1.00KB"},
114 "1000 %.3f": {1000, "%.3f", "1.000KB"},
115 "1024 %d": {1024, "%d", "1KB"},
116 "1024 %s": {1024, "%s", "1KB"},
117 "1024 %f": {1024, "%f", "1.024000KB"},
118 "1024 %.6f": {1024, "%.6f", "1.024000KB"},
119 "1024 %.0f": {1024, "%.0f", "1KB"},
120 "1024 %.1f": {1024, "%.1f", "1.0KB"},
121 "1024 %.2f": {1024, "%.2f", "1.02KB"},
122 "1024 %.3f": {1024, "%.3f", "1.024KB"},
123
124 "3*MB+100*KB %d": {3*int64(_MB) + 100*int64(_KB), "%d", "3MB"},
125 "3*MB+100*KB %s": {3*int64(_MB) + 100*int64(_KB), "%s", "3MB"},
126 "3*MB+100*KB %f": {3*int64(_MB) + 100*int64(_KB), "%f", "3.100000MB"},
127 "3*MB+100*KB %.6f": {3*int64(_MB) + 100*int64(_KB), "%.6f", "3.100000MB"},
128 "3*MB+100*KB %.0f": {3*int64(_MB) + 100*int64(_KB), "%.0f", "3MB"},
129 "3*MB+100*KB %.1f": {3*int64(_MB) + 100*int64(_KB), "%.1f", "3.1MB"},
130 "3*MB+100*KB %.2f": {3*int64(_MB) + 100*int64(_KB), "%.2f", "3.10MB"},
131 "3*MB+100*KB %.3f": {3*int64(_MB) + 100*int64(_KB), "%.3f", "3.100MB"},
132
133 "2*GB %d": {2 * int64(_GB), "%d", "2GB"},
134 "2*GB %s": {2 * int64(_GB), "%s", "2GB"},
135 "2*GB %f": {2 * int64(_GB), "%f", "2.000000GB"},
136 "2*GB %.6f": {2 * int64(_GB), "%.6f", "2.000000GB"},
137 "2*GB %.0f": {2 * int64(_GB), "%.0f", "2GB"},
138 "2*GB %.1f": {2 * int64(_GB), "%.1f", "2.0GB"},
139 "2*GB %.2f": {2 * int64(_GB), "%.2f", "2.00GB"},
140 "2*GB %.3f": {2 * int64(_GB), "%.3f", "2.000GB"},
141
142 "4*TB %d": {4 * int64(_TB), "%d", "4TB"},
143 "4*TB %s": {4 * int64(_TB), "%s", "4TB"},
144 "4*TB %f": {4 * int64(_TB), "%f", "4.000000TB"},
145 "4*TB %.6f": {4 * int64(_TB), "%.6f", "4.000000TB"},
146 "4*TB %.0f": {4 * int64(_TB), "%.0f", "4TB"},
147 "4*TB %.1f": {4 * int64(_TB), "%.1f", "4.0TB"},
148 "4*TB %.2f": {4 * int64(_TB), "%.2f", "4.00TB"},
149 "4*TB %.3f": {4 * int64(_TB), "%.3f", "4.000TB"},
92150 }
93151 for name, tc := range cases {
94152 t.Run(name, func(t *testing.T) {
88 "github.com/VividCortex/ewma"
99 )
1010
11 var (
12 _ Decorator = (*movingAverageSpeed)(nil)
13 _ EwmaDecorator = (*movingAverageSpeed)(nil)
14 _ Decorator = (*averageSpeed)(nil)
15 _ AverageDecorator = (*averageSpeed)(nil)
16 )
17
1118 // FmtAsSpeed adds "/s" to the end of the input formatter. To be
1219 // used with SizeB1000 or SizeB1024 types, for example:
1320 //
1421 // fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048)))
15 //
1622 func FmtAsSpeed(input fmt.Formatter) fmt.Formatter {
17 return &speedFormatter{input}
23 return speedFormatter{input}
1824 }
1925
2026 type speedFormatter struct {
2127 fmt.Formatter
2228 }
2329
24 func (self *speedFormatter) Format(st fmt.State, verb rune) {
30 func (self speedFormatter) Format(st fmt.State, verb rune) {
2531 self.Formatter.Format(st, verb)
26 io.WriteString(st, "/s")
32 _, err := io.WriteString(st, "/s")
33 if err != nil {
34 panic(err)
35 }
2736 }
2837
2938 // EwmaSpeed exponential-weighted-moving-average based speed decorator.
30 // For this decorator to work correctly you have to measure each
31 // iteration's duration and pass it to the
32 // *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
33 func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
39 // For this decorator to work correctly you have to measure each iteration's
40 // duration and pass it to one of the (*Bar).EwmaIncr... family methods.
41 func EwmaSpeed(unit interface{}, format string, age float64, wcc ...WC) Decorator {
3442 var average ewma.MovingAverage
3543 if age == 0 {
3644 average = ewma.NewMovingAverage()
4351 // MovingAverageSpeed decorator relies on MovingAverage implementation
4452 // to calculate its average.
4553 //
46 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
54 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
4755 //
4856 // `format` printf compatible verb for value, like "%f" or "%d"
4957 //
5361 //
5462 // format examples:
5563 //
56 // unit=UnitKiB, format="%.1f" output: "1.0MiB/s"
57 // unit=UnitKiB, format="% .1f" output: "1.0 MiB/s"
58 // unit=UnitKB, format="%.1f" output: "1.0MB/s"
59 // unit=UnitKB, format="% .1f" output: "1.0 MB/s"
60 //
61 func MovingAverageSpeed(unit int, format string, average ewma.MovingAverage, wcc ...WC) Decorator {
62 if format == "" {
63 format = "%.0f"
64 }
64 // unit=SizeB1024(0), format="%.1f" output: "1.0MiB/s"
65 // unit=SizeB1024(0), format="% .1f" output: "1.0 MiB/s"
66 // unit=SizeB1000(0), format="%.1f" output: "1.0MB/s"
67 // unit=SizeB1000(0), format="% .1f" output: "1.0 MB/s"
68 func MovingAverageSpeed(unit interface{}, format string, average ewma.MovingAverage, wcc ...WC) Decorator {
6569 d := &movingAverageSpeed{
6670 WC: initWC(wcc...),
6771 average: average,
7781 msg string
7882 }
7983
80 func (d *movingAverageSpeed) Decor(s Statistics) string {
84 func (d *movingAverageSpeed) Decor(s Statistics) (string, int) {
8185 if !s.Completed {
8286 var speed float64
8387 if v := d.average.Value(); v > 0 {
8589 }
8690 d.msg = d.producer(speed * 1e9)
8791 }
88 return d.FormatMsg(d.msg)
92 return d.Format(d.msg)
8993 }
9094
9195 func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) {
98102
99103 // AverageSpeed decorator with dynamic unit measure adjustment. It's
100104 // a wrapper of NewAverageSpeed.
101 func AverageSpeed(unit int, format string, wcc ...WC) Decorator {
105 func AverageSpeed(unit interface{}, format string, wcc ...WC) Decorator {
102106 return NewAverageSpeed(unit, format, time.Now(), wcc...)
103107 }
104108
105109 // NewAverageSpeed decorator with dynamic unit measure adjustment and
106110 // user provided start time.
107111 //
108 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
112 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
109113 //
110114 // `format` printf compatible verb for value, like "%f" or "%d"
111115 //
115119 //
116120 // format examples:
117121 //
118 // unit=UnitKiB, format="%.1f" output: "1.0MiB/s"
119 // unit=UnitKiB, format="% .1f" output: "1.0 MiB/s"
120 // unit=UnitKB, format="%.1f" output: "1.0MB/s"
121 // unit=UnitKB, format="% .1f" output: "1.0 MB/s"
122 //
123 func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator {
124 if format == "" {
125 format = "%.0f"
126 }
122 // unit=SizeB1024(0), format="%.1f" output: "1.0MiB/s"
123 // unit=SizeB1024(0), format="% .1f" output: "1.0 MiB/s"
124 // unit=SizeB1000(0), format="%.1f" output: "1.0MB/s"
125 // unit=SizeB1000(0), format="% .1f" output: "1.0 MB/s"
126 func NewAverageSpeed(unit interface{}, format string, startTime time.Time, wcc ...WC) Decorator {
127127 d := &averageSpeed{
128128 WC: initWC(wcc...),
129129 startTime: startTime,
139139 msg string
140140 }
141141
142 func (d *averageSpeed) Decor(s Statistics) string {
142 func (d *averageSpeed) Decor(s Statistics) (string, int) {
143143 if !s.Completed {
144144 speed := float64(s.Current) / float64(time.Since(d.startTime))
145145 d.msg = d.producer(speed * 1e9)
146146 }
147
148 return d.FormatMsg(d.msg)
147 return d.Format(d.msg)
149148 }
150149
151150 func (d *averageSpeed) AverageAdjust(startTime time.Time) {
152151 d.startTime = startTime
153152 }
154153
155 func chooseSpeedProducer(unit int, format string) func(float64) string {
156 switch unit {
157 case UnitKiB:
154 func chooseSpeedProducer(unit interface{}, format string) func(float64) string {
155 switch unit.(type) {
156 case SizeB1024:
157 if format == "" {
158 format = "% d"
159 }
158160 return func(speed float64) string {
159161 return fmt.Sprintf(format, FmtAsSpeed(SizeB1024(math.Round(speed))))
160162 }
161 case UnitKB:
163 case SizeB1000:
164 if format == "" {
165 format = "% d"
166 }
162167 return func(speed float64) string {
163168 return fmt.Sprintf(format, FmtAsSpeed(SizeB1000(math.Round(speed))))
164169 }
165170 default:
171 if format == "" {
172 format = "%f"
173 }
166174 return func(speed float64) string {
167175 return fmt.Sprintf(format, speed)
168176 }
44 "time"
55 )
66
7 func TestSpeedKiBDecor(t *testing.T) {
7 func TestAverageSpeedSizeB1024(t *testing.T) {
88 cases := []struct {
99 name string
1010 fmt string
11 unit int
11 unit interface{}
1212 current int64
1313 elapsed time.Duration
1414 expected string
1515 }{
1616 {
1717 name: "empty fmt",
18 unit: UnitKiB,
18 unit: SizeB1024(0),
1919 fmt: "",
2020 current: 0,
2121 elapsed: time.Second,
22 expected: "0 b/s",
23 },
24 {
25 name: "SizeB1024(0):%d:0b",
26 unit: SizeB1024(0),
27 fmt: "%d",
28 current: 0,
29 elapsed: time.Second,
2230 expected: "0b/s",
2331 },
2432 {
25 name: "UnitKiB:%d:0b",
26 unit: UnitKiB,
27 fmt: "%d",
28 current: 0,
29 elapsed: time.Second,
30 expected: "0b/s",
31 },
32 {
33 name: "UnitKiB:% .2f:0b",
34 unit: UnitKiB,
33 name: "SizeB1024(0):%f:0b",
34 unit: SizeB1024(0),
35 fmt: "%f",
36 current: 0,
37 elapsed: time.Second,
38 expected: "0.000000b/s",
39 },
40 {
41 name: "SizeB1024(0):% .2f:0b",
42 unit: SizeB1024(0),
3543 fmt: "% .2f",
3644 current: 0,
3745 elapsed: time.Second,
3846 expected: "0.00 b/s",
3947 },
4048 {
41 name: "UnitKiB:%d:1b",
42 unit: UnitKiB,
49 name: "SizeB1024(0):%d:1b",
50 unit: SizeB1024(0),
4351 fmt: "%d",
4452 current: 1,
4553 elapsed: time.Second,
4654 expected: "1b/s",
4755 },
4856 {
49 name: "UnitKiB:% .2f:1b",
50 unit: UnitKiB,
57 name: "SizeB1024(0):% .2f:1b",
58 unit: SizeB1024(0),
5159 fmt: "% .2f",
5260 current: 1,
5361 elapsed: time.Second,
5462 expected: "1.00 b/s",
5563 },
5664 {
57 name: "UnitKiB:%d:KiB",
58 unit: UnitKiB,
65 name: "SizeB1024(0):%d:KiB",
66 unit: SizeB1024(0),
5967 fmt: "%d",
6068 current: 2 * int64(_iKiB),
6169 elapsed: 1 * time.Second,
6270 expected: "2KiB/s",
6371 },
6472 {
65 name: "UnitKiB:% .f:KiB",
66 unit: UnitKiB,
73 name: "SizeB1024(0):% .f:KiB",
74 unit: SizeB1024(0),
6775 fmt: "% .2f",
6876 current: 2 * int64(_iKiB),
6977 elapsed: 1 * time.Second,
7078 expected: "2.00 KiB/s",
7179 },
7280 {
73 name: "UnitKiB:%d:MiB",
74 unit: UnitKiB,
81 name: "SizeB1024(0):%d:MiB",
82 unit: SizeB1024(0),
7583 fmt: "%d",
7684 current: 2 * int64(_iMiB),
7785 elapsed: 1 * time.Second,
7886 expected: "2MiB/s",
7987 },
8088 {
81 name: "UnitKiB:% .2f:MiB",
82 unit: UnitKiB,
89 name: "SizeB1024(0):% .2f:MiB",
90 unit: SizeB1024(0),
8391 fmt: "% .2f",
8492 current: 2 * int64(_iMiB),
8593 elapsed: 1 * time.Second,
8694 expected: "2.00 MiB/s",
8795 },
8896 {
89 name: "UnitKiB:%d:GiB",
90 unit: UnitKiB,
97 name: "SizeB1024(0):%d:GiB",
98 unit: SizeB1024(0),
9199 fmt: "%d",
92100 current: 2 * int64(_iGiB),
93101 elapsed: 1 * time.Second,
94102 expected: "2GiB/s",
95103 },
96104 {
97 name: "UnitKiB:% .2f:GiB",
98 unit: UnitKiB,
105 name: "SizeB1024(0):% .2f:GiB",
106 unit: SizeB1024(0),
99107 fmt: "% .2f",
100108 current: 2 * int64(_iGiB),
101109 elapsed: 1 * time.Second,
102110 expected: "2.00 GiB/s",
103111 },
104112 {
105 name: "UnitKiB:%d:TiB",
106 unit: UnitKiB,
113 name: "SizeB1024(0):%d:TiB",
114 unit: SizeB1024(0),
107115 fmt: "%d",
108116 current: 2 * int64(_iTiB),
109117 elapsed: 1 * time.Second,
110118 expected: "2TiB/s",
111119 },
112120 {
113 name: "UnitKiB:% .2f:TiB",
114 unit: UnitKiB,
121 name: "SizeB1024(0):% .2f:TiB",
122 unit: SizeB1024(0),
115123 fmt: "% .2f",
116124 current: 2 * int64(_iTiB),
117125 elapsed: 1 * time.Second,
124132 stat := Statistics{
125133 Current: tc.current,
126134 }
127 res := decor.Decor(stat)
135 res, _ := decor.Decor(stat)
128136 if res != tc.expected {
129137 t.Fatalf("expected: %q, got: %q\n", tc.expected, res)
130138 }
132140 }
133141 }
134142
135 func TestSpeedKBDecor(t *testing.T) {
143 func TestAverageSpeedSizeB1000(t *testing.T) {
136144 cases := []struct {
137145 name string
138146 fmt string
139 unit int
147 unit interface{}
140148 current int64
141149 elapsed time.Duration
142150 expected string
143151 }{
144152 {
145153 name: "empty fmt",
146 unit: UnitKB,
154 unit: SizeB1000(0),
147155 fmt: "",
148156 current: 0,
149157 elapsed: time.Second,
158 expected: "0 b/s",
159 },
160 {
161 name: "SizeB1000(0):%d:0b",
162 unit: SizeB1000(0),
163 fmt: "%d",
164 current: 0,
165 elapsed: time.Second,
150166 expected: "0b/s",
151167 },
152168 {
153 name: "UnitKB:%d:0b",
154 unit: UnitKB,
155 fmt: "%d",
156 current: 0,
157 elapsed: time.Second,
158 expected: "0b/s",
159 },
160 {
161 name: "UnitKB:% .2f:0b",
162 unit: UnitKB,
169 name: "SizeB1000(0):%f:0b",
170 unit: SizeB1000(0),
171 fmt: "%f",
172 current: 0,
173 elapsed: time.Second,
174 expected: "0.000000b/s",
175 },
176 {
177 name: "SizeB1000(0):% .2f:0b",
178 unit: SizeB1000(0),
163179 fmt: "% .2f",
164180 current: 0,
165181 elapsed: time.Second,
166182 expected: "0.00 b/s",
167183 },
168184 {
169 name: "UnitKB:%d:1b",
170 unit: UnitKB,
185 name: "SizeB1000(0):%d:1b",
186 unit: SizeB1000(0),
171187 fmt: "%d",
172188 current: 1,
173189 elapsed: time.Second,
174190 expected: "1b/s",
175191 },
176192 {
177 name: "UnitKB:% .2f:1b",
178 unit: UnitKB,
193 name: "SizeB1000(0):% .2f:1b",
194 unit: SizeB1000(0),
179195 fmt: "% .2f",
180196 current: 1,
181197 elapsed: time.Second,
182198 expected: "1.00 b/s",
183199 },
184200 {
185 name: "UnitKB:%d:KB",
186 unit: UnitKB,
201 name: "SizeB1000(0):%d:KB",
202 unit: SizeB1000(0),
187203 fmt: "%d",
188204 current: 2 * int64(_KB),
189205 elapsed: 1 * time.Second,
190206 expected: "2KB/s",
191207 },
192208 {
193 name: "UnitKB:% .f:KB",
194 unit: UnitKB,
209 name: "SizeB1000(0):% .f:KB",
210 unit: SizeB1000(0),
195211 fmt: "% .2f",
196212 current: 2 * int64(_KB),
197213 elapsed: 1 * time.Second,
198214 expected: "2.00 KB/s",
199215 },
200216 {
201 name: "UnitKB:%d:MB",
202 unit: UnitKB,
217 name: "SizeB1000(0):%d:MB",
218 unit: SizeB1000(0),
203219 fmt: "%d",
204220 current: 2 * int64(_MB),
205221 elapsed: 1 * time.Second,
206222 expected: "2MB/s",
207223 },
208224 {
209 name: "UnitKB:% .2f:MB",
210 unit: UnitKB,
225 name: "SizeB1000(0):% .2f:MB",
226 unit: SizeB1000(0),
211227 fmt: "% .2f",
212228 current: 2 * int64(_MB),
213229 elapsed: 1 * time.Second,
214230 expected: "2.00 MB/s",
215231 },
216232 {
217 name: "UnitKB:%d:GB",
218 unit: UnitKB,
233 name: "SizeB1000(0):%d:GB",
234 unit: SizeB1000(0),
219235 fmt: "%d",
220236 current: 2 * int64(_GB),
221237 elapsed: 1 * time.Second,
222238 expected: "2GB/s",
223239 },
224240 {
225 name: "UnitKB:% .2f:GB",
226 unit: UnitKB,
241 name: "SizeB1000(0):% .2f:GB",
242 unit: SizeB1000(0),
227243 fmt: "% .2f",
228244 current: 2 * int64(_GB),
229245 elapsed: 1 * time.Second,
230246 expected: "2.00 GB/s",
231247 },
232248 {
233 name: "UnitKB:%d:TB",
234 unit: UnitKB,
249 name: "SizeB1000(0):%d:TB",
250 unit: SizeB1000(0),
235251 fmt: "%d",
236252 current: 2 * int64(_TB),
237253 elapsed: 1 * time.Second,
238254 expected: "2TB/s",
239255 },
240256 {
241 name: "UnitKB:% .2f:TB",
242 unit: UnitKB,
257 name: "SizeB1000(0):% .2f:TB",
258 unit: SizeB1000(0),
243259 fmt: "% .2f",
244260 current: 2 * int64(_TB),
245261 elapsed: 1 * time.Second,
252268 stat := Statistics{
253269 Current: tc.current,
254270 }
255 res := decor.Decor(stat)
271 res, _ := decor.Decor(stat)
256272 if res != tc.expected {
257273 t.Fatalf("expected: %q, got: %q\n", tc.expected, res)
258274 }
00 package decor
11
2 var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
2 var defaultSpinnerStyle = [...]string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
33
44 // Spinner returns spinner decorator.
55 //
88 // `wcc` optional WC config
99 func Spinner(frames []string, wcc ...WC) Decorator {
1010 if len(frames) == 0 {
11 frames = defaultSpinnerStyle
11 frames = defaultSpinnerStyle[:]
1212 }
1313 var count uint
1414 f := func(s Statistics) string {
00 package mpb_test
11
22 import (
3 "sync"
43 "testing"
54
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
5 "github.com/vbauerster/mpb/v8"
6 "github.com/vbauerster/mpb/v8/decor"
87 )
98
109 func TestNameDecorator(t *testing.T) {
3130 }
3231
3332 for _, test := range tests {
34 got := test.decorator.Decor(decor.Statistics{})
33 got, _ := test.decorator.Decor(decor.Statistics{})
3534 if got != test.want {
3635 t.Errorf("Want: %q, Got: %q\n", test.want, got)
3736 }
182181 }
183182
184183 for _, columnCase := range testCases {
185 mpb.SyncWidth(toSyncMatrix(columnCase))
186 numBars := len(columnCase)
187 gott := make([]chan string, numBars)
188 wg := new(sync.WaitGroup)
189 wg.Add(numBars)
190 for i, step := range columnCase {
184 mpb.SyncWidth(toSyncMatrix(columnCase), nil)
185 var results []chan string
186 for _, step := range columnCase {
191187 step := step
192 ch := make(chan string, 1)
188 ch := make(chan string)
193189 go func() {
194 defer wg.Done()
195 ch <- step.decorator.Decor(step.stat)
190 str, _ := step.decorator.Decor(step.stat)
191 ch <- str
196192 }()
197 gott[i] = ch
198 }
199 wg.Wait()
200
201 for i, ch := range gott {
202 got := <-ch
193 results = append(results, ch)
194 }
195
196 for i, ch := range results {
197 res := <-ch
203198 want := columnCase[i].want
204 if got != want {
205 t.Errorf("Want: %q, Got: %q\n", want, got)
199 if res != want {
200 t.Errorf("Want: %q, Got: %q\n", want, res)
206201 }
207202 }
208
209203 }
210204 }
211205
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 {
1111 style BarStyleComposer
1919 }{
2020 0: {
2121 {
22 style: BarStyle(),
2223 name: "t,c{60,20}",
2324 total: 60,
2425 current: 20,
2526 want: "",
2627 },
2728 {
29 style: BarStyle(),
2830 name: "t,c{60,20}trim",
2931 total: 60,
3032 current: 20,
3436 },
3537 1: {
3638 {
39 style: BarStyle(),
3740 name: "t,c{60,20}",
3841 total: 60,
3942 current: 20,
4043 want: "",
4144 },
4245 {
46 style: BarStyle(),
4347 name: "t,c{60,20}trim",
4448 total: 60,
4549 current: 20,
4953 },
5054 2: {
5155 {
56 style: BarStyle(),
5257 name: "t,c{60,20}",
5358 total: 60,
5459 current: 20,
5560 want: " ",
5661 },
5762 {
63 style: BarStyle(),
5864 name: "t,c{60,20}trim",
5965 total: 60,
6066 current: 20,
6470 },
6571 3: {
6672 {
73 style: BarStyle(),
6774 name: "t,c{60,20}",
6875 total: 60,
6976 current: 20,
7077 want: " ",
7178 },
7279 {
80 style: BarStyle(),
7381 name: "t,c{60,20}trim",
7482 total: 60,
7583 current: 20,
7684 trim: true,
7785 want: "[-]",
7886 },
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 },
79117 },
80118 4: {
81119 {
120 style: BarStyle(),
82121 name: "t,c{60,20}",
83122 total: 60,
84123 current: 20,
85124 want: " [] ",
86125 },
87126 {
127 style: BarStyle(),
88128 name: "t,c{60,20}trim",
89129 total: 60,
90130 current: 20,
91131 trim: true,
92132 want: "[>-]",
93133 },
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 },
94164 },
95165 5: {
96166 {
167 style: BarStyle(),
97168 name: "t,c{60,20}",
98169 total: 60,
99170 current: 20,
100171 want: " [-] ",
101172 },
102173 {
174 style: BarStyle(),
103175 name: "t,c{60,20}trim",
104176 total: 60,
105177 current: 20,
106178 trim: true,
107179 want: "[>--]",
108180 },
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 },
109211 },
110212 6: {
111213 {
214 style: BarStyle(),
112215 name: "t,c{60,20}",
113216 total: 60,
114217 current: 20,
115218 want: " [>-] ",
116219 },
117220 {
221 style: BarStyle(),
118222 name: "t,c{60,20}trim",
119223 total: 60,
120224 current: 20,
121225 trim: true,
122226 want: "[>---]",
123227 },
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 },
124258 },
125259 7: {
126260 {
261 style: BarStyle(),
127262 name: "t,c{60,20}",
128263 total: 60,
129264 current: 20,
130265 want: " [>--] ",
131266 },
132267 {
268 style: BarStyle(),
133269 name: "t,c{60,20}trim",
134270 total: 60,
135271 current: 20,
136272 trim: true,
137273 want: "[=>---]",
138274 },
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 },
139305 },
140306 8: {
141307 {
308 style: BarStyle(),
142309 name: "t,c{60,20}",
143310 total: 60,
144311 current: 20,
145312 want: " [>---] ",
146313 },
147314 {
315 style: BarStyle(),
148316 name: "t,c{60,20}trim",
149317 total: 60,
150318 current: 20,
151319 trim: true,
152320 want: "[=>----]",
153321 },
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 },
154352 },
155353 80: {
156354 {
355 style: BarStyle(),
157356 name: "t,c{60,20}",
158357 total: 60,
159358 current: 20,
160359 want: " [========================>---------------------------------------------------] ",
161360 },
162361 {
362 style: BarStyle(),
163363 name: "t,c{60,20}trim",
164364 total: 60,
165365 current: 20,
167367 want: "[=========================>----------------------------------------------------]",
168368 },
169369 {
370 style: BarStyle(),
170371 name: "t,c,bw{60,20,60}",
171372 total: 60,
172373 current: 20,
174375 want: " [==================>---------------------------------------] ",
175376 },
176377 {
378 style: BarStyle(),
177379 name: "t,c,bw{60,20,60}trim",
178380 total: 60,
179381 current: 20,
181383 trim: true,
182384 want: "[==================>---------------------------------------]",
183385 },
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 },
184450 },
185451 99: {
186452 {
187 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
188 name: "[のだつ] t,c{99,1}",
189 total: 99,
453 style: BarStyle(),
454 name: "t,c{100,1}",
455 total: 100,
190456 current: 1,
191 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
192 },
193 {
194 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
195 name: "[のだつ] t,c{99,2}",
196 total: 99,
197 current: 2,
198 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
199 },
200 {
201 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
202 name: "[のだつ] t,c{99,3}",
203 total: 99,
204 current: 3,
205 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
206 },
207 {
208 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
209 name: "[のだつ] t,c{99,4}",
210 total: 99,
211 current: 4,
212 want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
213 },
214 {
215 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
216 name: "[のだつ] t,c{99,5}",
217 total: 99,
218 current: 5,
219 want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
220 },
221 {
222 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
223 name: "[のだつ] t,c{99,6}",
224 total: 99,
225 current: 6,
226 want: " [ののだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
227 },
228 {
229 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]").Reverse(),
230 name: "[のだつ] t,c{99,6}rev",
231 total: 99,
232 current: 6,
233 want: " […つつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつだのの] ",
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: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
234598 },
235599 },
236600 100: {
237601 {
602 style: BarStyle(),
238603 name: "t,c{100,0}",
239604 total: 100,
240605 current: 0,
241606 want: " [------------------------------------------------------------------------------------------------] ",
242607 },
243608 {
609 style: BarStyle(),
244610 name: "t,c{100,0}trim",
245611 total: 100,
246612 current: 0,
248614 want: "[--------------------------------------------------------------------------------------------------]",
249615 },
250616 {
617 style: BarStyle(),
251618 name: "t,c{100,1}",
252619 total: 100,
253620 current: 1,
254621 want: " [>-----------------------------------------------------------------------------------------------] ",
255622 },
256623 {
624 style: BarStyle(),
257625 name: "t,c{100,1}trim",
258626 total: 100,
259627 current: 1,
261629 want: "[>-------------------------------------------------------------------------------------------------]",
262630 },
263631 {
632 style: BarStyle(),
264633 name: "t,c{100,99}",
265634 total: 100,
266635 current: 99,
267636 want: " [==============================================================================================>-] ",
268637 },
269638 {
639 style: BarStyle(),
270640 name: "t,c{100,99}trim",
271641 total: 100,
272642 current: 99,
274644 want: "[================================================================================================>-]",
275645 },
276646 {
647 style: BarStyle(),
277648 name: "t,c{100,100}",
278649 total: 100,
279650 current: 100,
280651 want: " [================================================================================================] ",
281652 },
282653 {
654 style: BarStyle(),
283655 name: "t,c{100,100}trim",
284656 total: 100,
285657 current: 100,
287659 want: "[==================================================================================================]",
288660 },
289661 {
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(),
290688 name: "t,c,r{100,100,100}trim",
291689 total: 100,
292690 current: 100,
295693 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
296694 },
297695 {
298 name: "t,c{100,33}",
299 total: 100,
300 current: 33,
301 want: " [===============================>----------------------------------------------------------------] ",
302 },
303 {
304 name: "t,c{100,33}trim",
305 total: 100,
306 current: 33,
307 trim: true,
308 want: "[===============================>------------------------------------------------------------------]",
309 },
310 {
311 style: BarStyle().Tip("<").Reverse(),
312 name: "t,c{100,33}trim,rev",
313 total: 100,
314 current: 33,
315 trim: true,
316 want: "[------------------------------------------------------------------<===============================]",
317 },
318 {
319 name: "t,c,r{100,33,33}",
320 total: 100,
321 current: 33,
322 refill: 33,
323 want: " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ",
324 },
325 {
326 name: "t,c,r{100,33,33}trim",
327 total: 100,
328 current: 33,
329 refill: 33,
330 trim: true,
331 want: "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]",
332 },
333 {
334 style: BarStyle().Tip("<").Reverse(),
335 name: "t,c,r{100,33,33}trim,rev",
336 total: 100,
337 current: 33,
338 refill: 33,
339 trim: true,
340 want: "[------------------------------------------------------------------<+++++++++++++++++++++++++++++++]",
341 },
342 {
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(),
343731 name: "t,c,r{100,40,33}",
344732 total: 100,
345733 current: 40,
347735 want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ",
348736 },
349737 {
738 style: BarStyle(),
350739 name: "t,c,r{100,40,33}trim",
351740 total: 100,
352741 current: 40,
370759 refill: 33,
371760 trim: true,
372761 want: "[-----------------------------------------------------------<======++++++++++++++++++++++++++++++++]",
373 },
374 },
375 197: {
376 {
377 name: "t,c,r{97486999,2805950,2805483}trim",
378 total: 97486999,
379 current: 2805950,
380 refill: 2805483,
381 barWidth: 60,
382 trim: true,
383 want: "[+>--------------------------------------------------------]",
384762 },
385763 },
386764 }
388766 var tmpBuf bytes.Buffer
389767 for tw, cases := range testSuite {
390768 for _, tc := range cases {
391 if tc.style == nil {
392 tc.style = BarStyle()
393 }
394 s := newTestState(NewBarFiller(tc.style))
769 s := newTestState(tc.style.Build())
395770 s.reqWidth = tc.barWidth
396771 s.total = tc.total
397772 s.current = tc.current
398773 s.trimSpace = tc.trim
399774 s.refill = tc.refill
775 s.completed = tc.total > 0 && tc.current >= tc.total
400776 tmpBuf.Reset()
401 tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
777 r, err := s.draw(newStatistics(tw, s))
778 if err != nil {
779 t.FailNow()
780 }
781 _, err = tmpBuf.ReadFrom(r)
782 if err != nil {
783 t.FailNow()
784 }
785 by := tmpBuf.Bytes()
786
787 got := string(by[:len(by)-1])
788 if !utf8.ValidString(got) {
789 t.Fail()
790 }
791 if got != tc.want {
792 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))
793 }
794 }
795 }
796 }
797
798 func TestDrawTipOnComplete(t *testing.T) {
799 // key is termWidth
800 testSuite := map[int][]struct {
801 style BarStyleComposer
802 name string
803 total int64
804 current int64
805 refill int64
806 barWidth int
807 trim bool
808 want string
809 }{
810 3: {
811 {
812 style: BarStyle().TipOnComplete(),
813 name: "t,c{60,60}",
814 total: 60,
815 current: 60,
816 want: " ",
817 },
818 {
819 style: BarStyle().TipOnComplete(),
820 name: "t,c{60,60}trim",
821 total: 60,
822 current: 60,
823 trim: true,
824 want: "[>]",
825 },
826 },
827 4: {
828 {
829 style: BarStyle().TipOnComplete(),
830 name: "t,c{60,59}",
831 total: 60,
832 current: 59,
833 want: " [] ",
834 },
835 {
836 style: BarStyle().TipOnComplete(),
837 name: "t,c{60,59}trim",
838 total: 60,
839 current: 59,
840 trim: true,
841 want: "[=>]",
842 },
843 {
844 style: BarStyle().TipOnComplete(),
845 name: "t,c{60,60}",
846 total: 60,
847 current: 60,
848 want: " [] ",
849 },
850 {
851 style: BarStyle().TipOnComplete(),
852 name: "t,c{60,60}trim",
853 total: 60,
854 current: 60,
855 trim: true,
856 want: "[=>]",
857 },
858 },
859 5: {
860 {
861 style: BarStyle().TipOnComplete(),
862 name: "t,c{60,59}",
863 total: 60,
864 current: 59,
865 want: " [>] ",
866 },
867 {
868 style: BarStyle().TipOnComplete(),
869 name: "t,c{60,59}trim",
870 total: 60,
871 current: 59,
872 trim: true,
873 want: "[==>]",
874 },
875 {
876 style: BarStyle().TipOnComplete(),
877 name: "t,c{60,60}",
878 total: 60,
879 current: 60,
880 want: " [>] ",
881 },
882 {
883 style: BarStyle().TipOnComplete(),
884 name: "t,c{60,60}trim",
885 total: 60,
886 current: 60,
887 trim: true,
888 want: "[==>]",
889 },
890 },
891 6: {
892 {
893 style: BarStyle().TipOnComplete(),
894 name: "t,c{60,59}",
895 total: 60,
896 current: 59,
897 want: " [=>] ",
898 },
899 {
900 style: BarStyle().TipOnComplete(),
901 name: "t,c{60,59}trim",
902 total: 60,
903 current: 59,
904 trim: true,
905 want: "[===>]",
906 },
907 {
908 style: BarStyle().TipOnComplete(),
909 name: "t,c{60,60}",
910 total: 60,
911 current: 60,
912 want: " [=>] ",
913 },
914 {
915 style: BarStyle().TipOnComplete(),
916 name: "t,c{60,60}trim",
917 total: 60,
918 current: 60,
919 trim: true,
920 want: "[===>]",
921 },
922 },
923 7: {
924 {
925 style: BarStyle().TipOnComplete(),
926 name: "t,c{60,59}",
927 total: 60,
928 current: 59,
929 want: " [==>] ",
930 },
931 {
932 style: BarStyle().TipOnComplete(),
933 name: "t,c{60,59}trim",
934 total: 60,
935 current: 59,
936 trim: true,
937 want: "[====>]",
938 },
939 {
940 style: BarStyle().TipOnComplete(),
941 name: "t,c{60,60}",
942 total: 60,
943 current: 60,
944 want: " [==>] ",
945 },
946 {
947 style: BarStyle().TipOnComplete(),
948 name: "t,c{60,60}trim",
949 total: 60,
950 current: 60,
951 trim: true,
952 want: "[====>]",
953 },
954 },
955 8: {
956 {
957 style: BarStyle().TipOnComplete(),
958 name: "t,c{60,59}",
959 total: 60,
960 current: 59,
961 want: " [===>] ",
962 },
963 {
964 style: BarStyle().TipOnComplete(),
965 name: "t,c{60,59}trim",
966 total: 60,
967 current: 59,
968 trim: true,
969 want: "[=====>]",
970 },
971 {
972 style: BarStyle().TipOnComplete(),
973 name: "t,c{60,60}",
974 total: 60,
975 current: 60,
976 want: " [===>] ",
977 },
978 {
979 style: BarStyle().TipOnComplete(),
980 name: "t,c{60,60}trim",
981 total: 60,
982 current: 60,
983 trim: true,
984 want: "[=====>]",
985 },
986 },
987 80: {
988 {
989 style: BarStyle().TipOnComplete(),
990 name: "t,c{60,59}",
991 total: 60,
992 current: 59,
993 want: " [==========================================================================>-] ",
994 },
995 {
996 style: BarStyle().TipOnComplete(),
997 name: "t,c{60,59}trim",
998 total: 60,
999 current: 59,
1000 trim: true,
1001 want: "[============================================================================>-]",
1002 },
1003 {
1004 style: BarStyle().TipOnComplete(),
1005 name: "t,c,bw{60,59,60}",
1006 total: 60,
1007 current: 59,
1008 barWidth: 60,
1009 want: " [========================================================>-] ",
1010 },
1011 {
1012 style: BarStyle().TipOnComplete(),
1013 name: "t,c,bw{60,59,60}trim",
1014 total: 60,
1015 current: 59,
1016 barWidth: 60,
1017 trim: true,
1018 want: "[========================================================>-]",
1019 },
1020 {
1021 style: BarStyle().TipOnComplete(),
1022 name: "t,c{60,60}",
1023 total: 60,
1024 current: 60,
1025 want: " [===========================================================================>] ",
1026 },
1027 {
1028 style: BarStyle().TipOnComplete(),
1029 name: "t,c{60,60}trim",
1030 total: 60,
1031 current: 60,
1032 trim: true,
1033 want: "[=============================================================================>]",
1034 },
1035 {
1036 style: BarStyle().TipOnComplete(),
1037 name: "t,c,bw{60,60,60}",
1038 total: 60,
1039 current: 60,
1040 barWidth: 60,
1041 want: " [=========================================================>] ",
1042 },
1043 {
1044 style: BarStyle().TipOnComplete(),
1045 name: "t,c,bw{60,60,60}trim",
1046 total: 60,
1047 current: 60,
1048 barWidth: 60,
1049 trim: true,
1050 want: "[=========================================================>]",
1051 },
1052 },
1053 99: {
1054 {
1055 style: BarStyle().TipOnComplete(),
1056 name: "t,c{100,99}",
1057 total: 100,
1058 current: 99,
1059 want: " [=============================================================================================>-] ",
1060 },
1061 {
1062 style: BarStyle().TipOnComplete(),
1063 name: "t,c{100,99}trim",
1064 total: 100,
1065 current: 99,
1066 trim: true,
1067 want: "[===============================================================================================>-]",
1068 },
1069 {
1070 style: BarStyle().TipOnComplete(),
1071 name: "t,c{100,100}",
1072 total: 100,
1073 current: 100,
1074 want: " [==============================================================================================>] ",
1075 },
1076 {
1077 style: BarStyle().TipOnComplete(),
1078 name: "t,c{100,100}trim",
1079 total: 100,
1080 current: 100,
1081 trim: true,
1082 want: "[================================================================================================>]",
1083 },
1084 {
1085 style: BarStyle().TipOnComplete(),
1086 name: "t,c,r{100,100,99}",
1087 total: 100,
1088 current: 100,
1089 refill: 99,
1090 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1091 },
1092 {
1093 style: BarStyle().TipOnComplete(),
1094 name: "t,c,r{100,100,99}trim",
1095 total: 100,
1096 current: 100,
1097 refill: 99,
1098 trim: true,
1099 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1100 },
1101 {
1102 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1103 name: `t,c,r{100,100,99}.Tip("<").TipOnComplete().Reverse()`,
1104 total: 100,
1105 current: 100,
1106 refill: 99,
1107 want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
1108 },
1109 {
1110 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1111 name: `t,c,r{100,100,99}.Tip("<").TipOnComplete().Reverse()trim`,
1112 total: 100,
1113 current: 100,
1114 refill: 99,
1115 trim: true,
1116 want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
1117 },
1118 {
1119 style: BarStyle().TipOnComplete(),
1120 name: "t,c,r{100,100,100}",
1121 total: 100,
1122 current: 100,
1123 refill: 100,
1124 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1125 },
1126 {
1127 style: BarStyle().TipOnComplete(),
1128 name: "t,c,r{100,100,100}trim",
1129 total: 100,
1130 current: 100,
1131 refill: 100,
1132 trim: true,
1133 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1134 },
1135 {
1136 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1137 name: `t,c,r{100,100,100}.Tip("<").TipOnComplete().Reverse()`,
1138 total: 100,
1139 current: 100,
1140 refill: 100,
1141 want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
1142 },
1143 {
1144 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1145 name: `t,c,r{100,100,100}.Tip("<").TipOnComplete().Reverse()trim`,
1146 total: 100,
1147 current: 100,
1148 refill: 100,
1149 trim: true,
1150 want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
1151 },
1152 },
1153 100: {
1154 {
1155 style: BarStyle().TipOnComplete(),
1156 name: "t,c{100,99}",
1157 total: 100,
1158 current: 99,
1159 want: " [==============================================================================================>-] ",
1160 },
1161 {
1162 style: BarStyle().TipOnComplete(),
1163 name: "t,c{100,99}trim",
1164 total: 100,
1165 current: 99,
1166 trim: true,
1167 want: "[================================================================================================>-]",
1168 },
1169 {
1170 style: BarStyle().TipOnComplete(),
1171 name: "t,c{100,100}",
1172 total: 100,
1173 current: 100,
1174 want: " [===============================================================================================>] ",
1175 },
1176 {
1177 style: BarStyle().TipOnComplete(),
1178 name: "t,c{100,100}trim",
1179 total: 100,
1180 current: 100,
1181 trim: true,
1182 want: "[=================================================================================================>]",
1183 },
1184 {
1185 style: BarStyle().TipOnComplete(),
1186 name: "t,c,r{100,100,99}",
1187 total: 100,
1188 current: 100,
1189 refill: 99,
1190 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1191 },
1192 {
1193 style: BarStyle().TipOnComplete(),
1194 name: "t,c,r{100,100,99}trim",
1195 total: 100,
1196 current: 100,
1197 refill: 99,
1198 trim: true,
1199 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1200 },
1201 {
1202 style: BarStyle().TipOnComplete(),
1203 name: "t,c,r{100,100,100}",
1204 total: 100,
1205 current: 100,
1206 refill: 100,
1207 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1208 },
1209 {
1210 style: BarStyle().TipOnComplete(),
1211 name: "t,c,r{100,100,100}trim",
1212 total: 100,
1213 current: 100,
1214 refill: 100,
1215 trim: true,
1216 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1217 },
1218 },
1219 }
1220
1221 var tmpBuf bytes.Buffer
1222 for tw, cases := range testSuite {
1223 for _, tc := range cases {
1224 s := newTestState(tc.style.Build())
1225 s.reqWidth = tc.barWidth
1226 s.total = tc.total
1227 s.current = tc.current
1228 s.trimSpace = tc.trim
1229 s.refill = tc.refill
1230 s.completed = tc.total > 0 && tc.current >= tc.total
1231 tmpBuf.Reset()
1232 r, err := s.draw(newStatistics(tw, s))
1233 if err != nil {
1234 t.FailNow()
1235 }
1236 _, err = tmpBuf.ReadFrom(r)
1237 if err != nil {
1238 t.FailNow()
1239 }
1240 by := tmpBuf.Bytes()
1241
1242 got := string(by[:len(by)-1])
1243 if !utf8.ValidString(got) {
1244 t.Fail()
1245 }
1246 if got != tc.want {
1247 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))
1248 }
1249 }
1250 }
1251 }
1252
1253 func TestDrawDoubleWidth(t *testing.T) {
1254 // key is termWidth
1255 testSuite := map[int][]struct {
1256 style BarStyleComposer
1257 name string
1258 total int64
1259 current int64
1260 refill int64
1261 barWidth int
1262 trim bool
1263 want string
1264 }{
1265 99: {
1266 {
1267 style: BarStyle().Lbound("の").Rbound("の"),
1268 name: `t,c{100,1}.Lbound("の").Rbound("の")`,
1269 total: 100,
1270 current: 1,
1271 want: " の>--------------------------------------------------------------------------------------------の ",
1272 },
1273 {
1274 style: BarStyle().Lbound("の").Rbound("の"),
1275 name: `t,c{100,1}.Lbound("の").Rbound("の")`,
1276 total: 100,
1277 current: 2,
1278 want: " の=>-------------------------------------------------------------------------------------------の ",
1279 },
1280 {
1281 style: BarStyle().Tip("だ"),
1282 name: `t,c{100,1}Tip("だ")`,
1283 total: 100,
1284 current: 1,
1285 want: " [だ---------------------------------------------------------------------------------------------] ",
1286 },
1287 {
1288 style: BarStyle().Tip("だ"),
1289 name: `t,c{100,2}Tip("だ")`,
1290 total: 100,
1291 current: 2,
1292 want: " [だ---------------------------------------------------------------------------------------------] ",
1293 },
1294 {
1295 style: BarStyle().Tip("だ"),
1296 name: `t,c{100,3}Tip("だ")`,
1297 total: 100,
1298 current: 3,
1299 want: " [=だ--------------------------------------------------------------------------------------------] ",
1300 },
1301 {
1302 style: BarStyle().Tip("だ"),
1303 name: `t,c{100,99}Tip("だ")`,
1304 total: 100,
1305 current: 99,
1306 want: " [============================================================================================だ-] ",
1307 },
1308 {
1309 style: BarStyle().Tip("だ"),
1310 name: `t,c{100,100}Tip("だ")`,
1311 total: 100,
1312 current: 100,
1313 want: " [===============================================================================================] ",
1314 },
1315 {
1316 style: BarStyle().Tip("だ").TipOnComplete(),
1317 name: `t,c{100,100}.Tip("だ").TipOnComplete()`,
1318 total: 100,
1319 current: 100,
1320 want: " [=============================================================================================だ] ",
1321 },
1322 {
1323 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1324 name: `t,c{100,1}Filler("の").Tip("だ").Padding("つ")`,
1325 total: 100,
1326 current: 1,
1327 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
1328 },
1329 {
1330 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1331 name: `t,c{100,2}Filler("の").Tip("だ").Padding("つ")`,
1332 total: 100,
1333 current: 2,
1334 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
1335 },
1336 {
1337 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1338 name: `t,c{100,99}Filler("の").Tip("だ").Padding("つ")`,
1339 total: 100,
1340 current: 99,
1341 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1342 },
1343 {
1344 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1345 name: `t,c{100,100}.Filler("の").Tip("だ").Padding("つ")`,
1346 total: 100,
1347 current: 100,
1348 want: " [ののののののののののののののののののののののののののののののののののののののののののののののの…] ",
1349 },
1350 {
1351 style: BarStyle().Filler("の").Tip("だ").Padding("つ").Reverse(),
1352 name: `t,c{100,100}Filler("の").Tip("だ").Padding("つ").Reverse()`,
1353 total: 100,
1354 current: 100,
1355 want: " […ののののののののののののののののののののののののののののののののののののののののののののののの] ",
1356 },
1357 {
1358 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ"),
1359 name: `t,c{100,99}Filler("の").Tip("だ").TipOnComplete().Padding("つ")`,
1360 total: 100,
1361 current: 99,
1362 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1363 },
1364 {
1365 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ"),
1366 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete().Padding("つ")`,
1367 total: 100,
1368 current: 100,
1369 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1370 },
1371 {
1372 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ").Reverse(),
1373 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete().Padding("つ").Reverse()`,
1374 total: 100,
1375 current: 100,
1376 want: " […だのののののののののののののののののののののののののののののののののののののののののののののの] ",
1377 },
1378 {
1379 style: BarStyle().Refiller("の"),
1380 name: `t,c,r{100,100,99}Refiller("の")`,
1381 total: 100,
1382 current: 100,
1383 refill: 99,
1384 want: " [ののののののののののののののののののののののののののののののののののののののののののののののの=] ",
1385 },
1386 {
1387 style: BarStyle().Refiller("の"),
1388 name: `t,c,r{100,100,99}Refiller("の")trim`,
1389 total: 100,
1390 current: 100,
1391 refill: 99,
1392 trim: true,
1393 want: "[のののののののののののののののののののののののののののののののののののののののののののののののの=]",
1394 },
1395 },
1396 }
1397
1398 var tmpBuf bytes.Buffer
1399 for tw, cases := range testSuite {
1400 for _, tc := range cases {
1401 s := newTestState(tc.style.Build())
1402 s.reqWidth = tc.barWidth
1403 s.total = tc.total
1404 s.current = tc.current
1405 s.trimSpace = tc.trim
1406 s.refill = tc.refill
1407 s.completed = tc.total > 0 && tc.current >= tc.total
1408 tmpBuf.Reset()
1409 r, err := s.draw(newStatistics(tw, s))
1410 if err != nil {
1411 t.FailNow()
1412 }
1413 _, err = tmpBuf.ReadFrom(r)
1414 if err != nil {
1415 t.FailNow()
1416 }
4021417 by := tmpBuf.Bytes()
4031418
4041419 got := string(by[:len(by)-1])
4131428 }
4141429
4151430 func newTestState(filler BarFiller) *bState {
416 s := &bState{
1431 bs := &bState{
4171432 filler: filler,
418 bufP: new(bytes.Buffer),
419 bufB: new(bytes.Buffer),
420 bufA: new(bytes.Buffer),
4211433 }
422 return s
1434 for i := 0; i < len(bs.buffers); i++ {
1435 bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
1436 }
1437 return bs
4231438 }
22 import (
33 crand "crypto/rand"
44 "io"
5 "io/ioutil"
65 "math/rand"
76 "time"
87
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1110 )
1211
1312 func Example() {
1615
1716 total := 100
1817 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(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
18 // create a single bar, which will inherit container's width
19 bar := p.New(int64(total),
20 // BarFillerBuilder with custom style
21 mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"),
2322 mpb.PrependDecorators(
2423 // display our name with one space on the right
2524 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
2625 // replace ETA decorator with "done" message, OnComplete event
2726 decor.OnComplete(
28 // ETA decorator with ewma age of 60, and width reservation of 4
29 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WC{W: 4}), "done",
27 decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "done",
3028 ),
3129 ),
3230 mpb.AppendDecorators(decor.Percentage()),
3432 // simulating some work
3533 max := 100 * time.Millisecond
3634 for i := 0; i < total; i++ {
37 // start variable is solely for EWMA calculation
38 // EWMA's unit of measure is an iteration's duration
39 start := time.Now()
4035 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
4136 bar.Increment()
42 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
43 bar.DecoratorEwmaUpdate(time.Since(start))
4437 }
4538 // wait for our bar to complete and flush
4639 p.Wait()
7770 defer proxyReader.Close()
7871
7972 // and copy from reader, ignoring errors
80 io.Copy(ioutil.Discard, proxyReader)
73 _, _ = io.Copy(io.Discard, proxyReader)
8174
8275 p.Wait()
8376 }
11
22 // make syncWidth func public in test
33 var SyncWidth = syncWidth
4 var MaxWidthDistributor = &maxWidthDistributor
4
5 type PriorityQueue = priorityQueue
0 module github.com/vbauerster/mpb/v7
0 module github.com/vbauerster/mpb/v8
11
22 require (
33 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.13
6 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
5 github.com/mattn/go-runewidth v0.0.15
6 golang.org/x/sys v0.11.0
77 )
88
9 go 1.14
9 require github.com/rivo/uniseg v0.4.4 // indirect
10
11 go 1.17
11 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.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
5 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
6 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
4 github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
5 github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
76 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
8 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
9 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
7 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
8 github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
9 golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
10 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
0 package mpb
1
2 import "container/heap"
3
4 type heapManager chan heapRequest
5
6 type heapCmd int
7
8 const (
9 h_sync heapCmd = iota
10 h_push
11 h_iter
12 h_drain
13 h_fix
14 h_state
15 h_end
16 )
17
18 type heapRequest struct {
19 cmd heapCmd
20 data interface{}
21 }
22
23 type iterData struct {
24 iter chan<- *Bar
25 drop <-chan struct{}
26 }
27
28 type pushData struct {
29 bar *Bar
30 sync bool
31 }
32
33 type fixData struct {
34 bar *Bar
35 priority int
36 lazy bool
37 }
38
39 func (m heapManager) run() {
40 var bHeap priorityQueue
41 var pMatrix, aMatrix map[int][]chan int
42
43 var l int
44 var sync bool
45
46 for req := range m {
47 switch req.cmd {
48 case h_push:
49 data := req.data.(pushData)
50 heap.Push(&bHeap, data.bar)
51 if !sync {
52 sync = data.sync
53 }
54 case h_sync:
55 if sync || l != bHeap.Len() {
56 pMatrix = make(map[int][]chan int)
57 aMatrix = make(map[int][]chan int)
58 for _, b := range bHeap {
59 table := b.wSyncTable()
60 for i, ch := range table[0] {
61 pMatrix[i] = append(pMatrix[i], ch)
62 }
63 for i, ch := range table[1] {
64 aMatrix[i] = append(aMatrix[i], ch)
65 }
66 }
67 sync = false
68 l = bHeap.Len()
69 }
70 drop := req.data.(<-chan struct{})
71 syncWidth(pMatrix, drop)
72 syncWidth(aMatrix, drop)
73 case h_iter:
74 data := req.data.(iterData)
75 drop_iter:
76 for _, b := range bHeap {
77 select {
78 case data.iter <- b:
79 case <-data.drop:
80 break drop_iter
81 }
82 }
83 close(data.iter)
84 case h_drain:
85 data := req.data.(iterData)
86 drop_drain:
87 for bHeap.Len() != 0 {
88 select {
89 case data.iter <- heap.Pop(&bHeap).(*Bar):
90 case <-data.drop:
91 break drop_drain
92 }
93 }
94 close(data.iter)
95 case h_fix:
96 data := req.data.(fixData)
97 if data.bar.index < 0 {
98 break
99 }
100 data.bar.priority = data.priority
101 if !data.lazy {
102 heap.Fix(&bHeap, data.bar.index)
103 }
104 case h_state:
105 ch := req.data.(chan<- bool)
106 ch <- sync || l != bHeap.Len()
107 case h_end:
108 ch := req.data.(chan<- interface{})
109 if ch != nil {
110 go func() {
111 ch <- []*Bar(bHeap)
112 }()
113 }
114 close(m)
115 }
116 }
117 }
118
119 func (m heapManager) sync(drop <-chan struct{}) {
120 m <- heapRequest{cmd: h_sync, data: drop}
121 }
122
123 func (m heapManager) push(b *Bar, sync bool) {
124 data := pushData{b, sync}
125 m <- heapRequest{cmd: h_push, data: data}
126 }
127
128 func (m heapManager) iter(iter chan<- *Bar, drop <-chan struct{}) {
129 data := iterData{iter, drop}
130 m <- heapRequest{cmd: h_iter, data: data}
131 }
132
133 func (m heapManager) drain(iter chan<- *Bar, drop <-chan struct{}) {
134 data := iterData{iter, drop}
135 m <- heapRequest{cmd: h_drain, data: data}
136 }
137
138 func (m heapManager) fix(b *Bar, priority int, lazy bool) {
139 data := fixData{b, priority, lazy}
140 m <- heapRequest{cmd: h_fix, data: data}
141 }
142
143 func (m heapManager) state(ch chan<- bool) {
144 m <- heapRequest{cmd: h_state, data: ch}
145 }
146
147 func (m heapManager) end(ch chan<- interface{}) {
148 m <- heapRequest{cmd: h_end, data: ch}
149 }
150
151 func syncWidth(matrix map[int][]chan int, drop <-chan struct{}) {
152 for _, column := range matrix {
153 go maxWidthDistributor(column, drop)
154 }
155 }
156
157 func maxWidthDistributor(column []chan int, drop <-chan struct{}) {
158 var maxWidth int
159 for _, ch := range column {
160 select {
161 case w := <-ch:
162 if w > maxWidth {
163 maxWidth = w
164 }
165 case <-drop:
166 return
167 }
168 }
169 for _, ch := range column {
170 ch <- maxWidth
171 }
172 }
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 }
00 package mpb
11
2 // A priorityQueue implements heap.Interface
2 import "container/heap"
3
4 var _ heap.Interface = (*priorityQueue)(nil)
5
36 type priorityQueue []*Bar
47
58 func (pq priorityQueue) Len() int { return len(pq) }
69
710 func (pq priorityQueue) Less(i, j int) bool {
8 return pq[i].priority < pq[j].priority
11 // greater priority pops first
12 return pq[i].priority > pq[j].priority
913 }
1014
1115 func (pq priorityQueue) Swap(i, j int) {
1519 }
1620
1721 func (pq *priorityQueue) Push(x interface{}) {
18 s := *pq
22 n := len(*pq)
1923 bar := x.(*Bar)
20 bar.index = len(s)
21 s = append(s, bar)
22 *pq = s
24 bar.index = n
25 *pq = append(*pq, bar)
2326 }
2427
2528 func (pq *priorityQueue) Pop() interface{} {
26 s := *pq
27 *pq = s[0 : len(s)-1]
28 bar := s[len(s)-1]
29 old := *pq
30 n := len(old)
31 bar := old[n-1]
32 old[n-1] = nil // avoid memory leak
2933 bar.index = -1 // for safety
34 *pq = old[:n-1]
3035 return bar
3136 }
11
22 import (
33 "bytes"
4 "container/heap"
54 "context"
65 "fmt"
76 "io"
8 "io/ioutil"
9 "log"
107 "math"
118 "os"
129 "sync"
1310 "time"
1411
15 "github.com/vbauerster/mpb/v7/cwriter"
16 "github.com/vbauerster/mpb/v7/decor"
12 "github.com/vbauerster/mpb/v8/cwriter"
13 "github.com/vbauerster/mpb/v8/decor"
1714 )
1815
1916 const (
20 // default RefreshRate
21 prr = 120 * time.Millisecond
17 defaultRefreshRate = 150 * time.Millisecond
2218 )
2319
24 // Progress represents a container that renders one or more progress
25 // bars.
20 // DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
21 var DoneError = fmt.Errorf("%T instance can't be reused after it's done", (*Progress)(nil))
22
23 // Progress represents a container that renders one or more progress bars.
2624 type Progress struct {
25 uwg *sync.WaitGroup
26 pwg, bwg sync.WaitGroup
27 operateState chan func(*pState)
28 interceptIO chan func(io.Writer)
29 done <-chan struct{}
30 cancel func()
31 }
32
33 // pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
34 type pState struct {
2735 ctx context.Context
28 uwg *sync.WaitGroup
29 cwg *sync.WaitGroup
30 bwg *sync.WaitGroup
31 operateState chan func(*pState)
32 done chan struct{}
33 refreshCh chan time.Time
34 once sync.Once
35 dlogger *log.Logger
36 }
37
38 // pState holds bars in its priorityQueue. It gets passed to
39 // *Progress.serve(...) monitor goroutine.
40 type pState struct {
41 bHeap priorityQueue
42 heapUpdated bool
43 pMatrix map[int][]chan int
44 aMatrix map[int][]chan int
45 barShutdownQueue []*Bar
36 hm heapManager
37 dropS, dropD chan struct{}
38 renderReq chan time.Time
39 idCount int
40 popPriority int
4641
4742 // following are provided/overrided by user
48 idCount int
43 refreshRate time.Duration
4944 reqWidth int
5045 popCompleted bool
51 outputDiscarded bool
52 rr time.Duration
53 uwg *sync.WaitGroup
54 externalRefresh <-chan interface{}
55 renderDelay <-chan struct{}
56 shutdownNotifier chan struct{}
57 parkedBars map[*Bar]*Bar
46 autoRefresh bool
47 delayRC <-chan struct{}
48 manualRC <-chan interface{}
49 shutdownNotifier chan<- interface{}
50 queueBars map[*Bar]*Bar
5851 output io.Writer
5952 debugOut io.Writer
53 uwg *sync.WaitGroup
6054 }
6155
6256 // New creates new Progress container instance. It's not possible to
63 // reuse instance after *Progress.Wait() method has been called.
57 // reuse instance after (*Progress).Wait method has been called.
6458 func New(options ...ContainerOption) *Progress {
6559 return NewWithContext(context.Background(), options...)
6660 }
6761
6862 // NewWithContext creates new Progress container instance with provided
69 // context. It's not possible to reuse instance after *Progress.Wait()
63 // context. It's not possible to reuse instance after (*Progress).Wait
7064 // method has been called.
7165 func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
66 if ctx == nil {
67 ctx = context.Background()
68 }
69 ctx, cancel := context.WithCancel(ctx)
7270 s := &pState{
73 bHeap: priorityQueue{},
74 rr: prr,
75 parkedBars: make(map[*Bar]*Bar),
76 output: os.Stdout,
77 debugOut: ioutil.Discard,
71 ctx: ctx,
72 hm: make(heapManager),
73 dropS: make(chan struct{}),
74 dropD: make(chan struct{}),
75 renderReq: make(chan time.Time),
76 refreshRate: defaultRefreshRate,
77 popPriority: math.MinInt32,
78 queueBars: make(map[*Bar]*Bar),
79 output: os.Stdout,
80 debugOut: io.Discard,
7881 }
7982
8083 for _, opt := range options {
8487 }
8588
8689 p := &Progress{
87 ctx: ctx,
8890 uwg: s.uwg,
89 cwg: new(sync.WaitGroup),
90 bwg: new(sync.WaitGroup),
9191 operateState: make(chan func(*pState)),
92 done: make(chan struct{}),
93 dlogger: log.New(s.debugOut, "[mpb] ", log.Lshortfile),
94 }
95
96 p.cwg.Add(1)
97 go p.serve(s, cwriter.New(s.output))
92 interceptIO: make(chan func(io.Writer)),
93 cancel: cancel,
94 }
95
96 cw := cwriter.New(s.output)
97 if s.manualRC != nil {
98 done := make(chan struct{})
99 p.done = done
100 s.autoRefresh = false
101 go s.manualRefreshListener(done)
102 } else if cw.IsTerminal() || s.autoRefresh {
103 done := make(chan struct{})
104 p.done = done
105 s.autoRefresh = true
106 go s.autoRefreshListener(done)
107 } else {
108 p.done = ctx.Done()
109 s.autoRefresh = false
110 }
111
112 p.pwg.Add(1)
113 go p.serve(s, cw)
114 go s.hm.run()
98115 return p
99116 }
100117
101 // AddBar creates a bar with default bar filler. Different filler can
102 // be chosen and applied via `*Progress.Add(...) *Bar` method.
118 // AddBar creates a bar with default bar filler.
103119 func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
104 return p.Add(total, NewBarFiller(BarStyle()), options...)
105 }
106
107 // AddSpinner creates a bar with default spinner filler. Different
108 // filler can be chosen and applied via `*Progress.Add(...) *Bar`
109 // method.
120 return p.New(total, BarStyle(), options...)
121 }
122
123 // AddSpinner creates a bar with default spinner filler.
110124 func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
111 return p.Add(total, NewBarFiller(SpinnerStyle()), options...)
112 }
113
114 // Add creates a bar which renders itself by provided filler.
115 // If `total <= 0` trigger complete event is disabled until reset with *bar.SetTotal(int64, bool).
116 // Panics if *Progress instance is done, i.e. called after *Progress.Wait().
117 func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
125 return p.New(total, SpinnerStyle(), options...)
126 }
127
128 // New creates a bar by calling `Build` method on provided `BarFillerBuilder`.
129 func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
130 return p.MustAdd(total, builder.Build(), options...)
131 }
132
133 // MustAdd creates a bar which renders itself by provided BarFiller.
134 // If `total <= 0` triggering complete event by increment methods is
135 // disabled. Panics if *Progress instance is done, i.e. called after
136 // (*Progress).Wait().
137 func (p *Progress) MustAdd(total int64, filler BarFiller, options ...BarOption) *Bar {
138 bar, err := p.Add(total, filler, options...)
139 if err != nil {
140 panic(err)
141 }
142 return bar
143 }
144
145 // Add creates a bar which renders itself by provided BarFiller.
146 // If `total <= 0` triggering complete event by increment methods
147 // is disabled. If *Progress instance is done, i.e. called after
148 // (*Progress).Wait(), return error == DoneError.
149 func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) (*Bar, error) {
118150 if filler == nil {
119 filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
120 }
121 p.bwg.Add(1)
122 result := make(chan *Bar)
151 filler = NopStyle().Build()
152 }
153 type result struct {
154 bar *Bar
155 bs *bState
156 }
157 ch := make(chan result)
123158 select {
124159 case p.operateState <- func(ps *pState) {
125160 bs := ps.makeBarState(total, filler, options...)
126 bar := newBar(p, bs)
127 if bs.runningBar != nil {
128 bs.runningBar.noPop = true
129 ps.parkedBars[bs.runningBar] = bar
161 bar := newBar(ps.ctx, p, bs)
162 if bs.waitBar != nil {
163 ps.queueBars[bs.waitBar] = bar
130164 } else {
131 heap.Push(&ps.bHeap, bar)
132 ps.heapUpdated = true
165 ps.hm.push(bar, true)
133166 }
134167 ps.idCount++
135 result <- bar
168 ch <- result{bar, bs}
136169 }:
137 bar := <-result
138 bar.subscribeDecorators()
139 return bar
170 res := <-ch
171 bar, bs := res.bar, res.bs
172 bar.TraverseDecorators(func(d decor.Decorator) {
173 if d, ok := d.(decor.AverageDecorator); ok {
174 bs.averageDecorators = append(bs.averageDecorators, d)
175 }
176 if d, ok := d.(decor.EwmaDecorator); ok {
177 bs.ewmaDecorators = append(bs.ewmaDecorators, d)
178 }
179 if d, ok := d.(decor.ShutdownListener); ok {
180 bs.shutdownListeners = append(bs.shutdownListeners, d)
181 }
182 })
183 return bar, nil
140184 case <-p.done:
141 p.bwg.Done()
142 panic(fmt.Sprintf("%T instance can't be reused after it's done!", p))
143 }
144 }
145
146 func (p *Progress) dropBar(b *Bar) {
185 return nil, DoneError
186 }
187 }
188
189 func (p *Progress) traverseBars(cb func(b *Bar) bool) {
190 iter, drop := make(chan *Bar), make(chan struct{})
147191 select {
148 case p.operateState <- func(s *pState) {
149 if b.index < 0 {
150 return
151 }
152 heap.Remove(&s.bHeap, b.index)
153 s.heapUpdated = true
192 case p.operateState <- func(s *pState) { s.hm.iter(iter, drop) }:
193 for b := range iter {
194 if cb(b) {
195 close(drop)
196 break
197 }
198 }
199 case <-p.done:
200 }
201 }
202
203 // UpdateBarPriority either immediately or lazy.
204 // With lazy flag order is updated after the next refresh cycle.
205 // If you don't care about laziness just use *Bar.SetPriority(int).
206 func (p *Progress) UpdateBarPriority(b *Bar, priority int, lazy bool) {
207 if b == nil {
208 return
209 }
210 select {
211 case p.operateState <- func(s *pState) { s.hm.fix(b, priority, lazy) }:
212 case <-p.done:
213 }
214 }
215
216 // Write is implementation of io.Writer.
217 // Writing to `*mpb.Progress` will print lines above a running bar.
218 // Writes aren't flushed immediately, but at next refresh cycle.
219 // If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
220 // is returned.
221 func (p *Progress) Write(b []byte) (int, error) {
222 type result struct {
223 n int
224 err error
225 }
226 ch := make(chan result)
227 select {
228 case p.interceptIO <- func(w io.Writer) {
229 n, err := w.Write(b)
230 ch <- result{n, err}
154231 }:
232 res := <-ch
233 return res.n, res.err
155234 case <-p.done:
156 }
157 }
158
159 func (p *Progress) setBarPriority(b *Bar, priority int) {
160 select {
161 case p.operateState <- func(s *pState) {
162 if b.index < 0 {
163 return
164 }
165 b.priority = priority
166 heap.Fix(&s.bHeap, b.index)
167 }:
168 case <-p.done:
169 }
170 }
171
172 // UpdateBarPriority same as *Bar.SetPriority(int).
173 func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
174 p.setBarPriority(b, priority)
175 }
176
177 // BarCount returns bars count.
178 func (p *Progress) BarCount() int {
179 result := make(chan int, 1)
180 select {
181 case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
182 return <-result
183 case <-p.done:
184 return 0
185 }
186 }
187
188 // Wait waits for all bars to complete and finally shutdowns container.
189 // After this method has been called, there is no way to reuse *Progress
190 // instance.
235 return 0, DoneError
236 }
237 }
238
239 // Wait waits for all bars to complete and finally shutdowns container. After
240 // this method has been called, there is no way to reuse (*Progress) instance.
191241 func (p *Progress) Wait() {
242 // wait for user wg, if any
192243 if p.uwg != nil {
193 // wait for user wg
194244 p.uwg.Wait()
195245 }
196246
197 // wait for bars to quit, if any
198247 p.bwg.Wait()
199
200 p.once.Do(p.shutdown)
201
202 // wait for container to quit
203 p.cwg.Wait()
204 }
205
206 func (p *Progress) shutdown() {
207 close(p.done)
248 p.Shutdown()
249 }
250
251 // Shutdown cancels any running bar immediately and then shutdowns (*Progress)
252 // instance. Normally this method shouldn't be called unless you know what you
253 // are doing. Proper way to shutdown is to call (*Progress).Wait() instead.
254 func (p *Progress) Shutdown() {
255 p.cancel()
256 p.pwg.Wait()
208257 }
209258
210259 func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
211 defer p.cwg.Done()
212
213 p.refreshCh = s.newTicker(p.done)
260 defer p.pwg.Done()
261 render := func() error { return s.render(cw) }
262 var err error
214263
215264 for {
216265 select {
217266 case op := <-p.operateState:
218267 op(s)
219 case <-p.refreshCh:
220 if err := s.render(cw); err != nil {
221 p.dlogger.Println(err)
222 }
223 case <-s.shutdownNotifier:
224 if s.heapUpdated {
225 if err := s.render(cw); err != nil {
226 p.dlogger.Println(err)
268 case fn := <-p.interceptIO:
269 fn(cw)
270 case <-s.renderReq:
271 e := render()
272 if e != nil {
273 p.cancel() // cancel all bars
274 render = func() error { return nil }
275 err = e
276 }
277 case <-p.done:
278 update := make(chan bool)
279 for s.autoRefresh && err == nil {
280 s.hm.state(update)
281 if <-update {
282 err = render()
283 } else {
284 break
227285 }
228286 }
287 if err != nil {
288 _, _ = fmt.Fprintln(s.debugOut, err.Error())
289 }
290 s.hm.end(s.shutdownNotifier)
229291 return
230292 }
231293 }
232294 }
233295
234 func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
235 ch := make(chan time.Time)
236 if s.shutdownNotifier == nil {
237 s.shutdownNotifier = make(chan struct{})
238 }
239 go func() {
240 if s.renderDelay != nil {
241 <-s.renderDelay
242 }
243 var internalRefresh <-chan time.Time
244 if !s.outputDiscarded {
245 if s.externalRefresh == nil {
246 ticker := time.NewTicker(s.rr)
247 defer ticker.Stop()
248 internalRefresh = ticker.C
249 }
296 func (s pState) autoRefreshListener(done chan struct{}) {
297 if s.delayRC != nil {
298 <-s.delayRC
299 }
300 ticker := time.NewTicker(s.refreshRate)
301 defer ticker.Stop()
302 for {
303 select {
304 case t := <-ticker.C:
305 s.renderReq <- t
306 case <-s.ctx.Done():
307 close(done)
308 return
309 }
310 }
311 }
312
313 func (s pState) manualRefreshListener(done chan struct{}) {
314 for {
315 select {
316 case x := <-s.manualRC:
317 if t, ok := x.(time.Time); ok {
318 s.renderReq <- t
319 } else {
320 s.renderReq <- time.Now()
321 }
322 case <-s.ctx.Done():
323 close(done)
324 return
325 }
326 }
327 }
328
329 func (s *pState) render(cw *cwriter.Writer) (err error) {
330 s.hm.sync(s.dropS)
331 iter := make(chan *Bar)
332 go s.hm.iter(iter, s.dropS)
333
334 var width, height int
335 if cw.IsTerminal() {
336 width, height, err = cw.GetTermSize()
337 if err != nil {
338 close(s.dropS)
339 return err
340 }
341 } else {
342 if s.reqWidth > 0 {
343 width = s.reqWidth
250344 } else {
251 s.externalRefresh = nil
252 }
253 for {
254 select {
255 case t := <-internalRefresh:
256 ch <- t
257 case x := <-s.externalRefresh:
258 if t, ok := x.(time.Time); ok {
259 ch <- t
260 } else {
261 ch <- time.Now()
345 width = 100
346 }
347 height = 100
348 }
349
350 for b := range iter {
351 go b.render(width)
352 }
353
354 return s.flush(cw, height)
355 }
356
357 func (s *pState) flush(cw *cwriter.Writer, height int) error {
358 var wg sync.WaitGroup
359 defer wg.Wait() // waiting for all s.hm.push to complete
360
361 var popCount int
362 var rows []io.Reader
363
364 iter := make(chan *Bar)
365 s.hm.drain(iter, s.dropD)
366
367 for b := range iter {
368 frame := <-b.frameCh
369 if frame.err != nil {
370 close(s.dropD)
371 b.cancel()
372 return frame.err // b.frameCh is buffered it's ok to return here
373 }
374 var usedRows int
375 for i := len(frame.rows) - 1; i >= 0; i-- {
376 if row := frame.rows[i]; len(rows) < height {
377 rows = append(rows, row)
378 usedRows++
379 } else {
380 _, _ = io.Copy(io.Discard, row)
381 }
382 }
383 if frame.shutdown != 0 && !frame.done {
384 if qb, ok := s.queueBars[b]; ok {
385 b.cancel()
386 delete(s.queueBars, b)
387 qb.priority = b.priority
388 wg.Add(1)
389 go func(b *Bar) {
390 s.hm.push(b, true)
391 wg.Done()
392 }(qb)
393 continue
394 }
395 if s.popCompleted && !frame.noPop {
396 switch frame.shutdown {
397 case 1:
398 b.priority = s.popPriority
399 s.popPriority++
400 default:
401 b.cancel()
402 popCount += usedRows
403 continue
262404 }
263 case <-done:
264 close(s.shutdownNotifier)
265 return
266 }
267 }
268 }()
269 return ch
270 }
271
272 func (s *pState) render(cw *cwriter.Writer) error {
273 if s.heapUpdated {
274 s.updateSyncMatrix()
275 s.heapUpdated = false
276 }
277 syncWidth(s.pMatrix)
278 syncWidth(s.aMatrix)
279
280 tw, err := cw.GetWidth()
281 if err != nil {
282 tw = s.reqWidth
283 }
284 for i := 0; i < s.bHeap.Len(); i++ {
285 bar := s.bHeap[i]
286 go bar.render(tw)
287 }
288
289 return s.flush(cw)
290 }
291
292 func (s *pState) flush(cw *cwriter.Writer) error {
293 var lineCount int
294 bm := make(map[*Bar]struct{}, s.bHeap.Len())
295 for s.bHeap.Len() > 0 {
296 b := heap.Pop(&s.bHeap).(*Bar)
297 cw.ReadFrom(<-b.frameCh)
298 if b.toShutdown {
299 if b.recoveredPanic != nil {
300 s.barShutdownQueue = append(s.barShutdownQueue, b)
301 b.toShutdown = false
405 } else if frame.rmOnComplete {
406 b.cancel()
407 continue
302408 } else {
303 // shutdown at next flush
304 // this ensures no bar ends up with less than 100% rendered
305 defer func() {
306 s.barShutdownQueue = append(s.barShutdownQueue, b)
307 }()
308 }
309 }
310 lineCount += b.extendedLines + 1
311 bm[b] = struct{}{}
312 }
313
314 for _, b := range s.barShutdownQueue {
315 if parkedBar := s.parkedBars[b]; parkedBar != nil {
316 parkedBar.priority = b.priority
317 heap.Push(&s.bHeap, parkedBar)
318 delete(s.parkedBars, b)
319 b.toDrop = true
320 }
321 if s.popCompleted && !b.noPop {
322 lineCount -= b.extendedLines + 1
323 b.toDrop = true
324 }
325 if b.toDrop {
326 delete(bm, b)
327 s.heapUpdated = true
328 }
329 b.cancel()
330 }
331 s.barShutdownQueue = s.barShutdownQueue[0:0]
332
333 for b := range bm {
334 heap.Push(&s.bHeap, b)
335 }
336
337 return cw.Flush(lineCount)
338 }
339
340 func (s *pState) updateSyncMatrix() {
341 s.pMatrix = make(map[int][]chan int)
342 s.aMatrix = make(map[int][]chan int)
343 for i := 0; i < s.bHeap.Len(); i++ {
344 bar := s.bHeap[i]
345 table := bar.wSyncTable()
346 pRow, aRow := table[0], table[1]
347
348 for i, ch := range pRow {
349 s.pMatrix[i] = append(s.pMatrix[i], ch)
350 }
351
352 for i, ch := range aRow {
353 s.aMatrix[i] = append(s.aMatrix[i], ch)
354 }
355 }
356 }
357
358 func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
409 b.cancel()
410 }
411 }
412 wg.Add(1)
413 go func(b *Bar) {
414 s.hm.push(b, false)
415 wg.Done()
416 }(b)
417 }
418
419 for i := len(rows) - 1; i >= 0; i-- {
420 _, err := cw.ReadFrom(rows[i])
421 if err != nil {
422 return err
423 }
424 }
425
426 return cw.Flush(len(rows) - popCount)
427 }
428
429 func (s pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
359430 bs := &bState{
360 id: s.idCount,
361 priority: s.idCount,
362 reqWidth: s.reqWidth,
363 total: total,
364 filler: filler,
365 extender: func(r io.Reader, _ int, _ decor.Statistics) (io.Reader, int) { return r, 0 },
366 debugOut: s.debugOut,
431 id: s.idCount,
432 priority: s.idCount,
433 reqWidth: s.reqWidth,
434 total: total,
435 filler: filler,
436 renderReq: s.renderReq,
437 autoRefresh: s.autoRefresh,
367438 }
368439
369440 if total > 0 {
376447 }
377448 }
378449
379 if bs.middleware != nil {
380 bs.filler = bs.middleware(filler)
381 bs.middleware = nil
382 }
383
384 if s.popCompleted && !bs.noPop {
385 bs.priority = -(math.MaxInt32 - s.idCount)
386 }
387
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))
450 for i := 0; i < len(bs.buffers); i++ {
451 bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
452 }
391453
392454 return bs
393455 }
394
395 func syncWidth(matrix map[int][]chan int) {
396 for _, column := range matrix {
397 go maxWidthDistributor(column)
398 }
399 }
400
401 var maxWidthDistributor = func(column []chan int) {
402 var maxWidth int
403 for _, ch := range column {
404 if w := <-ch; w > maxWidth {
405 maxWidth = w
406 }
407 }
408 for _, ch := range column {
409 ch <- maxWidth
410 }
411 }
11
22 import (
33 "bytes"
4 "container/heap"
45 "context"
5 "io/ioutil"
6 "math/rand"
7 "sync"
6 "errors"
7 "io"
8 "strings"
89 "testing"
910 "time"
1011
11 "github.com/vbauerster/mpb/v7"
12 "github.com/vbauerster/mpb/v7/decor"
12 "github.com/vbauerster/mpb/v8"
13 "github.com/vbauerster/mpb/v8/decor"
1314 )
1415
15 func init() {
16 rand.Seed(time.Now().UnixNano())
17 }
18
19 func TestBarCount(t *testing.T) {
20 p := mpb.New(mpb.WithOutput(ioutil.Discard))
21
22 var wg sync.WaitGroup
23 wg.Add(1)
16 const (
17 timeout = 300 * time.Millisecond
18 )
19
20 func TestWithContext(t *testing.T) {
21 shutdown := make(chan interface{})
22 ctx, cancel := context.WithCancel(context.Background())
23 p := mpb.NewWithContext(ctx,
24 mpb.WithOutput(io.Discard),
25 mpb.WithShutdownNotifier(shutdown),
26 )
27 _ = p.AddBar(0) // never complete bar
28 _ = p.AddBar(0) // never complete bar
29 go func() {
30 time.Sleep(10 * time.Millisecond)
31 cancel()
32 }()
33
34 p.Wait()
35
36 select {
37 case v := <-shutdown:
38 if l := len(v.([]*mpb.Bar)); l != 2 {
39 t.Errorf("Expected len of bars: %d, got: %d", 2, l)
40 }
41 case <-time.After(timeout):
42 t.Errorf("Progress didn't shutdown after %v", timeout)
43 }
44 }
45
46 func TestShutdownsWithErrFiller(t *testing.T) {
47 var debug bytes.Buffer
48 shutdown := make(chan interface{})
49 p := mpb.New(
50 mpb.WithShutdownNotifier(shutdown),
51 mpb.WithOutput(io.Discard),
52 mpb.WithDebugOutput(&debug),
53 mpb.WithAutoRefresh(),
54 )
55
56 var errReturnCount int
57 testError := errors.New("test error")
58 bar := p.AddBar(100,
59 mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller {
60 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
61 if st.Current >= 22 {
62 errReturnCount++
63 return testError
64 }
65 return base.Fill(w, st)
66 })
67 }),
68 )
69
70 go func() {
71 for bar.IsRunning() {
72 bar.Increment()
73 time.Sleep(10 * time.Millisecond)
74 }
75 }()
76
77 p.Wait()
78
79 if errReturnCount != 1 {
80 t.Errorf("Expected errReturnCount: %d, got: %d\n", 1, errReturnCount)
81 }
82
83 select {
84 case v := <-shutdown:
85 if l := len(v.([]*mpb.Bar)); l != 0 {
86 t.Errorf("Expected len of bars: %d, got: %d\n", 0, l)
87 }
88 if err := strings.TrimSpace(debug.String()); err != testError.Error() {
89 t.Errorf("Expected err: %q, got %q\n", testError.Error(), err)
90 }
91 case <-time.After(timeout):
92 t.Errorf("Progress didn't shutdown after %v", timeout)
93 }
94 }
95
96 func TestShutdownAfterBarAbortWithDrop(t *testing.T) {
97 shutdown := make(chan interface{})
98 p := mpb.New(
99 mpb.WithShutdownNotifier(shutdown),
100 mpb.WithOutput(io.Discard),
101 mpb.WithAutoRefresh(),
102 )
24103 b := p.AddBar(100)
104
105 var count int
106 for i := 0; !b.Aborted(); i++ {
107 if i >= 10 {
108 count++
109 b.Abort(true)
110 } else {
111 b.Increment()
112 time.Sleep(10 * time.Millisecond)
113 }
114 }
115
116 p.Wait()
117
118 if count != 1 {
119 t.Errorf("Expected count: %d, got: %d", 1, count)
120 }
121
122 select {
123 case v := <-shutdown:
124 if l := len(v.([]*mpb.Bar)); l != 0 {
125 t.Errorf("Expected len of bars: %d, got: %d", 0, l)
126 }
127 case <-time.After(timeout):
128 t.Errorf("Progress didn't shutdown after %v", timeout)
129 }
130 }
131
132 func TestShutdownAfterBarAbortWithNoDrop(t *testing.T) {
133 shutdown := make(chan interface{})
134 p := mpb.New(
135 mpb.WithShutdownNotifier(shutdown),
136 mpb.WithOutput(io.Discard),
137 mpb.WithAutoRefresh(),
138 )
139 b := p.AddBar(100)
140
141 var count int
142 for i := 0; !b.Aborted(); i++ {
143 if i >= 10 {
144 count++
145 b.Abort(false)
146 } else {
147 b.Increment()
148 time.Sleep(10 * time.Millisecond)
149 }
150 }
151
152 p.Wait()
153
154 if count != 1 {
155 t.Errorf("Expected count: %d, got: %d", 1, count)
156 }
157
158 select {
159 case v := <-shutdown:
160 if l := len(v.([]*mpb.Bar)); l != 1 {
161 t.Errorf("Expected len of bars: %d, got: %d", 1, l)
162 }
163 case <-time.After(timeout):
164 t.Errorf("Progress didn't shutdown after %v", timeout)
165 }
166 }
167
168 func TestBarPristinePopOrder(t *testing.T) {
169 shutdown := make(chan interface{})
170 ctx, cancel := context.WithCancel(context.Background())
171 p := mpb.NewWithContext(ctx,
172 mpb.WithOutput(io.Discard), // auto refresh is disabled
173 mpb.WithShutdownNotifier(shutdown),
174 )
175 a := p.AddBar(100, mpb.BarPriority(1), mpb.BarID(1))
176 b := p.AddBar(100, mpb.BarPriority(2), mpb.BarID(2))
177 c := p.AddBar(100, mpb.BarPriority(3), mpb.BarID(3))
178 pristineOrder := []*mpb.Bar{c, b, a}
179
180 go cancel()
181
182 bars := (<-shutdown).([]*mpb.Bar)
183 if l := len(bars); l != 3 {
184 t.Fatalf("Expected len of bars: %d, got: %d", 3, l)
185 }
186
187 p.Wait()
188 pq := mpb.PriorityQueue(bars)
189
190 for _, b := range pristineOrder {
191 // higher priority pops first
192 if bar := heap.Pop(&pq).(*mpb.Bar); bar.ID() != b.ID() {
193 t.Errorf("Expected bar id: %d, got bar id: %d", b.ID(), bar.ID())
194 }
195 }
196 }
197
198 func makeUpdateBarPriorityTest(refresh, lazy bool) func(*testing.T) {
199 return func(t *testing.T) {
200 shutdown := make(chan interface{})
201 refreshCh := make(chan interface{})
202 ctx, cancel := context.WithCancel(context.Background())
203 p := mpb.NewWithContext(ctx,
204 mpb.WithOutput(io.Discard),
205 mpb.WithManualRefresh(refreshCh),
206 mpb.WithShutdownNotifier(shutdown),
207 )
208 a := p.AddBar(100, mpb.BarPriority(1), mpb.BarID(1))
209 b := p.AddBar(100, mpb.BarPriority(2), mpb.BarID(2))
210 c := p.AddBar(100, mpb.BarPriority(3), mpb.BarID(3))
211
212 p.UpdateBarPriority(c, 2, lazy)
213 p.UpdateBarPriority(b, 3, lazy)
214 checkOrder := []*mpb.Bar{b, c, a} // updated order
215
216 if refresh {
217 refreshCh <- time.Now()
218 } else if lazy {
219 checkOrder = []*mpb.Bar{c, b, a} // pristine order
220 }
221
222 go cancel()
223
224 bars := (<-shutdown).([]*mpb.Bar)
225 if l := len(bars); l != 3 {
226 t.Fatalf("Expected len of bars: %d, got: %d", 3, l)
227 }
228
229 p.Wait()
230 pq := mpb.PriorityQueue(bars)
231
232 for _, b := range checkOrder {
233 // higher priority pops first
234 if bar := heap.Pop(&pq).(*mpb.Bar); bar.ID() != b.ID() {
235 t.Errorf("Expected bar id: %d, got bar id: %d", b.ID(), bar.ID())
236 }
237 }
238 }
239 }
240
241 func TestUpdateBarPriority(t *testing.T) {
242 makeUpdateBarPriorityTest(false, false)(t)
243 makeUpdateBarPriorityTest(true, false)(t)
244 }
245
246 func TestUpdateBarPriorityLazy(t *testing.T) {
247 makeUpdateBarPriorityTest(false, true)(t)
248 makeUpdateBarPriorityTest(true, true)(t)
249 }
250
251 func TestNoOutput(t *testing.T) {
252 var buf bytes.Buffer
253 p := mpb.New(mpb.WithOutput(&buf))
254 bar := p.AddBar(100)
255
25256 go func() {
26 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
27 for i := 0; i < 100; i++ {
28 if i == 33 {
29 wg.Done()
30 }
31 b.Increment()
32 time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2)
257 for !bar.Completed() {
258 bar.Increment()
33259 }
34260 }()
35261
36 wg.Wait()
37 count := p.BarCount()
38 if count != 1 {
39 t.Errorf("BarCount want: %q, got: %q\n", 1, count)
40 }
41
42 b.Abort(true)
43 p.Wait()
44 }
45
46 func TestBarAbort(t *testing.T) {
47 p := mpb.New(mpb.WithOutput(ioutil.Discard))
48
49 var wg sync.WaitGroup
50 wg.Add(1)
51 bars := make([]*mpb.Bar, 3)
52 for i := 0; i < 3; i++ {
53 b := p.AddBar(100)
54 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
55 go func(n int) {
56 for i := 0; !b.Completed(); i++ {
57 if n == 0 && i >= 33 {
58 b.Abort(true)
59 wg.Done()
60 }
61 b.Increment()
62 time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2)
63 }
64 }(i)
65 bars[i] = b
66 }
67
68 wg.Wait()
69 count := p.BarCount()
70 if count != 2 {
71 t.Errorf("BarCount want: %q, got: %q\n", 2, count)
72 }
73 bars[1].Abort(true)
74 bars[2].Abort(true)
75 p.Wait()
76 }
77
78 func TestWithContext(t *testing.T) {
79 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 )
86
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 }
100
101 time.Sleep(50 * time.Millisecond)
102 cancel()
103
104 p.Wait()
105 select {
106 case <-shutdown:
107 case <-time.After(100 * time.Millisecond):
108 t.Error("Progress didn't stop")
109 }
110 }
111
112 // MaxWidthDistributor shouldn't stuck in the middle while removing or aborting a bar
113 func TestMaxWidthDistributor(t *testing.T) {
114
115 makeWrapper := func(f func([]chan int), start, end chan struct{}) func([]chan int) {
116 return func(column []chan int) {
117 start <- struct{}{}
118 f(column)
119 <-end
120 }
121 }
122
123 ready := make(chan struct{})
124 start := make(chan struct{})
125 end := make(chan struct{})
126 *mpb.MaxWidthDistributor = makeWrapper(*mpb.MaxWidthDistributor, start, end)
127
128 total := 80
129 numBars := 3
130 p := mpb.New(mpb.WithOutput(ioutil.Discard))
131 for i := 0; i < numBars; i++ {
132 bar := p.AddBar(int64(total),
133 mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0),
134 mpb.PrependDecorators(
135 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace),
136 ),
137 )
138 go func() {
139 <-ready
140 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
141 for i := 0; i < total; i++ {
142 start := time.Now()
143 if bar.ID() >= numBars-1 && i >= 42 {
144 bar.Abort(true)
145 }
146 time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2)
147 bar.Increment()
148 bar.DecoratorEwmaUpdate(time.Since(start))
149 }
150 }()
151 }
152
153 go func() {
154 <-ready
155 p.Wait()
156 close(start)
157 }()
158
159 res := t.Run("maxWidthDistributor", func(t *testing.T) {
160 close(ready)
161 for v := range start {
162 timer := time.NewTimer(100 * time.Millisecond)
163 select {
164 case end <- v:
165 timer.Stop()
166 case <-timer.C:
167 t.FailNow()
168 }
169 }
170 })
171
172 if !res {
173 t.Error("maxWidthDistributor stuck in the middle")
174 }
175 }
176
177 func getLastLine(bb []byte) []byte {
178 split := bytes.Split(bb, []byte("\n"))
179 return split[len(split)-2]
180 }
181
182 func randomDuration(max time.Duration) time.Duration {
183 return time.Duration(rand.Intn(10)+1) * max / 10
184 }
262 p.Wait()
263
264 if buf.Len() != 0 {
265 t.Errorf("Expected buf.Len == 0, got: %d\n", buf.Len())
266 }
267 }
268
269 func TestAddAfterDone(t *testing.T) {
270 p := mpb.New(mpb.WithOutput(io.Discard))
271 bar := p.AddBar(100)
272 bar.IncrBy(100)
273
274 p.Wait()
275
276 _, err := p.Add(100, nil)
277
278 if err != mpb.DoneError {
279 t.Errorf("Expected %q, got: %q\n", mpb.DoneError, err)
280 }
281 }
11
22 import (
33 "io"
4 "io/ioutil"
54 "time"
65 )
76
109 bar *Bar
1110 }
1211
13 func (x *proxyReader) Read(p []byte) (int, error) {
12 func (x proxyReader) Read(p []byte) (int, error) {
1413 n, err := x.ReadCloser.Read(p)
1514 x.bar.IncrBy(n)
16 if err == io.EOF {
17 go x.bar.SetTotal(0, true)
18 }
1915 return n, err
2016 }
2117
2218 type proxyWriterTo struct {
23 io.ReadCloser // *proxyReader
24 wt io.WriterTo
25 bar *Bar
19 proxyReader
2620 }
2721
28 func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
29 n, err := x.wt.WriteTo(w)
22 func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
23 n, err := x.ReadCloser.(io.WriterTo).WriteTo(w)
3024 x.bar.IncrInt64(n)
31 if err == io.EOF {
32 go x.bar.SetTotal(0, true)
33 }
3425 return n, err
3526 }
3627
3728 type ewmaProxyReader struct {
38 io.ReadCloser // *proxyReader
39 bar *Bar
40 iT time.Time
29 io.ReadCloser
30 bar *Bar
4131 }
4232
43 func (x *ewmaProxyReader) Read(p []byte) (int, error) {
33 func (x ewmaProxyReader) Read(p []byte) (int, error) {
34 start := time.Now()
4435 n, err := x.ReadCloser.Read(p)
45 if n > 0 {
46 x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
47 x.iT = time.Now()
48 }
36 x.bar.EwmaIncrBy(n, time.Since(start))
4937 return n, err
5038 }
5139
5240 type ewmaProxyWriterTo struct {
53 io.ReadCloser // *ewmaProxyReader
54 wt io.WriterTo // *proxyWriterTo
55 bar *Bar
56 iT time.Time
41 ewmaProxyReader
5742 }
5843
59 func (x *ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
60 n, err := x.wt.WriteTo(w)
61 if n > 0 {
62 x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
63 x.iT = time.Now()
64 }
44 func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
45 start := time.Now()
46 n, err := x.ReadCloser.(io.WriterTo).WriteTo(w)
47 x.bar.EwmaIncrInt64(n, time.Since(start))
6548 return n, err
6649 }
6750
68 func newProxyReader(r io.Reader, bar *Bar) io.ReadCloser {
51 func newProxyReader(r io.Reader, b *Bar, hasEwma bool) io.ReadCloser {
6952 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}
53 if hasEwma {
54 epr := ewmaProxyReader{rc, b}
55 if _, ok := r.(io.WriterTo); ok {
56 return ewmaProxyWriterTo{epr}
7757 }
78 } else if isWriterTo {
79 rc = &proxyWriterTo{rc, wt, bar}
58 return epr
8059 }
81 return rc
60 pr := proxyReader{rc, b}
61 if _, ok := r.(io.WriterTo); ok {
62 return proxyWriterTo{pr}
63 }
64 return pr
8265 }
8366
8467 func toReadCloser(r io.Reader) io.ReadCloser {
8568 if rc, ok := r.(io.ReadCloser); ok {
8669 return rc
8770 }
88 return ioutil.NopCloser(r)
71 return toNopReadCloser(r)
8972 }
73
74 func toNopReadCloser(r io.Reader) io.ReadCloser {
75 if _, ok := r.(io.WriterTo); ok {
76 return nopReadCloserWriterTo{r}
77 }
78 return nopReadCloser{r}
79 }
80
81 type nopReadCloser struct {
82 io.Reader
83 }
84
85 func (nopReadCloser) Close() error { return nil }
86
87 type nopReadCloserWriterTo struct {
88 io.Reader
89 }
90
91 func (nopReadCloserWriterTo) Close() error { return nil }
92
93 func (c nopReadCloserWriterTo) WriteTo(w io.Writer) (int64, error) {
94 return c.Reader.(io.WriterTo).WriteTo(w)
95 }
22 import (
33 "bytes"
44 "io"
5 "io/ioutil"
65 "strings"
76 "testing"
87
9 "github.com/vbauerster/mpb/v7"
8 "github.com/vbauerster/mpb/v8"
109 )
1110
1211 const content = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
2827 }
2928
3029 func TestProxyReader(t *testing.T) {
31 p := mpb.New(mpb.WithOutput(ioutil.Discard))
30 p := mpb.New(mpb.WithOutput(io.Discard))
3231
33 tReader := &testReader{strings.NewReader(content), false}
32 tr := &testReader{strings.NewReader(content), false}
3433
35 bar := p.AddBar(int64(len(content)), mpb.BarFillerTrim())
34 bar := p.New(int64(len(content)), mpb.NopStyle())
3635
3736 var buf bytes.Buffer
38 _, err := io.Copy(&buf, bar.ProxyReader(tReader))
37 _, err := io.Copy(&buf, bar.ProxyReader(tr))
3938 if err != nil {
40 t.Errorf("Error copying from reader: %+v\n", err)
39 t.Errorf("io.Copy: %s\n", err.Error())
4140 }
4241
4342 p.Wait()
4443
45 if !tReader.called {
44 if !tr.called {
4645 t.Error("Read not called")
4746 }
4847
5150 }
5251 }
5352
54 type testWriterTo struct {
53 type testReadCloser struct {
5554 io.Reader
56 wt io.WriterTo
5755 called bool
5856 }
5957
60 func (wt *testWriterTo) WriteTo(w io.Writer) (n int64, err error) {
61 wt.called = true
62 return wt.wt.WriteTo(w)
58 func (r *testReadCloser) Close() error {
59 r.called = true
60 return nil
6361 }
6462
65 func TestProxyWriterTo(t *testing.T) {
66 p := mpb.New(mpb.WithOutput(ioutil.Discard))
63 func TestProxyReadCloser(t *testing.T) {
64 p := mpb.New(mpb.WithOutput(io.Discard))
6765
68 var reader io.Reader = strings.NewReader(content)
69 wt := reader.(io.WriterTo)
70 tReader := &testWriterTo{reader, wt, false}
66 tr := &testReadCloser{strings.NewReader(content), false}
7167
72 bar := p.AddBar(int64(len(content)), mpb.BarFillerTrim())
68 bar := p.New(int64(len(content)), mpb.NopStyle())
69
70 rc := bar.ProxyReader(tr)
71 _, err := io.Copy(io.Discard, rc)
72 if err != nil {
73 t.Errorf("io.Copy: %s\n", err.Error())
74 }
75 _ = rc.Close()
76
77 p.Wait()
78
79 if !tr.called {
80 t.Error("Close not called")
81 }
82 }
83
84 type testReaderWriterTo struct {
85 io.Reader
86 called bool
87 }
88
89 func (r *testReaderWriterTo) WriteTo(w io.Writer) (n int64, err error) {
90 r.called = true
91 return r.Reader.(io.WriterTo).WriteTo(w)
92 }
93
94 func TestProxyReaderWriterTo(t *testing.T) {
95 p := mpb.New(mpb.WithOutput(io.Discard))
96
97 tr := &testReaderWriterTo{strings.NewReader(content), false}
98
99 bar := p.New(int64(len(content)), mpb.NopStyle())
73100
74101 var buf bytes.Buffer
75 _, err := io.Copy(&buf, bar.ProxyReader(tReader))
102 _, err := io.Copy(&buf, bar.ProxyReader(tr))
76103 if err != nil {
77 t.Errorf("Error copying from reader: %+v\n", err)
104 t.Errorf("io.Copy: %s\n", err.Error())
78105 }
79106
80107 p.Wait()
81108
82 if !tReader.called {
109 if !tr.called {
83110 t.Error("WriteTo not called")
84111 }
85112
0 package mpb
1
2 import (
3 "io"
4 "time"
5 )
6
7 type proxyWriter struct {
8 io.WriteCloser
9 bar *Bar
10 }
11
12 func (x proxyWriter) Write(p []byte) (int, error) {
13 n, err := x.WriteCloser.Write(p)
14 x.bar.IncrBy(n)
15 return n, err
16 }
17
18 type proxyReaderFrom struct {
19 proxyWriter
20 }
21
22 func (x proxyReaderFrom) ReadFrom(r io.Reader) (int64, error) {
23 n, err := x.WriteCloser.(io.ReaderFrom).ReadFrom(r)
24 x.bar.IncrInt64(n)
25 return n, err
26 }
27
28 type ewmaProxyWriter struct {
29 io.WriteCloser
30 bar *Bar
31 }
32
33 func (x ewmaProxyWriter) Write(p []byte) (int, error) {
34 start := time.Now()
35 n, err := x.WriteCloser.Write(p)
36 x.bar.EwmaIncrBy(n, time.Since(start))
37 return n, err
38 }
39
40 type ewmaProxyReaderFrom struct {
41 ewmaProxyWriter
42 }
43
44 func (x ewmaProxyReaderFrom) ReadFrom(r io.Reader) (int64, error) {
45 start := time.Now()
46 n, err := x.WriteCloser.(io.ReaderFrom).ReadFrom(r)
47 x.bar.EwmaIncrInt64(n, time.Since(start))
48 return n, err
49 }
50
51 func newProxyWriter(w io.Writer, b *Bar, hasEwma bool) io.WriteCloser {
52 wc := toWriteCloser(w)
53 if hasEwma {
54 epw := ewmaProxyWriter{wc, b}
55 if _, ok := w.(io.ReaderFrom); ok {
56 return ewmaProxyReaderFrom{epw}
57 }
58 return epw
59 }
60 pw := proxyWriter{wc, b}
61 if _, ok := w.(io.ReaderFrom); ok {
62 return proxyReaderFrom{pw}
63 }
64 return pw
65 }
66
67 func toWriteCloser(w io.Writer) io.WriteCloser {
68 if wc, ok := w.(io.WriteCloser); ok {
69 return wc
70 }
71 return toNopWriteCloser(w)
72 }
73
74 func toNopWriteCloser(w io.Writer) io.WriteCloser {
75 if _, ok := w.(io.ReaderFrom); ok {
76 return nopWriteCloserReaderFrom{w}
77 }
78 return nopWriteCloser{w}
79 }
80
81 type nopWriteCloser struct {
82 io.Writer
83 }
84
85 func (nopWriteCloser) Close() error { return nil }
86
87 type nopWriteCloserReaderFrom struct {
88 io.Writer
89 }
90
91 func (nopWriteCloserReaderFrom) Close() error { return nil }
92
93 func (c nopWriteCloserReaderFrom) ReadFrom(r io.Reader) (int64, error) {
94 return c.Writer.(io.ReaderFrom).ReadFrom(r)
95 }
0 package mpb_test
1
2 import (
3 "bytes"
4 "io"
5 "strings"
6 "testing"
7
8 "github.com/vbauerster/mpb/v8"
9 )
10
11 type testWriter struct {
12 io.Writer
13 called bool
14 }
15
16 func (w *testWriter) Write(p []byte) (n int, err error) {
17 w.called = true
18 return w.Writer.Write(p)
19 }
20
21 func TestProxyWriter(t *testing.T) {
22 p := mpb.New(mpb.WithOutput(io.Discard))
23
24 var buf bytes.Buffer
25 tw := &testWriter{&buf, false}
26
27 bar := p.New(int64(len(content)), mpb.NopStyle())
28
29 _, err := io.Copy(bar.ProxyWriter(tw), strings.NewReader(content))
30 if err != nil {
31 t.Errorf("io.Copy: %s\n", err.Error())
32 }
33
34 p.Wait()
35
36 if !tw.called {
37 t.Error("Read not called")
38 }
39
40 if got := buf.String(); got != content {
41 t.Errorf("Expected content: %s, got: %s\n", content, got)
42 }
43 }
44
45 type testWriteCloser struct {
46 io.Writer
47 called bool
48 }
49
50 func (w *testWriteCloser) Close() error {
51 w.called = true
52 return nil
53 }
54
55 func TestProxyWriteCloser(t *testing.T) {
56 p := mpb.New(mpb.WithOutput(io.Discard))
57
58 var buf bytes.Buffer
59 tw := &testWriteCloser{&buf, false}
60
61 bar := p.New(int64(len(content)), mpb.NopStyle())
62
63 wc := bar.ProxyWriter(tw)
64 _, err := io.Copy(wc, strings.NewReader(content))
65 if err != nil {
66 t.Errorf("io.Copy: %s\n", err.Error())
67 }
68 _ = wc.Close()
69
70 p.Wait()
71
72 if !tw.called {
73 t.Error("Close not called")
74 }
75 }
76
77 type testWriterReadFrom struct {
78 io.Writer
79 called bool
80 }
81
82 func (w *testWriterReadFrom) ReadFrom(r io.Reader) (n int64, err error) {
83 w.called = true
84 return w.Writer.(io.ReaderFrom).ReadFrom(r)
85 }
86
87 type dumbReader struct {
88 r io.Reader
89 }
90
91 func (r dumbReader) Read(p []byte) (int, error) {
92 return r.r.Read(p)
93 }
94
95 func TestProxyWriterReadFrom(t *testing.T) {
96 p := mpb.New(mpb.WithOutput(io.Discard))
97
98 var buf bytes.Buffer
99 tw := &testWriterReadFrom{&buf, false}
100
101 bar := p.New(int64(len(content)), mpb.NopStyle())
102
103 // To trigger ReadFrom, WriteTo needs to be hidden, hence a dumb wrapper
104 dr := dumbReader{strings.NewReader(content)}
105 _, err := io.Copy(bar.ProxyWriter(tw), dr)
106 if err != nil {
107 t.Errorf("io.Copy: %s\n", err.Error())
108 }
109
110 p.Wait()
111
112 if !tw.called {
113 t.Error("ReadFrom not called")
114 }
115
116 if got := buf.String(); got != content {
117 t.Errorf("Expected content: %s, got: %s\n", content, got)
118 }
119 }