Codebase list golang-github-vbauerster-mpb / c5547aa
Update upstream source from tag 'upstream/8.8.2' Update to upstream version '8.8.2' with Debian dir a91e096184cbf8323fd0fc3058cf2d13b2e17586 Reinhard Tartler 1 year, 7 months ago
110 changed file(s) with 5684 addition(s) and 2751 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@v4
25 - uses: actions/setup-go@v5
26 with:
27 go-version: ${{ matrix.go-version }}
28 cache: false
29 - uses: golangci/golangci-lint-action@v6
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
34 # Optional: golangci-lint command line arguments.
35 # args: --issues-exit-code=0
36
37 # Optional: show only new issues if it's a pull request. The default value is `false`.
38 only-new-issues: true
0 name: test
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 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@v4
24 - uses: actions/setup-go@v5
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/v6)
3 [![Build Status](https://travis-ci.org/vbauerster/mpb.svg?branch=master)](https://travis-ci.org/vbauerster/mpb)
4 [![Go Report Card](https://goreportcard.com/badge/github.com/vbauerster/mpb)](https://goreportcard.com/report/github.com/vbauerster/mpb)
2 [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/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)
55
66 **mpb** is a Go lib for rendering progress bars in terminal applications.
77
2525 "math/rand"
2626 "time"
2727
28 "github.com/vbauerster/mpb/v6"
29 "github.com/vbauerster/mpb/v6/decor"
28 "github.com/vbauerster/mpb/v8"
29 "github.com/vbauerster/mpb/v8/decor"
3030 )
3131
3232 func main() {
3535
3636 total := 100
3737 name := "Single Bar:"
38 // adding a single bar, which will inherit container's width
39 bar := p.Add(int64(total),
40 // progress bar filler with customized style
41 mpb.NewBarFiller("╢▌▌░╟"),
38 // create a single bar, which will inherit container's width
39 bar := p.New(int64(total),
40 // BarFillerBuilder with custom style
41 mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"),
4242 mpb.PrependDecorators(
4343 // display our name with one space on the right
44 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
44 decor.Name(name, decor.WC{C: decor.DindentRight | decor.DextraSpace}),
4545 // replace ETA decorator with "done" message, OnComplete event
46 decor.OnComplete(
47 decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "done",
48 ),
46 decor.OnComplete(decor.AverageETA(decor.ET_STYLE_GO), "done"),
4947 ),
5048 mpb.AppendDecorators(decor.Percentage()),
5149 )
6462
6563 ```go
6664 var wg sync.WaitGroup
67 // pass &wg (optional), so p will wait for it eventually
65 // passed wg will be accounted at p.Wait() call
6866 p := mpb.New(mpb.WithWaitGroup(&wg))
6967 total, numBars := 100, 3
7068 wg.Add(numBars)
8179 mpb.AppendDecorators(
8280 // replace ETA decorator with "done" message, OnComplete event
8381 decor.OnComplete(
84 // ETA decorator with ewma age of 60
85 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
82 // ETA decorator with ewma age of 30
83 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
8684 ),
8785 ),
8886 )
9694 // EWMA's unit of measure is an iteration's duration
9795 start := time.Now()
9896 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
99 bar.Increment()
100 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
101 bar.DecoratorEwmaUpdate(time.Since(start))
97 // we need to call EwmaIncrement to fulfill ewma decorator's contract
98 bar.EwmaIncrement(time.Since(start))
10299 }
103100 }()
104101 }
105 // Waiting for passed &wg and for all bars to complete and flush
102 // wait for passed wg and for all bars to complete and flush
106103 p.Wait()
107104 ```
108105
00 module github.com/vbauerster/mpb/_examples/barExtender
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
66 "sync"
77 "time"
88
9 "github.com/vbauerster/mpb/v6"
10 "github.com/vbauerster/mpb/v6/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.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
66 "sync"
77 "time"
88
9 "github.com/vbauerster/mpb/v6"
10 "github.com/vbauerster/mpb/v6/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/v6 v6.0.3
4 require (
5 github.com/fatih/color v1.17.0
6 github.com/vbauerster/mpb/v8 v8.8.2
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.20 // indirect
14 github.com/mattn/go-runewidth v0.0.16 // indirect
15 github.com/rivo/uniseg v0.4.7 // indirect
16 golang.org/x/sys v0.24.0 // indirect
17 )
22 import (
33 "fmt"
44 "math/rand"
5 "sync"
65 "time"
76
8 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v6/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(
31 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
32 decor.Name(job, decor.WCSyncSpaceR),
27 decor.Name(task, decor.WC{C: decor.DindentRight | decor.DextraSpace}),
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{C: decor.DindentRight | decor.DextraSpace}),
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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v6"
8 "github.com/vbauerster/mpb/v6/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("╢▌▌░╟"))),
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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v6/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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v6"
8 "github.com/vbauerster/mpb/v6/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 set -e
2
3 for d in *; do
4 [ ! -d "$d" ] && continue
5 pushd "$d" >/dev/null 2>&1
6 go mod tidy
7 popd >/dev/null 2>&1
8 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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
22 import (
33 "crypto/rand"
44 "io"
5 "io/ioutil"
65 "time"
76
8 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v6/decor"
7 "github.com/vbauerster/mpb/v8"
8 "github.com/vbauerster/mpb/v8/decor"
109 )
1110
1211 func main() {
13 var total int64 = 1024 * 1024 * 500
14 reader := io.LimitReader(rand.Reader, total)
12 var total int64 = 64 * 1024 * 1024
13
14 r, w := io.Pipe()
15
16 go func() {
17 for i := 0; i < 1024; i++ {
18 _, _ = io.Copy(w, io.LimitReader(rand.Reader, 64*1024))
19 time.Sleep(time.Second / 10)
20 }
21 w.Close()
22 }()
1523
1624 p := mpb.New(
1725 mpb.WithWidth(60),
1826 mpb.WithRefreshRate(180*time.Millisecond),
1927 )
2028
21 bar := p.Add(total,
22 mpb.NewBarFiller("[=>-|"),
29 bar := p.New(total,
30 mpb.BarStyle().Rbound("|"),
2331 mpb.PrependDecorators(
24 decor.CountersKibiByte("% .2f / % .2f"),
32 decor.Counters(decor.SizeB1024(0), "% .2f / % .2f"),
2533 ),
2634 mpb.AppendDecorators(
27 decor.EwmaETA(decor.ET_STYLE_GO, 90),
35 decor.EwmaETA(decor.ET_STYLE_GO, 30),
2836 decor.Name(" ] "),
29 decor.EwmaSpeed(decor.UnitKiB, "% .2f", 60),
37 decor.EwmaSpeed(decor.SizeB1024(0), "% .2f", 30),
3038 ),
3139 )
3240
3341 // create proxy reader
34 proxyReader := bar.ProxyReader(reader)
42 proxyReader := bar.ProxyReader(r)
3543 defer proxyReader.Close()
3644
3745 // copy from proxyReader, ignoring errors
38 io.Copy(ioutil.Discard, proxyReader)
46 _, _ = io.Copy(io.Discard, proxyReader)
3947
4048 p.Wait()
4149 }
+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/v6 v6.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/v6"
9 "github.com/vbauerster/mpb/v6/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 }
0 module github.com/vbauerster/mpb/_examples/mexicanBar
1
2 go 1.17
3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.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 := "Complex Filler:"
16 bs := mpb.BarStyle()
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,
34 mpb.PrependDecorators(decor.Name(name)),
35 mpb.AppendDecorators(decor.Percentage()),
36 )
37 // simulating some work
38 max := 100 * time.Millisecond
39 for i := 0; i < total; i++ {
40 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
41 bar.Increment()
42 }
43 // wait for our bar to complete
44 p.Wait()
45 }
00 module github.com/vbauerster/mpb/_examples/multiBars
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v6/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)
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/v6 v6.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/v6"
10 "github.com/vbauerster/mpb/v6/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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v6"
8 "github.com/vbauerster/mpb/v6/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)
2323 ),
2424 mpb.AppendDecorators(
2525 decor.OnComplete(decor.Name(" "), ""),
26 decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_GO, 60), ""),
26 decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_GO, 30), ""),
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.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.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 bwg, qwg sync.WaitGroup
16 bwg.Add(numBars)
17 qwg.Add(1)
18 done := make(chan interface{})
19 p := mpb.New(mpb.WithWidth(64), mpb.WithShutdownNotifier(done), mpb.WithWaitGroup(&qwg))
20
21 log.SetOutput(p)
22
23 go func() {
24 defer qwg.Done()
25 for {
26 select {
27 case <-done:
28 // after done, underlying io.Writer returns mpb.DoneError
29 // so following isn't printed
30 log.Println("all done")
31 return
32 default:
33 log.Println("waiting for done")
34 time.Sleep(150 * time.Millisecond)
35 }
36 }
37 }()
38
39 nopBar := p.MustAdd(0, nil)
40
41 for i := 0; i < numBars; i++ {
42 name := fmt.Sprintf("Bar#%d:", i)
43 bar := p.AddBar(int64(total),
44 mpb.PrependDecorators(
45 decor.Name(name),
46 decor.Percentage(decor.WCSyncSpace),
47 ),
48 mpb.AppendDecorators(
49 decor.OnComplete(
50 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
51 ),
52 ),
53 )
54 // simulating some work
55 go func() {
56 defer bwg.Done()
57 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
58 max := 100 * time.Millisecond
59 for i := 0; i < total; i++ {
60 // start variable is solely for EWMA calculation
61 // EWMA's unit of measure is an iteration's duration
62 start := time.Now()
63 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
64 // we need to call EwmaIncrement to fulfill ewma decorator's contract
65 bar.EwmaIncrement(time.Since(start))
66 }
67 log.Println(name, "done")
68 }()
69 }
70
71 bwg.Wait()
72 log.Println("completing nop bar")
73 nopBar.EnableTriggerComplete()
74
75 p.Wait()
76 }
00 module github.com/vbauerster/mpb/_examples/quietMode
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
22 import (
33 "flag"
44 "fmt"
5 "io"
56 "math/rand"
67 "sync"
78 "time"
89
9 "github.com/vbauerster/mpb/v6"
10 "github.com/vbauerster/mpb/v6/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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v6/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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v6/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 bar := p.Add(int64(total),
22 // reverse Bar#1
23 mpb.NewBarFillerPick("", i == 1),
28 bar := p.New(int64(total),
29 condFillerBuilder(i == 1),
2430 mpb.PrependDecorators(
2531 // simple name decorator
2632 decor.Name(name),
3036 mpb.AppendDecorators(
3137 // replace ETA decorator with "done" message, OnComplete event
3238 decor.OnComplete(
33 // ETA decorator with ewma age of 60
34 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",
3541 ),
3642 ),
3743 )
4551 // EWMA's unit of measure is an iteration's duration
4652 start := time.Now()
4753 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
48 bar.Increment()
49 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
50 bar.DecoratorEwmaUpdate(time.Since(start))
54 // we need to call EwmaIncrement to fulfill ewma decorator's contract
55 bar.EwmaIncrement(time.Since(start))
5156 }
5257 }()
5358 }
54 // 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
5560 p.Wait()
5661 }
00 module github.com/vbauerster/mpb/_examples/singleBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
33 "math/rand"
44 "time"
55
6 "github.com/vbauerster/mpb/v6"
7 "github.com/vbauerster/mpb/v6/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("╢▌▌░╟"),
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
22 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
22 decor.Name(name, decor.WC{C: decor.DindentRight | decor.DextraSpace}),
2323 // replace ETA decorator with "done" message, OnComplete event
24 decor.OnComplete(
25 decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "done",
26 ),
24 decor.OnComplete(decor.AverageETA(decor.ET_STYLE_GO), "done"),
2725 ),
2826 mpb.AppendDecorators(decor.Percentage()),
2927 )
0 module github.com/vbauerster/mpb/_examples/spinTipBar
1
2 go 1.17
3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.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().Tip(`-`, `\`, `|`, `/`).TipMeta(func(s string) string {
18 return "\033[31m" + s + "\033[0m" // red
19 }),
20 mpb.PrependDecorators(decor.Name(name)),
21 mpb.AppendDecorators(decor.Percentage()),
22 )
23 // simulating some work
24 max := 100 * time.Millisecond
25 for i := 0; i < total; i++ {
26 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
27 bar.Increment()
28 }
29 // wait for our bar to complete and flush
30 p.Wait()
31 }
00 module github.com/vbauerster/mpb/_examples/spinnerBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v6/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("╢▌▌░╟"),
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.NewSpinnerFiller(spinnerStyle, mpb.SpinnerOnMiddle),
44 mpb.PrependDecorators(
45 // simple name decorator
46 decor.Name(name),
47 ),
48 mpb.AppendDecorators(
49 // replace ETA decorator with "done" message, OnComplete event
50 decor.OnComplete(
51 // ETA decorator with ewma age of 60
52 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
53 ),
54 ),
55 )
56 }
57
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/v6 v6.0.3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v6"
9 "github.com/vbauerster/mpb/v6/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/v6 v6.0.3
4 require (
5 github.com/pkg/profile v1.7.0
6 github.com/vbauerster/mpb/v8 v8.8.2
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.4 // indirect
13 github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
14 github.com/mattn/go-runewidth v0.0.16 // indirect
15 github.com/rivo/uniseg v0.4.7 // indirect
16 golang.org/x/sys v0.24.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/v6"
9 "github.com/vbauerster/mpb/v6/decor"
10 "github.com/pkg/profile"
11 "github.com/vbauerster/mpb/v8"
12 "github.com/vbauerster/mpb/v8/decor"
1013 )
1114
1215 const (
13 totalBars = 32
16 totalBars = 42
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.OnComplete(decor.Percentage(decor.WCSyncWidth), "done"),
3141 ),
3242 mpb.AppendDecorators(
33 decor.OnComplete(
34 decor.Percentage(decor.WC{W: 5}), "done",
35 ),
43 decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), ""),
44 decor.EwmaSpeed(decor.SizeB1024(0), "", 30, decor.WCSyncSpace),
3645 ),
3746 )
3847
4049 defer wg.Done()
4150 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
4251 max := 100 * time.Millisecond
43 for !bar.Completed() {
52 for bar.IsRunning() {
53 start := time.Now()
4454 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
45 bar.Increment()
55 bar.EwmaIncrement(time.Since(start))
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
3
4 require github.com/vbauerster/mpb/v8 v8.8.2
35
46 require (
5 github.com/mattn/go-runewidth v0.0.10
6 github.com/vbauerster/mpb/v6 v6.0.3
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.0 // indirect
712 )
22 import (
33 "errors"
44 "fmt"
5 "io"
5 "math"
66 "math/rand"
77 "sync"
88 "time"
99
10 "github.com/mattn/go-runewidth"
11 "github.com/vbauerster/mpb/v6"
12 "github.com/vbauerster/mpb/v6/decor"
10 "github.com/vbauerster/mpb/v8"
11 "github.com/vbauerster/mpb/v8/decor"
1312 )
1413
1514 func main() {
1615 p := mpb.New()
1716
18 total := 100
19 msgCh := make(chan string)
20 resumeCh := make(chan struct{})
21 nextCh := make(chan struct{}, 1)
22 bar := p.AddBar(int64(total),
23 mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller {
24 var msg *string
25 return mpb.BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
26 select {
27 case m := <-msgCh:
28 defer func() {
29 msg = &m
30 }()
31 nextCh <- struct{}{}
32 case <-resumeCh:
33 msg = nil
34 default:
17 total, numBars := 100, 3
18 err := new(errorWrapper)
19 timer := time.AfterFunc(2*time.Second, func() {
20 err.set(errors.New("timeout"), rand.Intn(numBars))
21 })
22 defer timer.Stop()
23
24 for i := 0; i < numBars; i++ {
25 msgCh := make(chan string, 1)
26 bar := p.AddBar(int64(total),
27 mpb.PrependDecorators(newTitleDecorator(fmt.Sprintf("Bar#%d:", i), msgCh, 16)),
28 mpb.AppendDecorators(decor.Percentage(decor.WCSyncWidth)),
29 )
30 // simulating some work
31 barID := i
32 go func() {
33 max := 100 * time.Millisecond
34 for i := 0; i < total; i++ {
35 if err.check(barID) {
36 msgCh <- fmt.Sprintf("%s at %d, retrying...", err.Error(), i)
37 err.reset()
38 i--
39 bar.SetRefill(int64(i))
40 continue
3541 }
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 }
42 })
43 }),
44 mpb.PrependDecorators(decor.Name("my bar:")),
45 mpb.AppendDecorators(newCustomPercentage(nextCh)),
46 )
47 ew := &errorWrapper{}
48 time.AfterFunc(2*time.Second, func() {
49 ew.reset(errors.New("timeout"))
50 })
51 // simulating some work
52 go func() {
53 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
54 max := 100 * time.Millisecond
55 for i := 0; i < total; i++ {
56 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
57 if ew.isErr() {
58 msgCh <- fmt.Sprintf("%s at %d, retrying...", ew.Error(), i)
59 go ew.reset(nil)
60 i--
61 bar.SetRefill(int64(i))
62 time.Sleep(3 * time.Second)
63 resumeCh <- struct{}{}
64 continue
42 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
43 bar.Increment()
6544 }
66 bar.Increment()
67 }
68 }()
45 }()
46 }
6947
7048 p.Wait()
7149 }
7250
7351 type errorWrapper struct {
7452 sync.RWMutex
75 err error
53 err error
54 barID int
7655 }
7756
7857 func (ew *errorWrapper) Error() string {
8160 return ew.err.Error()
8261 }
8362
84 func (ew *errorWrapper) isErr() bool {
63 func (ew *errorWrapper) check(barID int) bool {
8564 ew.RLock()
8665 defer ew.RUnlock()
87 return ew.err != nil
66 return ew.err != nil && ew.barID == barID
8867 }
8968
90 func (ew *errorWrapper) reset(err error) {
69 func (ew *errorWrapper) set(err error, barID int) {
9170 ew.Lock()
9271 ew.err = err
72 ew.barID = barID
9373 ew.Unlock()
9474 }
9575
96 func newCustomPercentage(nextCh <-chan struct{}) decor.Decorator {
97 base := decor.Percentage()
98 fn := func(s decor.Statistics) string {
76 func (ew *errorWrapper) reset() {
77 ew.Lock()
78 ew.err = nil
79 ew.Unlock()
80 }
81
82 type title struct {
83 decor.Decorator
84 name string
85 msgCh <-chan string
86 msg string
87 count int
88 limit int
89 }
90
91 func (d *title) Decor(stat decor.Statistics) (string, int) {
92 if d.count == 0 {
9993 select {
100 case <-nextCh:
101 return ""
94 case msg := <-d.msgCh:
95 d.count = d.limit
96 d.msg = msg
10297 default:
103 return base.Decor(s)
98 return d.Decorator.Decor(stat)
10499 }
105100 }
106 return decor.Any(fn)
101 d.count--
102 _, _ = d.Format("")
103 return fmt.Sprintf("%s %s", d.name, d.msg), math.MaxInt
107104 }
105
106 func newTitleDecorator(name string, msgCh <-chan string, limit int) decor.Decorator {
107 return &title{
108 Decorator: decor.Name(name),
109 name: name,
110 msgCh: msgCh,
111 limit: limit,
112 }
113 }
0 module github.com/vbauerster/mpb/_examples/tipOnComplete
1
2 go 1.17
3
4 require github.com/vbauerster/mpb/v8 v8.8.2
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.16 // indirect
10 github.com/rivo/uniseg v0.4.7 // indirect
11 golang.org/x/sys v0.24.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 }
+481
-363
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/v6/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 container *Progress
22 bs *bState
23 bsOk chan struct{}
24 ctx context.Context
25 cancel func()
26 }
27
28 type syncTable [2][]chan int
29 type extenderFunc func(decor.Statistics, ...io.Reader) ([]io.Reader, error)
30
31 // bState is actual bar's state.
4832 type bState struct {
49 id int
50 priority int
51 reqWidth int
52 total int64
53 current int64
54 refill int64
55 lastN int64
56 iterated bool
57 trimSpace bool
58 completed bool
59 completeFlushed bool
60 triggerComplete bool
61 dropOnComplete bool
62 noPop bool
63 aDecorators []decor.Decorator
64 pDecorators []decor.Decorator
65 averageDecorators []decor.AverageDecorator
66 ewmaDecorators []decor.EwmaDecorator
67 shutdownListeners []decor.ShutdownListener
68 bufP, bufB, bufA *bytes.Buffer
69 filler BarFiller
70 middleware func(BarFiller) BarFiller
71 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)
33 id int
34 priority int
35 reqWidth int
36 shutdown int
37 total int64
38 current int64
39 refill int64
40 trimSpace bool
41 aborted bool
42 triggerComplete bool
43 rmOnComplete bool
44 noPop bool
45 autoRefresh bool
46 buffers [3]*bytes.Buffer
47 decorators [2][]decor.Decorator
48 ewmaDecorators []decor.EwmaDecorator
49 filler BarFiller
50 extender extenderFunc
51 renderReq chan<- time.Time
52 waitBar *Bar // key for (*pState).queueBars
53 }
54
55 type renderFrame struct {
56 rows []io.Reader
57 shutdown int
58 rmOnComplete bool
59 noPop bool
60 err error
61 }
62
63 func newBar(ctx context.Context, container *Progress, bs *bState) *Bar {
64 ctx, cancel := context.WithCancel(ctx)
8265
8366 bar := &Bar{
67 priority: bs.priority,
68 frameCh: make(chan *renderFrame, 1),
69 operateState: make(chan func(*bState)),
70 bsOk: make(chan struct{}),
8471 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{}),
72 ctx: ctx,
9373 cancel: cancel,
94 dlogger: log.New(bs.debugOut, logPrefix, log.Lshortfile),
95 }
96
97 go bar.serve(ctx, bs)
74 }
75
76 container.bwg.Add(1)
77 go bar.serve(bs)
9878 return bar
9979 }
10080
101 // ProxyReader wraps r with metrics required for progress tracking.
102 // Panics if r is nil.
81 // ProxyReader wraps io.Reader with metrics required for progress
82 // tracking. If `r` is 'unknown total/size' reader it's mandatory
83 // to call `(*Bar).SetTotal(-1, true)` after the wrapper returns
84 // `io.EOF`. If bar is already completed or aborted, returns nil.
85 // Panics if `r` is nil.
10386 func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
10487 if r == nil {
10588 panic("expected non nil io.Reader")
10689 }
107 return newProxyReader(r, b)
90 result := make(chan io.ReadCloser)
91 select {
92 case b.operateState <- func(s *bState) {
93 result <- newProxyReader(r, b, len(s.ewmaDecorators) != 0)
94 }:
95 return <-result
96 case <-b.ctx.Done():
97 return nil
98 }
99 }
100
101 // ProxyWriter wraps io.Writer with metrics required for progress tracking.
102 // If bar is already completed or aborted, returns nil.
103 // Panics if `w` is nil.
104 func (b *Bar) ProxyWriter(w io.Writer) io.WriteCloser {
105 if w == nil {
106 panic("expected non nil io.Writer")
107 }
108 result := make(chan io.WriteCloser)
109 select {
110 case b.operateState <- func(s *bState) {
111 result <- newProxyWriter(w, b, len(s.ewmaDecorators) != 0)
112 }:
113 return <-result
114 case <-b.ctx.Done():
115 return nil
116 }
108117 }
109118
110119 // ID returs id of the bar.
113122 select {
114123 case b.operateState <- func(s *bState) { result <- s.id }:
115124 return <-result
116 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 case <-b.bsOk:
126 return b.bs.id
127 }
128 }
129
130 // Current returns bar's current value, in other words sum of all increments.
122131 func (b *Bar) Current() int64 {
123132 result := make(chan int64)
124133 select {
125134 case b.operateState <- func(s *bState) { result <- s.current }:
126135 return <-result
127 case <-b.done:
128 return b.cacheState.current
136 case <-b.bsOk:
137 return b.bs.current
129138 }
130139 }
131140
136145 func (b *Bar) SetRefill(amount int64) {
137146 select {
138147 case b.operateState <- func(s *bState) {
139 s.refill = amount
140 }:
141 case <-b.done:
142 }
143 }
144
145 // TraverseDecorators traverses all available decorators and calls cb func on each.
148 if amount < s.current {
149 s.refill = amount
150 } else {
151 s.refill = s.current
152 }
153 }:
154 case <-b.ctx.Done():
155 }
156 }
157
158 // TraverseDecorators traverses available decorators and calls cb func
159 // on each in a new goroutine. Decorators implementing decor.Wrapper
160 // interface are unwrapped first.
146161 func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
147162 select {
148163 case b.operateState <- func(s *bState) {
149 for _, decorators := range [...][]decor.Decorator{
150 s.pDecorators,
151 s.aDecorators,
152 } {
164 var wg sync.WaitGroup
165 for _, decorators := range s.decorators {
166 wg.Add(len(decorators))
153167 for _, d := range decorators {
154 cb(extractBaseDecorator(d))
168 d := d
169 go func() {
170 cb(unwrap(d))
171 wg.Done()
172 }()
155173 }
156174 }
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 {
175 wg.Wait()
176 }:
177 case <-b.ctx.Done():
178 }
179 }
180
181 // EnableTriggerComplete enables triggering complete event. It's effective
182 // only for bars which were constructed with `total <= 0`. If `curren >= total`
183 // at the moment of call, complete event is triggered right away.
184 func (b *Bar) EnableTriggerComplete() {
185 select {
186 case b.operateState <- func(s *bState) {
187 if s.triggerComplete {
188 return
189 }
190 if s.current >= s.total {
191 s.current = s.total
192 s.triggerCompletion(b)
193 } else {
194 s.triggerComplete = true
195 }
196 }:
197 case <-b.ctx.Done():
198 }
199 }
200
201 // SetTotal sets total to an arbitrary value. It's effective only for bar
202 // which was constructed with `total <= 0`. Setting total to negative value
203 // is equivalent to `(*Bar).SetTotal((*Bar).Current(), bool)` but faster.
204 // If `complete` is true complete event is triggered right away.
205 // Calling `(*Bar).EnableTriggerComplete` makes this one no operational.
206 func (b *Bar) SetTotal(total int64, complete bool) {
207 select {
208 case b.operateState <- func(s *bState) {
209 if s.triggerComplete {
210 return
211 }
212 if total < 0 {
169213 s.total = s.current
170214 } else {
171215 s.total = total
172216 }
173 if s.triggerComplete && !s.completed {
217 if complete {
174218 s.current = s.total
175 s.completed = true
176 go b.refreshTillShutdown()
177 }
178 }:
179 case <-b.done:
219 s.triggerCompletion(b)
220 }
221 }:
222 case <-b.ctx.Done():
180223 }
181224 }
182225
183226 // SetCurrent sets progress' current to an arbitrary value.
184 // Setting a negative value will cause a panic.
185227 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
228 if current < 0 {
229 return
230 }
231 select {
232 case b.operateState <- func(s *bState) {
190233 s.current = current
191234 if s.triggerComplete && s.current >= s.total {
192235 s.current = s.total
193 s.completed = true
194 go b.refreshTillShutdown()
195 }
196 }:
197 case <-b.done:
236 s.triggerCompletion(b)
237 }
238 }:
239 case <-b.ctx.Done():
198240 }
199241 }
200242
212254 func (b *Bar) IncrInt64(n int64) {
213255 select {
214256 case b.operateState <- func(s *bState) {
215 s.iterated = true
216 s.lastN = n
217257 s.current += n
218258 if s.triggerComplete && s.current >= s.total {
219259 s.current = s.total
220 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)
239 }
240 }
241
242 // DecoratorAverageAdjust adjusts all average based decorators. Call
243 // if you need to adjust start time of all average based decorators
244 // or after progress resume.
260 s.triggerCompletion(b)
261 }
262 }:
263 case <-b.ctx.Done():
264 }
265 }
266
267 // EwmaIncrement is a shorthand for b.EwmaIncrInt64(1, iterDur).
268 func (b *Bar) EwmaIncrement(iterDur time.Duration) {
269 b.EwmaIncrInt64(1, iterDur)
270 }
271
272 // EwmaIncrBy is a shorthand for b.EwmaIncrInt64(int64(n), iterDur).
273 func (b *Bar) EwmaIncrBy(n int, iterDur time.Duration) {
274 b.EwmaIncrInt64(int64(n), iterDur)
275 }
276
277 // EwmaIncrInt64 increments progress by amount of n and updates EWMA based
278 // decorators by dur of a single iteration.
279 func (b *Bar) EwmaIncrInt64(n int64, iterDur time.Duration) {
280 select {
281 case b.operateState <- func(s *bState) {
282 var wg sync.WaitGroup
283 wg.Add(len(s.ewmaDecorators))
284 for _, d := range s.ewmaDecorators {
285 d := d
286 go func() {
287 d.EwmaUpdate(n, iterDur)
288 wg.Done()
289 }()
290 }
291 s.current += n
292 if s.triggerComplete && s.current >= s.total {
293 s.current = s.total
294 s.triggerCompletion(b)
295 }
296 wg.Wait()
297 }:
298 case <-b.ctx.Done():
299 }
300 }
301
302 // EwmaSetCurrent sets progress' current to an arbitrary value and updates
303 // EWMA based decorators by dur of a single iteration.
304 func (b *Bar) EwmaSetCurrent(current int64, iterDur time.Duration) {
305 if current < 0 {
306 return
307 }
308 select {
309 case b.operateState <- func(s *bState) {
310 n := current - s.current
311 var wg sync.WaitGroup
312 wg.Add(len(s.ewmaDecorators))
313 for _, d := range s.ewmaDecorators {
314 d := d
315 go func() {
316 d.EwmaUpdate(n, iterDur)
317 wg.Done()
318 }()
319 }
320 s.current = current
321 if s.triggerComplete && s.current >= s.total {
322 s.current = s.total
323 s.triggerCompletion(b)
324 }
325 wg.Wait()
326 }:
327 case <-b.ctx.Done():
328 }
329 }
330
331 // DecoratorAverageAdjust adjusts decorators implementing decor.AverageDecorator interface.
332 // Call if there is need to set start time after decorators have been constructed.
245333 func (b *Bar) DecoratorAverageAdjust(start time.Time) {
246 select {
247 case b.operateState <- func(s *bState) {
248 for _, d := range s.averageDecorators {
334 b.TraverseDecorators(func(d decor.Decorator) {
335 if d, ok := d.(decor.AverageDecorator); ok {
249336 d.AverageAdjust(start)
250337 }
251 }:
252 case <-b.done:
253 }
338 })
254339 }
255340
256341 // SetPriority changes bar's order among multiple bars. Zero is highest
257342 // priority, i.e. bar will be on top. If you don't need to set priority
258343 // dynamically, better use BarPriority option.
259344 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.
345 b.container.UpdateBarPriority(b, priority, false)
346 }
347
348 // Abort interrupts bar's running goroutine. Abort won't be engaged
349 // if bar is already in complete state. If drop is true bar will be
350 // removed as well. To make sure that bar has been removed call
351 // `(*Bar).Wait()` method.
270352 func (b *Bar) Abort(drop bool) {
271353 select {
272 case <-b.done:
273 default:
274 if drop {
275 b.container.dropBar(b)
276 }
277 b.cancel()
354 case b.operateState <- func(s *bState) {
355 if s.aborted || s.completed() {
356 return
357 }
358 s.aborted = true
359 s.rmOnComplete = drop
360 s.triggerCompletion(b)
361 }:
362 case <-b.ctx.Done():
363 }
364 }
365
366 // Aborted reports whether the bar is in aborted state.
367 func (b *Bar) Aborted() bool {
368 result := make(chan bool)
369 select {
370 case b.operateState <- func(s *bState) { result <- s.aborted }:
371 return <-result
372 case <-b.bsOk:
373 return b.bs.aborted
278374 }
279375 }
280376
281377 // Completed reports whether the bar is in completed state.
282378 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:
379 result := make(chan bool)
380 select {
381 case b.operateState <- func(s *bState) { result <- s.completed() }:
382 return <-result
383 case <-b.bsOk:
384 return b.bs.completed()
385 }
386 }
387
388 // IsRunning reports whether the bar is in running state.
389 func (b *Bar) IsRunning() bool {
390 select {
391 case <-b.ctx.Done():
392 return false
393 default:
287394 return true
288395 }
289396 }
290397
291 func (b *Bar) serve(ctx context.Context, s *bState) {
292 defer b.container.bwg.Done()
398 // Wait blocks until bar is completed or aborted.
399 func (b *Bar) Wait() {
400 <-b.bsOk
401 }
402
403 func (b *Bar) serve(bs *bState) {
404 decoratorsOnShutdown := func(decorators []decor.Decorator) {
405 for _, d := range decorators {
406 if d, ok := unwrap(d).(decor.ShutdownListener); ok {
407 b.container.bwg.Add(1)
408 go func() {
409 d.OnShutdown()
410 b.container.bwg.Done()
411 }()
412 }
413 }
414 }
293415 for {
294416 select {
295417 case op := <-b.operateState:
296 op(s)
297 case <-ctx.Done():
298 b.cacheState = s
299 close(b.done)
300 // Notifying decorators about shutdown event
301 for _, sl := range s.shutdownListeners {
302 sl.Shutdown()
418 op(bs)
419 case <-b.ctx.Done():
420 decoratorsOnShutdown(bs.decorators[0])
421 decoratorsOnShutdown(bs.decorators[1])
422 // bar can be aborted by canceling parent ctx without calling b.Abort
423 bs.aborted = !bs.completed()
424 b.bs = bs
425 close(b.bsOk)
426 b.container.bwg.Done()
427 return
428 }
429 }
430 }
431
432 func (b *Bar) render(tw int) {
433 fn := func(s *bState) {
434 frame := new(renderFrame)
435 stat := s.newStatistics(tw)
436 r, err := s.draw(stat)
437 if err != nil {
438 for _, buf := range s.buffers {
439 buf.Reset()
303440 }
441 frame.err = err
442 b.frameCh <- frame
304443 return
305444 }
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)
445 frame.rows, frame.err = s.extender(stat, r)
446 if s.aborted || s.completed() {
447 frame.shutdown = s.shutdown
448 frame.rmOnComplete = s.rmOnComplete
449 frame.noPop = s.noPop
450 // post increment makes sure OnComplete decorators are rendered
451 s.shutdown++
452 }
453 b.frameCh <- frame
454 }
455 select {
456 case b.operateState <- fn:
457 case <-b.bsOk:
458 fn(b.bs)
459 }
460 }
461
462 func (b *Bar) tryEarlyRefresh(renderReq chan<- time.Time) {
463 var otherRunning int
464 b.container.traverseBars(func(bar *Bar) bool {
465 if b != bar && bar.IsRunning() {
466 otherRunning++
467 return false // stop traverse
468 }
469 return true // continue traverse
470 })
471 if otherRunning == 0 {
472 for {
473 select {
474 case renderReq <- time.Now():
475 case <-b.ctx.Done():
476 return
325477 }
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 if !s.trimSpace {
478 }
479 }
480 }
481
482 func (b *Bar) wSyncTable() syncTable {
483 result := make(chan syncTable)
484 select {
485 case b.operateState <- func(s *bState) { result <- s.wSyncTable() }:
486 return <-result
487 case <-b.bsOk:
488 return b.bs.wSyncTable()
489 }
490 }
491
492 func (s *bState) draw(stat decor.Statistics) (_ io.Reader, err error) {
493 decorFiller := func(buf *bytes.Buffer, decorators []decor.Decorator) (err error) {
494 for _, d := range decorators {
495 // need to call Decor in any case becase of width synchronization
496 str, width := d.Decor(stat)
497 if err != nil {
498 continue
499 }
500 if w := stat.AvailableWidth - width; w >= 0 {
501 _, err = buf.WriteString(str)
502 stat.AvailableWidth = w
503 } else if stat.AvailableWidth > 0 {
504 trunc := runewidth.Truncate(stripansi.Strip(str), stat.AvailableWidth, "…")
505 _, err = buf.WriteString(trunc)
506 stat.AvailableWidth = 0
507 }
508 }
509 return err
510 }
511
512 for i, buf := range s.buffers[:2] {
513 err = decorFiller(buf, s.decorators[i])
514 if err != nil {
515 return nil, err
516 }
517 }
518
519 spaces := []io.Reader{
520 strings.NewReader(" "),
521 strings.NewReader(" "),
522 }
523 if s.trimSpace || stat.AvailableWidth < 2 {
524 for _, r := range spaces {
525 _, _ = io.Copy(io.Discard, r)
526 }
527 } else {
393528 stat.AvailableWidth -= 2
394 s.bufB.WriteByte(' ')
395 defer s.bufB.WriteByte(' ')
396 }
397
398 nlr := strings.NewReader("\n")
399 tw := stat.AvailableWidth
400 for _, d := range s.pDecorators {
401 str := d.Decor(stat)
402 stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
403 s.bufP.WriteString(str)
404 }
405 if stat.AvailableWidth <= 0 {
406 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufP.String()), tw, "…"))
407 s.bufP.Reset()
408 return io.MultiReader(trunc, s.bufB, nlr)
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 <= 0 {
418 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufA.String()), tw, "…"))
419 s.bufA.Reset()
420 return io.MultiReader(s.bufP, s.bufB, trunc, nlr)
421 }
422
423 s.filler.Fill(s.bufB, s.reqWidth, stat)
424
425 return io.MultiReader(s.bufP, s.bufB, s.bufA, nlr)
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]
529 }
530
531 err = s.filler.Fill(s.buffers[2], stat)
532 if err != nil {
533 return nil, err
534 }
535
536 return io.MultiReader(
537 s.buffers[0],
538 spaces[0],
539 s.buffers[2],
540 spaces[1],
541 s.buffers[1],
542 strings.NewReader("\n"),
543 ), nil
544 }
545
546 func (s *bState) wSyncTable() (table syncTable) {
547 var count int
548 var row []chan int
549
550 for i, decorators := range s.decorators {
551 for _, d := range decorators {
552 if ch, ok := d.Sync(); ok {
553 row = append(row, ch)
554 count++
555 }
556 }
557 switch i {
558 case 0:
559 table[i] = row[0:count]
560 default:
561 table[i] = row[len(table[i-1]):count]
562 }
563 }
447564 return table
448565 }
449566
450 func newStatistics(tw int, s *bState) decor.Statistics {
567 func (s *bState) populateEwmaDecorators(decorators []decor.Decorator) {
568 for _, d := range decorators {
569 if d, ok := unwrap(d).(decor.EwmaDecorator); ok {
570 s.ewmaDecorators = append(s.ewmaDecorators, d)
571 }
572 }
573 }
574
575 func (s *bState) triggerCompletion(b *Bar) {
576 s.triggerComplete = true
577 if s.autoRefresh {
578 // Technically this call isn't required, but if refresh rate is set to
579 // one hour for example and bar completes within a few minutes p.Wait()
580 // will wait for one hour. This call helps to avoid unnecessary waiting.
581 go b.tryEarlyRefresh(s.renderReq)
582 } else {
583 b.cancel()
584 }
585 }
586
587 func (s bState) completed() bool {
588 return s.triggerComplete && s.current == s.total
589 }
590
591 func (s bState) newStatistics(tw int) decor.Statistics {
451592 return decor.Statistics{
593 AvailableWidth: tw,
594 RequestedWidth: s.reqWidth,
452595 ID: s.id,
453 AvailableWidth: tw,
454596 Total: s.total,
455597 Current: s.current,
456598 Refill: s.refill,
457 Completed: s.completeFlushed,
458 }
459 }
460
461 func extractBaseDecorator(d decor.Decorator) decor.Decorator {
599 Completed: s.completed(),
600 Aborted: s.aborted,
601 }
602 }
603
604 func unwrap(d decor.Decorator) decor.Decorator {
462605 if d, ok := d.(decor.Wrapper); ok {
463 return extractBaseDecorator(d.Base())
606 return unwrap(d.Unwrap())
464607 }
465608 return d
466609 }
467
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 }
22 import (
33 "io"
44
5 "github.com/vbauerster/mpb/v6/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(style string) BarFiller
17 // func NewBarFillerRev(style string) BarFiller
18 // func NewBarFillerPick(style string, rev bool) BarFiller
19 // func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller
20 //
2110 type BarFiller interface {
22 Fill(w io.Writer, reqWidth int, stat decor.Statistics)
11 Fill(io.Writer, decor.Statistics) error
2312 }
2413
25 // BarFillerFunc is function type adapter to convert function into BarFiller.
26 type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
14 // BarFillerBuilder interface.
15 // Default implementations are:
16 //
17 // BarStyle()
18 // SpinnerStyle()
19 // NopStyle()
20 type BarFillerBuilder interface {
21 Build() BarFiller
22 }
2723
28 func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
29 f(w, reqWidth, stat)
24 // BarFillerFunc is function type adapter to convert compatible function
25 // into BarFiller interface.
26 type BarFillerFunc func(io.Writer, decor.Statistics) error
27
28 func (f BarFillerFunc) Fill(w io.Writer, stat decor.Statistics) error {
29 return f(w, stat)
3030 }
00 package mpb
11
22 import (
3 "bytes"
43 "io"
5 "unicode/utf8"
64
75 "github.com/mattn/go-runewidth"
8 "github.com/rivo/uniseg"
9 "github.com/vbauerster/mpb/v6/decor"
10 "github.com/vbauerster/mpb/v6/internal"
6 "github.com/vbauerster/mpb/v8/decor"
7 "github.com/vbauerster/mpb/v8/internal"
118 )
129
1310 const (
14 rLeft = iota
15 rFill
16 rTip
17 rSpace
18 rRight
19 rRevTip
20 rRefill
11 iLbound = iota
12 iRbound
13 iRefiller
14 iFiller
15 iTip
16 iPadding
17 components
2118 )
2219
23 // BarDefaultStyle is a style for rendering a progress bar.
24 // It consist of 7 ordered runes:
25 //
26 // '1st rune' stands for left boundary rune
27 //
28 // '2nd rune' stands for fill rune
29 //
30 // '3rd rune' stands for tip rune
31 //
32 // '4th rune' stands for space rune
33 //
34 // '5th rune' stands for right boundary rune
35 //
36 // '6th rune' stands for reverse tip rune
37 //
38 // '7th rune' stands for refill rune
39 //
40 const BarDefaultStyle string = "[=>-]<+"
41
42 type barFiller struct {
43 format [][]byte
44 rwidth []int
45 tip []byte
46 refill int64
47 reverse bool
48 flush func(io.Writer, *space, [][]byte)
49 }
50
51 type space struct {
52 space []byte
53 rwidth int
54 count int
55 }
56
57 // NewBarFiller returns a BarFiller implementation which renders a
58 // progress bar in regular direction. If style is empty string,
59 // BarDefaultStyle is applied. To be used with `*Progress.Add(...)
60 // *Bar` method.
61 func NewBarFiller(style string) BarFiller {
62 return newBarFiller(style, false)
63 }
64
65 // NewBarFillerRev returns a BarFiller implementation which renders a
66 // progress bar in reverse direction. If style is empty string,
67 // BarDefaultStyle is applied. To be used with `*Progress.Add(...)
68 // *Bar` method.
69 func NewBarFillerRev(style string) BarFiller {
70 return newBarFiller(style, true)
71 }
72
73 // NewBarFillerPick pick between regular and reverse BarFiller implementation
74 // based on rev param. To be used with `*Progress.Add(...) *Bar` method.
75 func NewBarFillerPick(style string, rev bool) BarFiller {
76 return newBarFiller(style, rev)
77 }
78
79 func newBarFiller(style string, rev bool) BarFiller {
80 bf := &barFiller{
81 format: make([][]byte, len(BarDefaultStyle)),
82 rwidth: make([]int, len(BarDefaultStyle)),
83 reverse: rev,
84 }
85 bf.parse(BarDefaultStyle)
86 if style != "" && style != BarDefaultStyle {
87 bf.parse(style)
20 var defaultBarStyle = [components]string{"[", "]", "+", "=", ">", "-"}
21
22 // BarStyleComposer interface.
23 type BarStyleComposer interface {
24 BarFillerBuilder
25 Lbound(string) BarStyleComposer
26 LboundMeta(func(string) string) BarStyleComposer
27 Rbound(string) BarStyleComposer
28 RboundMeta(func(string) string) BarStyleComposer
29 Filler(string) BarStyleComposer
30 FillerMeta(func(string) string) BarStyleComposer
31 Refiller(string) BarStyleComposer
32 RefillerMeta(func(string) string) BarStyleComposer
33 Padding(string) BarStyleComposer
34 PaddingMeta(func(string) string) BarStyleComposer
35 Tip(frames ...string) BarStyleComposer
36 TipMeta(func(string) string) BarStyleComposer
37 TipOnComplete() BarStyleComposer
38 Reverse() BarStyleComposer
39 }
40
41 type component struct {
42 width int
43 bytes []byte
44 }
45
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 tip struct {
56 onComplete bool
57 count uint
58 frames []component
59 }
60 }
61
62 type barStyle struct {
63 style [components]string
64 metaFuncs [components]func(io.Writer, []byte) error
65 tipFrames []string
66 tipOnComplete bool
67 rev bool
68 }
69
70 // BarStyle constructs default bar style which can be altered via
71 // BarStyleComposer interface.
72 func BarStyle() 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 {
151 s.rev = true
152 return s
153 }
154
155 func (s barStyle) Build() BarFiller {
156 bf := &bFiller{
157 meta: s.metaFuncs,
158 }
159 bf.components[iLbound] = component{
160 width: runewidth.StringWidth(s.style[iLbound]),
161 bytes: []byte(s.style[iLbound]),
162 }
163 bf.components[iRbound] = component{
164 width: runewidth.StringWidth(s.style[iRbound]),
165 bytes: []byte(s.style[iRbound]),
166 }
167 bf.components[iFiller] = component{
168 width: runewidth.StringWidth(s.style[iFiller]),
169 bytes: []byte(s.style[iFiller]),
170 }
171 bf.components[iRefiller] = component{
172 width: runewidth.StringWidth(s.style[iRefiller]),
173 bytes: []byte(s.style[iRefiller]),
174 }
175 bf.components[iPadding] = component{
176 width: runewidth.StringWidth(s.style[iPadding]),
177 bytes: []byte(s.style[iPadding]),
178 }
179 bf.tip.onComplete = s.tipOnComplete
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 }
187 if s.rev {
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
198 }
199 } else {
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
210 }
88211 }
89212 return bf
90213 }
91214
92 func (s *barFiller) parse(style string) {
93 if !utf8.ValidString(style) {
94 panic("invalid bar style")
95 }
96 srcFormat := make([][]byte, len(BarDefaultStyle))
97 srcRwidth := make([]int, len(BarDefaultStyle))
98 i := 0
99 for gr := uniseg.NewGraphemes(style); i < len(BarDefaultStyle) && gr.Next(); i++ {
100 srcFormat[i] = gr.Bytes()
101 srcRwidth[i] = runewidth.StringWidth(gr.Str())
102 }
103 copy(s.format, srcFormat[:i])
104 copy(s.rwidth, srcRwidth[:i])
105 if s.reverse {
106 s.tip = s.format[rRevTip]
107 s.flush = reverseFlush
108 } else {
109 s.tip = s.format[rTip]
110 s.flush = regularFlush
111 }
112 }
113
114 func (s *barFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
115 width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth)
116 brackets := s.rwidth[rLeft] + s.rwidth[rRight]
117 if width < brackets {
118 return
119 }
215 func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) error {
216 width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
120217 // don't count brackets as progress
121 width -= brackets
122
123 w.Write(s.format[rLeft])
124 defer w.Write(s.format[rRight])
125
126 cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width))
127 space := &space{
128 space: s.format[rSpace],
129 rwidth: s.rwidth[rSpace],
130 count: width - cwidth,
131 }
132
133 index, refill := 0, 0
134 bb := make([][]byte, cwidth)
135
136 if cwidth > 0 && cwidth != width {
137 bb[index] = s.tip
138 cwidth -= s.rwidth[rTip]
139 index++
140 }
141
142 if stat.Refill > 0 {
143 refill = int(internal.PercentageRound(stat.Total, int64(stat.Refill), width))
144 if refill > cwidth {
145 refill = cwidth
146 }
147 cwidth -= refill
148 }
149
150 for cwidth > 0 {
151 bb[index] = s.format[rFill]
152 cwidth -= s.rwidth[rFill]
153 index++
154 }
155
156 for refill > 0 {
157 bb[index] = s.format[rRefill]
158 refill -= s.rwidth[rRefill]
159 index++
160 }
161
162 if cwidth+refill < 0 || space.rwidth > 1 {
163 buf := new(bytes.Buffer)
164 s.flush(buf, space, bb[:index])
165 io.WriteString(w, runewidth.Truncate(buf.String(), width, "…"))
166 return
167 }
168
169 s.flush(w, space, bb)
170 }
171
172 func regularFlush(w io.Writer, space *space, bb [][]byte) {
173 for i := len(bb) - 1; i >= 0; i-- {
174 w.Write(bb[i])
175 }
176 for space.count > 0 {
177 w.Write(space.space)
178 space.count -= space.rwidth
179 }
180 }
181
182 func reverseFlush(w io.Writer, space *space, bb [][]byte) {
183 for space.count > 0 {
184 w.Write(space.space)
185 space.count -= space.rwidth
186 }
187 for i := 0; i < len(bb); i++ {
188 w.Write(bb[i])
189 }
190 }
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.tip.onComplete {
239 tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
240 s.tip.count++
241 fillCount += tip.width
242 }
243 switch refWidth := 0; {
244 case stat.Refill != 0:
245 refWidth = int(internal.PercentageRound(stat.Total, stat.Refill, uint(width)))
246 curWidth -= refWidth
247 refWidth += curWidth
248 fallthrough
249 default:
250 for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w {
251 filling = append(filling, s.components[iFiller].bytes...)
252 }
253 for w := s.components[iRefiller].width; refWidth-fillCount >= w; fillCount += w {
254 refilling = append(refilling, s.components[iRefiller].bytes...)
255 }
256 }
257 }
258
259 for w := s.components[iPadding].width; width-fillCount >= w; fillCount += w {
260 padding = append(padding, s.components[iPadding].bytes...)
261 }
262
263 for w := 1; width-fillCount >= w; fillCount += w {
264 padding = append(padding, "…"...)
265 }
266
267 err = s.flush(w,
268 flushSection{s.meta[iRefiller], refilling},
269 flushSection{s.meta[iFiller], filling},
270 flushSection{s.meta[iTip], tip.bytes},
271 flushSection{s.meta[iPadding], padding},
272 )
273 if err != nil {
274 return err
275 }
276 return s.meta[iRbound](w, s.components[iRbound].bytes)
277 }
278
279 func makeMetaFunc(fn func(string) string) func(io.Writer, []byte) error {
280 return func(w io.Writer, p []byte) (err error) {
281 _, err = io.WriteString(w, fn(string(p)))
282 return err
283 }
284 }
285
286 func defaultMeta(w io.Writer, p []byte) (err error) {
287 _, err = w.Write(p)
288 return err
289 }
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 }
44 "strings"
55
66 "github.com/mattn/go-runewidth"
7 "github.com/vbauerster/mpb/v6/decor"
8 "github.com/vbauerster/mpb/v6/internal"
7 "github.com/vbauerster/mpb/v8/decor"
8 "github.com/vbauerster/mpb/v8/internal"
99 )
1010
11 // SpinnerAlignment enum.
12 type SpinnerAlignment int
13
14 // SpinnerAlignment kinds.
1511 const (
16 SpinnerOnLeft SpinnerAlignment = iota
17 SpinnerOnMiddle
18 SpinnerOnRight
12 positionLeft = 1 + iota
13 positionRight
1914 )
2015
21 // SpinnerDefaultStyle is a style for rendering a spinner.
22 var SpinnerDefaultStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
16 var defaultSpinnerStyle = [...]string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
2317
24 type spinnerFiller struct {
25 frames []string
26 count uint
27 alignment SpinnerAlignment
18 // SpinnerStyleComposer interface.
19 type SpinnerStyleComposer interface {
20 BarFillerBuilder
21 PositionLeft() SpinnerStyleComposer
22 PositionRight() SpinnerStyleComposer
23 Meta(func(string) string) SpinnerStyleComposer
2824 }
2925
30 // NewSpinnerFiller returns a BarFiller implementation which renders
31 // a spinner. If style is nil or zero length, SpinnerDefaultStyle is
32 // applied. To be used with `*Progress.Add(...) *Bar` method.
33 func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller {
34 if len(style) == 0 {
35 style = SpinnerDefaultStyle
36 }
37 filler := &spinnerFiller{
38 frames: style,
39 alignment: alignment,
40 }
41 return filler
26 type sFiller struct {
27 frames []string
28 count uint
29 meta func(string) string
30 position func(string, int) string
4231 }
4332
44 func (s *spinnerFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
45 width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth)
33 type spinnerStyle struct {
34 position uint
35 frames []string
36 meta func(string) string
37 }
4638
39 // SpinnerStyle constructs default spinner style which can be altered via
40 // SpinnerStyleComposer interface.
41 func SpinnerStyle(frames ...string) SpinnerStyleComposer {
42 ss := spinnerStyle{
43 meta: func(s string) string { return s },
44 }
45 if len(frames) != 0 {
46 ss.frames = frames
47 } else {
48 ss.frames = defaultSpinnerStyle[:]
49 }
50 return ss
51 }
52
53 func (s spinnerStyle) PositionLeft() SpinnerStyleComposer {
54 s.position = positionLeft
55 return s
56 }
57
58 func (s spinnerStyle) PositionRight() SpinnerStyleComposer {
59 s.position = positionRight
60 return s
61 }
62
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 {
69 sf := &sFiller{
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 }
86 }
87 return sf
88 }
89
90 func (s *sFiller) Fill(w io.Writer, stat decor.Statistics) error {
91 width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
4792 frame := s.frames[s.count%uint(len(s.frames))]
4893 frameWidth := runewidth.StringWidth(frame)
94 s.count++
4995
5096 if width < frameWidth {
51 return
97 return nil
5298 }
5399
54 switch rest := width - frameWidth; s.alignment {
55 case SpinnerOnLeft:
56 io.WriteString(w, frame+strings.Repeat(" ", rest))
57 case SpinnerOnMiddle:
58 str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
59 io.WriteString(w, str)
60 case SpinnerOnRight:
61 io.WriteString(w, strings.Repeat(" ", rest)+frame)
62 }
63 s.count++
100 _, err := io.WriteString(w, s.position(s.meta(frame), width-frameWidth))
101 return err
64102 }
33 "bytes"
44 "io"
55
6 "github.com/vbauerster/mpb/v6/decor"
7 "github.com/vbauerster/mpb/v6/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)
14 if decorator == nil {
15 continue
16 }
17 dest = append(dest, decorator)
18 }
19 return
20 }
21
22 // PrependDecorators let you inject decorators to the bar's left side.
23 func PrependDecorators(decorators ...decor.Decorator) BarOption {
24 decorators = inspect(decorators)
25 return func(s *bState) {
26 s.populateEwmaDecorators(decorators)
27 s.decorators[0] = decorators
2228 }
2329 }
2430
2531 // AppendDecorators let you inject decorators to the bar's right side.
2632 func AppendDecorators(decorators ...decor.Decorator) BarOption {
27 return func(s *bState) {
28 s.addDecorators(&s.aDecorators, decorators...)
29 }
30 }
31
32 // PrependDecorators let you inject decorators to the bar's left side.
33 func PrependDecorators(decorators ...decor.Decorator) BarOption {
34 return func(s *bState) {
35 s.addDecorators(&s.pDecorators, decorators...)
33 decorators = inspect(decorators)
34 return func(s *bState) {
35 s.populateEwmaDecorators(decorators)
36 s.decorators[1] = decorators
3637 }
3738 }
3839
5051 }
5152 }
5253
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
54 // BarQueueAfter puts this (being constructed) bar into the queue.
55 // BarPriority will be inherited from the argument bar.
56 // When argument bar completes or aborts queued bar replaces its place.
57 func BarQueueAfter(bar *Bar) BarOption {
58 return func(s *bState) {
59 s.waitBar = bar
6160 }
6261 }
6362
6564 // on complete event.
6665 func BarRemoveOnComplete() BarOption {
6766 return func(s *bState) {
68 s.dropOnComplete = true
67 s.rmOnComplete = true
6968 }
7069 }
7170
7877 // BarFillerOnComplete replaces bar's filler with message, on complete event.
7978 func BarFillerOnComplete(message string) BarOption {
8079 return BarFillerMiddleware(func(base BarFiller) BarFiller {
81 return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
80 return BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
8281 if st.Completed {
83 io.WriteString(w, message)
84 } else {
85 base.Fill(w, reqWidth, st)
82 _, err := io.WriteString(w, message)
83 return err
8684 }
85 return base.Fill(w, st)
8786 })
8887 })
8988 }
9089
9190 // BarFillerMiddleware provides a way to augment the underlying BarFiller.
9291 func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption {
93 return func(s *bState) {
94 s.middleware = middle
92 if middle == nil {
93 return nil
94 }
95 return func(s *bState) {
96 s.filler = middle(s.filler)
9597 }
9698 }
9799
98100 // 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.
101 // will be on top. This option isn't effective with `BarQueueAfter` option.
101102 func BarPriority(priority int) BarOption {
102103 return func(s *bState) {
103104 s.priority = priority
104105 }
105106 }
106107
107 // BarExtender provides a way to extend bar to the next new line.
108 func BarExtender(filler BarFiller) BarOption {
108 // BarExtender extends bar with arbitrary lines. Provided BarFiller will be
109 // called at each render/flush cycle. Any lines written to the underlying
110 // io.Writer will extend the bar either in above (rev = true) or below
111 // (rev = false) direction.
112 func BarExtender(filler BarFiller, rev bool) BarOption {
109113 if filler == nil {
110114 return nil
111115 }
112 return func(s *bState) {
113 s.extender = makeExtenderFunc(filler)
114 }
115 }
116
117 func makeExtenderFunc(filler BarFiller) extenderFunc {
116 if f, ok := filler.(BarFillerFunc); ok && f == nil {
117 return nil
118 }
119 fn := makeExtenderFunc(filler, rev)
120 return func(s *bState) {
121 s.extender = fn
122 }
123 }
124
125 func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc {
118126 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"))
127 base := func(stat decor.Statistics, rows ...io.Reader) ([]io.Reader, error) {
128 err := filler.Fill(buf, stat)
129 if err != nil {
130 buf.Reset()
131 return rows, err
132 }
133 for {
134 line, err := buf.ReadBytes('\n')
135 if err != nil {
136 buf.Reset()
137 break
138 }
139 rows = append(rows, bytes.NewReader(line))
140 }
141 return rows, err
142 }
143 if !rev {
144 return base
145 }
146 return func(stat decor.Statistics, rows ...io.Reader) ([]io.Reader, error) {
147 rows, err := base(stat, rows...)
148 if err != nil {
149 return rows, err
150 }
151 for left, right := 0, len(rows)-1; left < right; left, right = left+1, right-1 {
152 rows[left], rows[right] = rows[right], rows[left]
153 }
154 return rows, err
122155 }
123156 }
124157
137170 }
138171 }
139172
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.
173 // BarOptional will return provided option only when cond is true.
174 func BarOptional(option BarOption, cond bool) BarOption {
175 if cond {
176 return option
177 }
178 return nil
179 }
180
181 // BarOptOn will return provided option only when predicate evaluates to true.
147182 func BarOptOn(option BarOption, predicate func() bool) BarOption {
148183 if predicate() {
149184 return option
150185 }
151186 return nil
152187 }
188
189 // BarFuncOptional will call option and return its value only when cond is true.
190 func BarFuncOptional(option func() BarOption, cond bool) BarOption {
191 if cond {
192 return option()
193 }
194 return nil
195 }
196
197 // BarFuncOptOn will call option and return its value only when predicate evaluates to true.
198 func BarFuncOptOn(option func() BarOption, predicate func() bool) BarOption {
199 if predicate() {
200 return option()
201 }
202 return nil
203 }
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/v6"
13 "github.com/vbauerster/mpb/v6/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.Error("expected bar not to complete")
23 }
24
25 bar.IncrBy(total)
26
27 if !bar.Completed() {
28 t.Error("expected bar to complete")
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.Error("expected bar not to be aborted")
41 }
42
43 bar.Abort(false)
44
45 if !bar.Aborted() {
46 t.Error("expected bar to be aborted")
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 TestBarEnableTriggerCompleteZeroBar(t *testing.T) {
70 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
71 bar := p.AddBar(0) // never complete bar
72
73 if bar.Completed() {
74 t.Error("expected bar not to complete")
75 }
76
77 // Calling bar.SetTotal(0, true) has same effect
78 // but this one is more concise and intuitive
79 bar.EnableTriggerComplete()
80
81 if !bar.Completed() {
82 t.Error("expected bar to complete")
83 }
84
85 p.Wait()
86 }
87
88 func TestBarEnableTriggerCompleteAndIncrementBefore(t *testing.T) {
89 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
90 bar := p.AddBar(0) // never complete bar
91
92 targetTotal := int64(80)
93
94 for _, f := range []func(){
95 func() { bar.SetTotal(40, false) },
96 func() { bar.IncrBy(60) },
97 func() { bar.SetTotal(targetTotal, false) },
98 func() { bar.IncrBy(20) },
99 } {
100 f()
101 if bar.Completed() {
102 t.Error("expected bar not to complete")
103 }
104 }
105
106 bar.EnableTriggerComplete()
107
108 if !bar.Completed() {
109 t.Error("expected bar to complete")
110 }
111
112 if current := bar.Current(); current != targetTotal {
113 t.Errorf("Expected current: %d, got: %d", targetTotal, current)
114 }
115
116 p.Wait()
117 }
118
119 func TestBarEnableTriggerCompleteAndIncrementAfter(t *testing.T) {
120 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
121 bar := p.AddBar(0) // never complete bar
122
123 targetTotal := int64(80)
124
125 for _, f := range []func(){
126 func() { bar.SetTotal(40, false) },
127 func() { bar.IncrBy(60) },
128 func() { bar.SetTotal(targetTotal, false) },
129 func() { bar.EnableTriggerComplete() }, // disables any next SetTotal
130 func() { bar.SetTotal(100, true) }, // nop
131 } {
132 f()
133 if bar.Completed() {
134 t.Error("expected bar not to complete")
135 }
136 }
137
138 bar.IncrBy(20)
139
140 if !bar.Completed() {
141 t.Error("expected bar to complete")
142 }
143
144 if current := bar.Current(); current != targetTotal {
145 t.Errorf("Expected current: %d, got: %d", targetTotal, current)
146 }
147
148 p.Wait()
32149 }
33150
34151 func TestBarID(t *testing.T) {
35 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(ioutil.Discard))
152 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
36153 total := 100
37154 wantID := 11
38155 bar := p.AddBar(int64(total), mpb.BarID(wantID))
39156
40 go func() {
41 for i := 0; i < total; i++ {
42 time.Sleep(50 * time.Millisecond)
43 bar.Increment()
44 }
45 }()
46
47157 gotID := bar.ID()
48158 if gotID != wantID {
49 t.Errorf("Expected bar id: %d, got %d\n", wantID, gotID)
50 }
51
52 bar.Abort(true)
159 t.Errorf("Expected bar id: %d, got %d", wantID, gotID)
160 }
161
162 bar.IncrBy(total)
163
53164 p.Wait()
54165 }
55166
56167 func TestBarSetRefill(t *testing.T) {
57168 var buf bytes.Buffer
58
59 p := mpb.New(mpb.WithOutput(&buf), mpb.WithWidth(100))
169 p := mpb.New(
170 mpb.WithWidth(100),
171 mpb.WithOutput(&buf),
172 mpb.WithAutoRefresh(),
173 )
60174
61175 total := 100
62176 till := 30
63 refillRune, _ := utf8.DecodeLastRuneInString(mpb.BarDefaultStyle)
64
65 bar := p.AddBar(int64(total), mpb.BarFillerTrim())
66
177 refiller := "+"
178
179 bar := p.New(int64(total), mpb.BarStyle().Refiller(refiller), mpb.BarFillerTrim())
180
181 bar.IncrBy(till)
67182 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 }
183 bar.IncrBy(total - till)
74184
75185 p.Wait()
76186
77187 wantBar := fmt.Sprintf("[%s%s]",
78 strings.Repeat(string(refillRune), till-1),
188 strings.Repeat(refiller, till-1),
79189 strings.Repeat("=", total-till-1),
80190 )
81191
82 got := string(getLastLine(buf.Bytes()))
192 got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
83193
84194 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)
195 t.Errorf("Want bar: %q, got bar: %q", wantBar, got)
114196 }
115197 }
116198
117199 func TestBarHas100PercentWithBarRemoveOnComplete(t *testing.T) {
118200 var buf bytes.Buffer
119
120 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(&buf))
201 p := mpb.New(
202 mpb.WithWidth(80),
203 mpb.WithOutput(&buf),
204 mpb.WithAutoRefresh(),
205 )
121206
122207 total := 50
123208
126211 mpb.AppendDecorators(decor.Percentage()),
127212 )
128213
129 for i := 0; i < total; i++ {
130 bar.Increment()
131 time.Sleep(10 * time.Millisecond)
132 }
214 bar.IncrBy(total)
133215
134216 p.Wait()
135217
136218 hundred := "100 %"
137219 if !bytes.Contains(buf.Bytes(), []byte(hundred)) {
138 t.Errorf("Bar's buffer does not contain: %q\n", hundred)
220 t.Errorf("Bar's buffer does not contain: %q", hundred)
139221 }
140222 }
141223
142224 func TestBarStyle(t *testing.T) {
143225 var buf bytes.Buffer
144226 customFormat := "╢▌▌░╟"
227 runes := []rune(customFormat)
145228 total := 80
146 p := mpb.New(mpb.WithWidth(total), mpb.WithOutput(&buf))
147 bar := p.Add(int64(total), mpb.NewBarFiller(customFormat), mpb.BarFillerTrim())
148
149 for i := 0; i < total; i++ {
150 bar.Increment()
151 time.Sleep(10 * time.Millisecond)
152 }
153
154 p.Wait()
155
156 runes := []rune(customFormat)
157 wantBar := fmt.Sprintf("%s%s%s",
158 string(runes[0]),
159 strings.Repeat(string(runes[1]), total-2),
160 string(runes[len(runes)-1]),
161 )
162 got := string(getLastLine(buf.Bytes()))
163
164 if !strings.Contains(got, wantBar) {
165 t.Errorf("Want bar: %q:%d, got bar: %q:%d\n", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got))
166 }
167 }
168
169 func TestBarPanicBeforeComplete(t *testing.T) {
170 var buf bytes.Buffer
171229 p := mpb.New(
172230 mpb.WithWidth(80),
173 mpb.WithDebugOutput(&buf),
174 mpb.WithOutput(ioutil.Discard),
175 )
176
177 total := 100
178 panicMsg := "Upps!!!"
179 var pCount uint32
180 bar := p.AddBar(int64(total),
181 mpb.PrependDecorators(panicDecorator(panicMsg,
182 func(st decor.Statistics) bool {
183 if st.Current >= 42 {
184 atomic.AddUint32(&pCount, 1)
185 return true
186 }
187 return false
188 },
189 )),
190 )
191
192 for i := 0; i < total; i++ {
231 mpb.WithOutput(&buf),
232 mpb.WithAutoRefresh(),
233 )
234 bs := mpb.BarStyle()
235 bs = bs.Lbound(string(runes[0]))
236 bs = bs.Filler(string(runes[1]))
237 bs = bs.Tip(string(runes[2]))
238 bs = bs.Padding(string(runes[3]))
239 bs = bs.Rbound(string(runes[4]))
240 bar := p.New(int64(total), bs, mpb.BarFillerTrim())
241
242 bar.IncrBy(total)
243
244 p.Wait()
245
246 wantBar := fmt.Sprintf("%s%s%s%s",
247 string(runes[0]),
248 strings.Repeat(string(runes[1]), total-3),
249 string(runes[2]),
250 string(runes[4]),
251 )
252 got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
253
254 if !strings.Contains(got, wantBar) {
255 t.Errorf("Want bar: %q:%d, got bar: %q:%d", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got))
256 }
257 }
258
259 func TestDecorStatisticsAvailableWidth(t *testing.T) {
260 ch := make(chan int, 2)
261 td1 := func(s decor.Statistics) string {
262 ch <- s.AvailableWidth
263 return strings.Repeat("0", 20)
264 }
265 td2 := func(s decor.Statistics) string {
266 ch <- s.AvailableWidth
267 return ""
268 }
269 ctx, cancel := context.WithCancel(context.Background())
270 refresh := make(chan interface{})
271 p := mpb.NewWithContext(ctx,
272 mpb.WithWidth(100),
273 mpb.WithManualRefresh(refresh),
274 mpb.WithOutput(io.Discard),
275 )
276 _ = p.AddBar(0,
277 mpb.BarFillerTrim(),
278 mpb.PrependDecorators(
279 decor.Name(strings.Repeat("0", 20)),
280 decor.Meta(
281 decor.Any(td1),
282 func(s string) string {
283 return "\x1b[31;1m" + s + "\x1b[0m"
284 },
285 ),
286 ),
287 mpb.AppendDecorators(
288 decor.Name(strings.Repeat("0", 20)),
289 decor.Any(td2),
290 ),
291 )
292 refresh <- time.Now()
293 go func() {
193294 time.Sleep(10 * time.Millisecond)
194 bar.Increment()
195 }
196
197 p.Wait()
198
199 if pCount != 1 {
200 t.Errorf("Decor called after panic %d times\n", pCount-1)
201 }
202
203 barStr := buf.String()
204 if !strings.Contains(barStr, panicMsg) {
205 t.Errorf("%q doesn't contain %q\n", barStr, panicMsg)
206 }
207 }
208
209 func TestBarPanicAfterComplete(t *testing.T) {
210 var buf bytes.Buffer
211 p := mpb.New(
212 mpb.WithWidth(80),
213 mpb.WithDebugOutput(&buf),
214 mpb.WithOutput(ioutil.Discard),
215 )
216
217 total := 100
218 panicMsg := "Upps!!!"
219 var pCount uint32
220 bar := p.AddBar(int64(total),
221 mpb.PrependDecorators(panicDecorator(panicMsg,
222 func(st decor.Statistics) bool {
223 if st.Completed {
224 atomic.AddUint32(&pCount, 1)
225 return true
226 }
227 return false
228 },
229 )),
230 )
231
232 for i := 0; i < total; i++ {
233 time.Sleep(10 * time.Millisecond)
234 bar.Increment()
235 }
236
237 p.Wait()
238
239 if pCount > 2 {
240 t.Error("Decor called after panic more than 2 times\n")
241 }
242
243 barStr := buf.String()
244 if !strings.Contains(barStr, panicMsg) {
245 t.Errorf("%q doesn't contain %q\n", barStr, panicMsg)
246 }
247 }
248
249 func panicDecorator(panicMsg string, cond func(decor.Statistics) bool) decor.Decorator {
250 return decor.Any(func(st decor.Statistics) string {
251 if cond(st) {
252 panic(panicMsg)
253 }
254 return ""
255 })
256 }
295 cancel()
296 }()
297 p.Wait()
298
299 if availableWidth := <-ch; availableWidth != 80 {
300 t.Errorf("expected AvailableWidth %d got %d", 80, availableWidth)
301 }
302
303 if availableWidth := <-ch; availableWidth != 40 {
304 t.Errorf("expected AvailableWidth %d got %d", 40, availableWidth)
305 }
306 }
307
308 func TestBarQueueAfterBar(t *testing.T) {
309 shutdown := make(chan interface{})
310 ctx, cancel := context.WithCancel(context.Background())
311 p := mpb.NewWithContext(ctx,
312 mpb.WithOutput(io.Discard),
313 mpb.WithAutoRefresh(),
314 mpb.WithShutdownNotifier(shutdown),
315 )
316 a := p.AddBar(100)
317 b := p.AddBar(100, mpb.BarQueueAfter(a))
318 identity := map[*mpb.Bar]string{
319 a: "a",
320 b: "b",
321 }
322
323 a.IncrBy(100)
324 a.Wait()
325 cancel()
326
327 bars := (<-shutdown).([]*mpb.Bar)
328 if l := len(bars); l != 1 {
329 t.Errorf("Expected len of bars: %d, got: %d", 1, l)
330 }
331
332 p.Wait()
333 if bars[0] != b {
334 t.Errorf("Expected bars[0] == b, got: %s", identity[bars[0]])
335 }
336 }
0 package mpb
0 package mpb_test
11
22 import (
3 "io/ioutil"
3 "io"
44 "testing"
55
6 "github.com/vbauerster/mpb/v6/decor"
6 "github.com/vbauerster/mpb/v8"
77 )
88
9 func BenchmarkIncrSingleBar(b *testing.B) {
10 p := New(WithOutput(ioutil.Discard), WithWidth(80))
11 bar := p.AddBar(int64(b.N))
9 const total = 1000
10
11 func BenchmarkNopStyleB1(b *testing.B) {
12 bench(b, mpb.NopStyle(), false, 1)
13 }
14
15 func BenchmarkNopStyleWithAutoRefreshB1(b *testing.B) {
16 bench(b, mpb.NopStyle(), true, 1)
17 }
18
19 func BenchmarkNopStylesB2(b *testing.B) {
20 bench(b, mpb.NopStyle(), false, 2)
21 }
22
23 func BenchmarkNopStylesWithAutoRefreshB2(b *testing.B) {
24 bench(b, mpb.NopStyle(), true, 2)
25 }
26
27 func BenchmarkNopStylesB3(b *testing.B) {
28 bench(b, mpb.NopStyle(), false, 3)
29 }
30
31 func BenchmarkNopStylesWithAutoRefreshB3(b *testing.B) {
32 bench(b, mpb.NopStyle(), true, 3)
33 }
34
35 func BenchmarkBarStyleB1(b *testing.B) {
36 bench(b, mpb.BarStyle(), false, 1)
37 }
38
39 func BenchmarkBarStyleWithAutoRefreshB1(b *testing.B) {
40 bench(b, mpb.BarStyle(), true, 1)
41 }
42
43 func BenchmarkBarStylesB2(b *testing.B) {
44 bench(b, mpb.BarStyle(), false, 2)
45 }
46
47 func BenchmarkBarStylesWithAutoRefreshB2(b *testing.B) {
48 bench(b, mpb.BarStyle(), true, 2)
49 }
50
51 func BenchmarkBarStylesB3(b *testing.B) {
52 bench(b, mpb.BarStyle(), false, 3)
53 }
54
55 func BenchmarkBarStylesWithAutoRefreshB3(b *testing.B) {
56 bench(b, mpb.BarStyle(), true, 3)
57 }
58
59 func bench(b *testing.B, builder mpb.BarFillerBuilder, autoRefresh bool, n int) {
60 p := mpb.New(
61 mpb.WithWidth(100),
62 mpb.WithOutput(io.Discard),
63 mpb.ContainerOptional(mpb.WithAutoRefresh(), autoRefresh),
64 )
65 defer p.Wait()
66 b.ResetTimer()
1267 for i := 0; i < b.N; i++ {
68 var bars []*mpb.Bar
69 for j := 0; j < n; j++ {
70 bars = append(bars, p.New(total, builder))
71 switch j {
72 case n - 1:
73 complete(bars[j])
74 default:
75 go complete(bars[j])
76 }
77 }
78 for _, bar := range bars {
79 bar.Wait()
80 }
81 }
82 }
83
84 func complete(bar *mpb.Bar) {
85 for i := 0; i < total; i++ {
1386 bar.Increment()
1487 }
1588 }
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/v6/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 pop completed bars out of progress container.
96 // In this mode completed bars get moved to the top and stop
97 // participating in rendering cycle.
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 //go:build zos
1
2 package cwriter
3
4 import "golang.org/x/sys/unix"
5
6 const ioctlReadTermios = unix.TCGETS
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 (
11 // DidentRight bit specifies identation direction.
12 // |foo |b | With DidentRight
13 // | foo| b| Without DidentRight
14 DidentRight = 1 << iota
10 // DindentRight sets indentation from right to left.
11 //
12 // |foo |b | DindentRight is set
13 // | foo| b| DindentRight is not set
14 DindentRight = 1 << iota
1515
16 // DextraSpace bit adds extra space, makes sense with DSyncWidth only.
17 // When DidentRight bit set, the space will be added to the right,
18 // otherwise to the left.
16 // DextraSpace bit adds extra indentation space.
1917 DextraSpace
2018
2119 // DSyncWidth bit enables same column width synchronization.
2220 // Effective with multiple bars only.
2321 DSyncWidth
2422
25 // DSyncWidthR is shortcut for DSyncWidth|DidentRight
26 DSyncWidthR = DSyncWidth | DidentRight
23 // DSyncWidthR is shortcut for DSyncWidth|DindentRight
24 DSyncWidthR = DSyncWidth | DindentRight
2725
2826 // DSyncSpace is shortcut for DSyncWidth|DextraSpace
2927 DSyncSpace = DSyncWidth | DextraSpace
3028
31 // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DidentRight
32 DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight
29 // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DindentRight
30 DSyncSpaceR = DSyncWidth | DextraSpace | DindentRight
3331 )
3432
3533 // TimeStyle enum.
4341 ET_STYLE_MMSS
4442 )
4543
46 // Statistics consists of progress related statistics, that Decorator
47 // may need.
44 // Statistics contains fields which are necessary for implementing
45 // `decor.Decorator` and `mpb.BarFiller` interfaces.
4846 type Statistics struct {
47 AvailableWidth int // calculated width initially equal to terminal width
48 RequestedWidth int // width set by `mpb.WithWidth`
4949 ID int
50 AvailableWidth int
5150 Total int64
5251 Current int64
5352 Refill int64
5453 Completed bool
54 Aborted bool
5555 }
5656
5757 // Decorator interface.
6363 // `DecorFunc` into a `Decorator` interface by using provided
6464 // `func Any(DecorFunc, ...WC) Decorator`.
6565 type Decorator interface {
66 Configurator
6766 Synchronizer
68 Decor(Statistics) string
67 Formatter
68 Decor(Statistics) (str string, viewWidth int)
6969 }
7070
7171 // DecorFunc func type.
72 // To be used with `func Any`(DecorFunc, ...WC) Decorator`.
72 // To be used with `func Any(DecorFunc, ...WC) Decorator`.
7373 type DecorFunc func(Statistics) string
7474
7575 // Synchronizer interface.
7979 Sync() (chan int, bool)
8080 }
8181
82 // Configurator interface.
83 type Configurator interface {
84 GetConf() WC
85 SetConf(WC)
82 // Formatter interface.
83 // Format method needs to be called from within Decorator.Decor method
84 // in order to format string according to decor.WC settings.
85 // No need to implement manually as long as decor.WC is embedded.
86 type Formatter interface {
87 Format(string) (_ string, width int)
8688 }
8789
8890 // Wrapper interface.
9092 // it is necessary to implement this interface to retain functionality
9193 // of built-in Decorator.
9294 type Wrapper interface {
93 Base() Decorator
95 Unwrap() Decorator
9496 }
9597
9698 // EwmaDecorator interface.
110112 // If decorator needs to be notified once upon bar shutdown event, so
111113 // this is the right interface to implement.
112114 type ShutdownListener interface {
113 Shutdown()
115 OnShutdown()
114116 }
115117
116118 // Global convenience instances of WC with sync width bit set.
132134 wsync chan int
133135 }
134136
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
137 // Format should be called by any Decorator implementation.
138 // Returns formatted string and its view (visual) width.
139 func (wc WC) Format(str string) (string, int) {
140 width := runewidth.StringWidth(str)
141 if wc.W > width {
142 width = wc.W
143 } else if (wc.C & DextraSpace) != 0 {
144 width++
145 }
141146 if (wc.C & DSyncWidth) != 0 {
142 cellCount := stripWidth
143 if (wc.C & DextraSpace) != 0 {
144 cellCount++
145 }
146 wc.wsync <- cellCount
147 maxCell = <-wc.wsync
147 wc.wsync <- width
148 width = <-wc.wsync
148149 }
149 return wc.fill(msg, maxCell+(pureWidth-stripWidth))
150 return wc.fill(str, width), width
150151 }
151152
152153 // Init initializes width related config.
153154 func (wc *WC) Init() WC {
154 wc.fill = runewidth.FillLeft
155 if (wc.C & DidentRight) != 0 {
155 if (wc.C & DindentRight) != 0 {
156156 wc.fill = runewidth.FillRight
157 } else {
158 wc.fill = runewidth.FillLeft
157159 }
158160 if (wc.C & DSyncWidth) != 0 {
159161 // it's deliberate choice to override wsync on each Init() call,
164166 }
165167
166168 // Sync is implementation of Synchronizer interface.
167 func (wc *WC) Sync() (chan int, bool) {
169 func (wc WC) Sync() (chan int, bool) {
168170 if (wc.C&DSyncWidth) != 0 && wc.wsync == nil {
169171 panic(fmt.Sprintf("%T is not initialized", wc))
170172 }
171173 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()
182174 }
183175
184176 func initWC(wcc ...WC) WC {
0 // Package decor provides common decorators for "github.com/vbauerster/mpb/v6" module.
1 /*
2 Some decorators returned by this package might have a closure state. It is ok to use
3 decorators concurrently, unless you share the same decorator among multiple
4 *mpb.Bar instances. To avoid data races, create new decorator per *mpb.Bar instance.
5
6 Don't:
7
8 p := mpb.New()
9 name := decor.Name("bar")
10 p.AddBar(100, mpb.AppendDecorators(name))
11 p.AddBar(100, mpb.AppendDecorators(name))
12
13 Do:
14
15 p := mpb.New()
16 p.AddBar(100, mpb.AppendDecorators(decor.Name("bar1")))
17 p.AddBar(100, mpb.AppendDecorators(decor.Name("bar2")))
18 */
0 // Package decor provides common decorators for "github.com/vbauerster/mpb/v8" module.
1 //
2 // Some decorators returned by this package might have a closure state. It is ok to use
3 // decorators concurrently, unless you share the same decorator among multiple
4 // *mpb.Bar instances. To avoid data races, create new decorator per *mpb.Bar instance.
5 //
6 // Don't:
7 //
8 // p := mpb.New()
9 // name := decor.Name("bar")
10 // p.AddBar(100, mpb.AppendDecorators(name))
11 // p.AddBar(100, mpb.AppendDecorators(name))
12 //
13 // Do:
14 //
15 // p := mpb.New()
16 // p.AddBar(100, mpb.AppendDecorators(decor.Name("bar1")))
17 // p.AddBar(100, mpb.AppendDecorators(decor.Name("bar2")))
1918 package decor
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 }
1716 //
1817 // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
1918 //
20 // `startTime` start time
19 // `start` start time
2120 //
2221 // `wcc` optional WC config
23 //
24 func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
22 func NewElapsed(style TimeStyle, start time.Time, wcc ...WC) Decorator {
2523 var msg string
2624 producer := chooseTimeProducer(style)
2725 fn := func(s Statistics) string {
28 if !s.Completed {
29 msg = producer(time.Since(startTime))
26 if !s.Completed && !s.Aborted {
27 msg = producer(time.Since(start))
3028 }
3129 return msg
3230 }
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 {
35 return EwmaNormalizedETA(style, age, nil, wcc...)
36 }
37
38 // EwmaNormalizedETA same as EwmaETA but with TimeNormalizer option.
39 func EwmaNormalizedETA(style TimeStyle, age float64, normalizer TimeNormalizer, wcc ...WC) Decorator {
2940 var average ewma.MovingAverage
3041 if age == 0 {
3142 average = ewma.NewMovingAverage()
3243 } else {
3344 average = ewma.NewMovingAverage(age)
3445 }
35 return MovingAverageETA(style, NewThreadSafeMovingAverage(average), nil, wcc...)
46 return MovingAverageETA(style, average, normalizer, wcc...)
3647 }
3748
3849 // MovingAverageETA decorator relies on MovingAverage implementation to calculate its average.
4455 // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
4556 //
4657 // `wcc` optional WC config
47 //
4858 func MovingAverageETA(style TimeStyle, average ewma.MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
59 if average == nil {
60 average = NewMedian()
61 }
4962 d := &movingAverageETA{
5063 WC: initWC(wcc...),
64 producer: chooseTimeProducer(style),
5165 average: average,
5266 normalizer: normalizer,
53 producer: chooseTimeProducer(style),
5467 }
5568 return d
5669 }
5770
5871 type movingAverageETA struct {
5972 WC
73 producer func(time.Duration) string
6074 average ewma.MovingAverage
6175 normalizer TimeNormalizer
62 producer func(time.Duration) string
63 }
64
65 func (d *movingAverageETA) Decor(s Statistics) string {
76 zDur time.Duration
77 }
78
79 func (d *movingAverageETA) Decor(s Statistics) (string, int) {
6680 v := math.Round(d.average.Value())
6781 remaining := time.Duration((s.Total - s.Current) * int64(v))
6882 if d.normalizer != nil {
6983 remaining = d.normalizer.Normalize(remaining)
7084 }
71 return d.FormatMsg(d.producer(remaining))
85 return d.Format(d.producer(remaining))
7286 }
7387
7488 func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) {
75 durPerItem := float64(dur) / float64(n)
89 if n <= 0 {
90 d.zDur += dur
91 return
92 }
93 durPerItem := float64(d.zDur+dur) / float64(n)
7694 if math.IsInf(durPerItem, 0) || math.IsNaN(durPerItem) {
95 d.zDur += dur
7796 return
7897 }
98 d.zDur = 0
7999 d.average.Add(durPerItem)
80100 }
81101
84104 // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
85105 //
86106 // `wcc` optional WC config
87 //
88107 func AverageETA(style TimeStyle, wcc ...WC) Decorator {
89108 return NewAverageETA(style, time.Now(), nil, wcc...)
90109 }
93112 //
94113 // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
95114 //
96 // `startTime` start time
115 // `start` start time
97116 //
98117 // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
99118 //
100119 // `wcc` optional WC config
101 //
102 func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator {
120 func NewAverageETA(style TimeStyle, start time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator {
103121 d := &averageETA{
104122 WC: initWC(wcc...),
105 startTime: startTime,
123 start: start,
106124 normalizer: normalizer,
107125 producer: chooseTimeProducer(style),
108126 }
111129
112130 type averageETA struct {
113131 WC
114 startTime time.Time
132 start time.Time
115133 normalizer TimeNormalizer
116134 producer func(time.Duration) string
117135 }
118136
119 func (d *averageETA) Decor(s Statistics) string {
137 func (d *averageETA) Decor(s Statistics) (string, int) {
120138 var remaining time.Duration
121139 if s.Current != 0 {
122 durPerItem := float64(time.Since(d.startTime)) / float64(s.Current)
140 durPerItem := float64(time.Since(d.start)) / float64(s.Current)
123141 durPerItem = math.Round(durPerItem)
124142 remaining = time.Duration((s.Total - s.Current) * int64(durPerItem))
125143 if d.normalizer != nil {
126144 remaining = d.normalizer.Normalize(remaining)
127145 }
128146 }
129 return d.FormatMsg(d.producer(remaining))
130 }
131
132 func (d *averageETA) AverageAdjust(startTime time.Time) {
133 d.startTime = startTime
147 return d.Format(d.producer(remaining))
148 }
149
150 func (d *averageETA) AverageAdjust(start time.Time) {
151 d.start = start
134152 }
135153
136154 // MaxTolerateTimeNormalizer returns implementation of TimeNormalizer.
195213 }
196214 default:
197215 return func(remaining time.Duration) string {
198 // strip off nanoseconds
199 return ((remaining / time.Second) * time.Second).String()
200 }
201 }
202 }
216 return remaining.Truncate(time.Second).String()
217 }
218 }
219 }
+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 {
6369
6470 // NewMedian is fixed last 3 samples median MovingAverage.
6571 func NewMedian() ewma.MovingAverage {
66 return NewThreadSafeMovingAverage(new(medianWindow))
72 return new(medianWindow)
6773 }
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.Aborted {
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/v6/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"
5161 }
5262 f := func(s Statistics) string {
53 p := internal.Percentage(s.Total, s.Current, 100)
63 p := internal.Percentage(uint(s.Total), uint(s.Current), 100)
5464 return fmt.Sprintf(format, percentageType(p))
5565 }
5666 return Any(f, wcc...)
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 }
58
59 func TestPercentageDecor(t *testing.T) {
60 cases := []struct {
61 name string
62 fmt string
63 current int64
64 total int64
65 expected string
66 }{
67 {
68 name: "tot:100 cur:0 fmt:none",
69 fmt: "",
70 current: 0,
71 total: 100,
72 expected: "0 %",
73 },
74 {
75 name: "tot:100 cur:10 fmt:none",
76 fmt: "",
77 current: 10,
78 total: 100,
79 expected: "10 %",
80 },
81 {
82 name: "tot:100 cur:10 fmt:%.2f",
83 fmt: "%.2f",
84 current: 10,
85 total: 100,
86 expected: "10.00%",
87 },
88 {
89 name: "tot:99 cur:10 fmt:%.2f",
90 fmt: "%.2f",
91 current: 11,
92 total: 99,
93 expected: "11.11%",
94 },
95 }
96 for _, tc := range cases {
97 t.Run(tc.name, func(t *testing.T) {
98 decor := NewPercentage(tc.fmt)
99 stat := Statistics{
100 Total: tc.total,
101 Current: tc.current,
102 }
103 res, _ := decor.Decor(stat)
104 if res != tc.expected {
105 t.Fatalf("expected: %q, got: %q\n", tc.expected, res)
106 }
107 })
108 }
109 }
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 {
1723 return &speedFormatter{input}
1824 }
2127 fmt.Formatter
2228 }
2329
24 func (self *speedFormatter) Format(st fmt.State, verb rune) {
25 self.Formatter.Format(st, verb)
26 io.WriteString(st, "/s")
30 func (s *speedFormatter) Format(st fmt.State, verb rune) {
31 s.Formatter.Format(st, verb)
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()
3745 } else {
3846 average = ewma.NewMovingAverage(age)
3947 }
40 return MovingAverageSpeed(unit, format, NewThreadSafeMovingAverage(average), wcc...)
48 return MovingAverageSpeed(unit, format, average, wcc...)
4149 }
4250
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...),
71 producer: chooseSpeedProducer(unit, format),
6772 average: average,
68 producer: chooseSpeedProducer(unit, format),
6973 }
7074 return d
7175 }
7478 WC
7579 producer func(float64) string
7680 average ewma.MovingAverage
77 msg string
81 zDur time.Duration
7882 }
7983
80 func (d *movingAverageSpeed) Decor(s Statistics) string {
81 if !s.Completed {
82 var speed float64
83 if v := d.average.Value(); v > 0 {
84 speed = 1 / v
85 }
86 d.msg = d.producer(speed * 1e9)
84 func (d *movingAverageSpeed) Decor(_ Statistics) (string, int) {
85 var str string
86 // ewma implementation may return 0 before accumulating certain number of samples
87 if v := d.average.Value(); v != 0 {
88 str = d.producer(1e9 / v)
89 } else {
90 str = d.producer(0)
8791 }
88 return d.FormatMsg(d.msg)
92 return d.Format(str)
8993 }
9094
9195 func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) {
92 durPerByte := float64(dur) / float64(n)
93 if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) {
96 if n <= 0 {
97 d.zDur += dur
9498 return
9599 }
100 durPerByte := float64(d.zDur+dur) / float64(n)
101 if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) {
102 d.zDur += dur
103 return
104 }
105 d.zDur = 0
96106 d.average.Add(durPerByte)
97107 }
98108
99109 // AverageSpeed decorator with dynamic unit measure adjustment. It's
100110 // a wrapper of NewAverageSpeed.
101 func AverageSpeed(unit int, format string, wcc ...WC) Decorator {
111 func AverageSpeed(unit interface{}, format string, wcc ...WC) Decorator {
102112 return NewAverageSpeed(unit, format, time.Now(), wcc...)
103113 }
104114
105115 // NewAverageSpeed decorator with dynamic unit measure adjustment and
106116 // user provided start time.
107117 //
108 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
118 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
109119 //
110120 // `format` printf compatible verb for value, like "%f" or "%d"
111121 //
112 // `startTime` start time
122 // `start` start time
113123 //
114124 // `wcc` optional WC config
115125 //
116126 // format examples:
117127 //
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 }
128 // unit=SizeB1024(0), format="%.1f" output: "1.0MiB/s"
129 // unit=SizeB1024(0), format="% .1f" output: "1.0 MiB/s"
130 // unit=SizeB1000(0), format="%.1f" output: "1.0MB/s"
131 // unit=SizeB1000(0), format="% .1f" output: "1.0 MB/s"
132 func NewAverageSpeed(unit interface{}, format string, start time.Time, wcc ...WC) Decorator {
127133 d := &averageSpeed{
128 WC: initWC(wcc...),
129 startTime: startTime,
130 producer: chooseSpeedProducer(unit, format),
134 WC: initWC(wcc...),
135 start: start,
136 producer: chooseSpeedProducer(unit, format),
131137 }
132138 return d
133139 }
134140
135141 type averageSpeed struct {
136142 WC
137 startTime time.Time
138 producer func(float64) string
139 msg string
143 start time.Time
144 producer func(float64) string
145 msg string
140146 }
141147
142 func (d *averageSpeed) Decor(s Statistics) string {
148 func (d *averageSpeed) Decor(s Statistics) (string, int) {
143149 if !s.Completed {
144 speed := float64(s.Current) / float64(time.Since(d.startTime))
150 speed := float64(s.Current) / float64(time.Since(d.start))
145151 d.msg = d.producer(speed * 1e9)
146152 }
147
148 return d.FormatMsg(d.msg)
153 return d.Format(d.msg)
149154 }
150155
151 func (d *averageSpeed) AverageAdjust(startTime time.Time) {
152 d.startTime = startTime
156 func (d *averageSpeed) AverageAdjust(start time.Time) {
157 d.start = start
153158 }
154159
155 func chooseSpeedProducer(unit int, format string) func(float64) string {
156 switch unit {
157 case UnitKiB:
160 func chooseSpeedProducer(unit interface{}, format string) func(float64) string {
161 switch unit.(type) {
162 case SizeB1024:
163 if format == "" {
164 format = "% d"
165 }
158166 return func(speed float64) string {
159167 return fmt.Sprintf(format, FmtAsSpeed(SizeB1024(math.Round(speed))))
160168 }
161 case UnitKB:
169 case SizeB1000:
170 if format == "" {
171 format = "% d"
172 }
162173 return func(speed float64) string {
163174 return fmt.Sprintf(format, FmtAsSpeed(SizeB1000(math.Round(speed))))
164175 }
165176 default:
177 if format == "" {
178 format = "%f"
179 }
166180 return func(speed float64) string {
167181 return fmt.Sprintf(format, speed)
168182 }
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/v6"
7 "github.com/vbauerster/mpb/v6/decor"
5 "github.com/vbauerster/mpb/v8"
6 "github.com/vbauerster/mpb/v8/decor"
87 )
98
109 func TestNameDecorator(t *testing.T) {
2524 want: " Test",
2625 },
2726 {
28 decorator: decor.Name("Test", decor.WC{W: 10, C: decor.DidentRight}),
27 decorator: decor.Name("Test", decor.WC{W: 10, C: decor.DindentRight}),
2928 want: "Test ",
3029 },
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 }
8887 testDecoratorConcurrently(t, testCases)
8988 }
9089
91 func TestPercentageDwidthSyncDidentRight(t *testing.T) {
90 func TestPercentageDwidthSyncDindentRight(t *testing.T) {
9291
9392 testCases := [][]step{
9493 {
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 {
11 style BarStyleComposer
1112 name string
12 style string
1313 total int64
1414 current int64
1515 refill int64
1616 barWidth int
1717 trim bool
18 reverse bool
1918 want string
2019 }{
2120 0: {
2221 {
22 style: BarStyle(),
2323 name: "t,c{60,20}",
2424 total: 60,
2525 current: 20,
26 want: "… ",
27 },
28 {
26 want: "",
27 },
28 {
29 style: BarStyle(),
2930 name: "t,c{60,20}trim",
3031 total: 60,
3132 current: 20,
3536 },
3637 1: {
3738 {
39 style: BarStyle(),
3840 name: "t,c{60,20}",
3941 total: 60,
4042 current: 20,
41 want: "… ",
42 },
43 {
43 want: "",
44 },
45 {
46 style: BarStyle(),
4447 name: "t,c{60,20}trim",
4548 total: 60,
4649 current: 20,
5053 },
5154 2: {
5255 {
56 style: BarStyle(),
5357 name: "t,c{60,20}",
5458 total: 60,
5559 current: 20,
5660 want: " ",
5761 },
5862 {
63 style: BarStyle(),
5964 name: "t,c{60,20}trim",
6065 total: 60,
6166 current: 20,
6570 },
6671 3: {
6772 {
73 style: BarStyle(),
6874 name: "t,c{60,20}",
6975 total: 60,
7076 current: 20,
7177 want: " ",
7278 },
7379 {
80 style: BarStyle(),
7481 name: "t,c{60,20}trim",
7582 total: 60,
7683 current: 20,
7784 trim: true,
7885 want: "[-]",
7986 },
87 {
88 style: BarStyle(),
89 name: "t,c{60,59}",
90 total: 60,
91 current: 59,
92 want: " ",
93 },
94 {
95 style: BarStyle(),
96 name: "t,c{60,59}trim",
97 total: 60,
98 current: 59,
99 trim: true,
100 want: "[>]",
101 },
102 {
103 style: BarStyle(),
104 name: "t,c{60,60}",
105 total: 60,
106 current: 60,
107 want: " ",
108 },
109 {
110 style: BarStyle(),
111 name: "t,c{60,60}trim",
112 total: 60,
113 current: 60,
114 trim: true,
115 want: "[=]",
116 },
80117 },
81118 4: {
82119 {
120 style: BarStyle(),
83121 name: "t,c{60,20}",
84122 total: 60,
85123 current: 20,
86124 want: " [] ",
87125 },
88126 {
127 style: BarStyle(),
89128 name: "t,c{60,20}trim",
90129 total: 60,
91130 current: 20,
92131 trim: true,
93132 want: "[>-]",
94133 },
134 {
135 style: BarStyle(),
136 name: "t,c{60,59}",
137 total: 60,
138 current: 59,
139 want: " [] ",
140 },
141 {
142 style: BarStyle(),
143 name: "t,c{60,59}trim",
144 total: 60,
145 current: 59,
146 trim: true,
147 want: "[=>]",
148 },
149 {
150 style: BarStyle(),
151 name: "t,c{60,60}",
152 total: 60,
153 current: 60,
154 want: " [] ",
155 },
156 {
157 style: BarStyle(),
158 name: "t,c{60,60}trim",
159 total: 60,
160 current: 60,
161 trim: true,
162 want: "[==]",
163 },
95164 },
96165 5: {
97166 {
167 style: BarStyle(),
98168 name: "t,c{60,20}",
99169 total: 60,
100170 current: 20,
101171 want: " [-] ",
102172 },
103173 {
174 style: BarStyle(),
104175 name: "t,c{60,20}trim",
105176 total: 60,
106177 current: 20,
107178 trim: true,
108179 want: "[>--]",
109180 },
181 {
182 style: BarStyle(),
183 name: "t,c{60,59}",
184 total: 60,
185 current: 59,
186 want: " [>] ",
187 },
188 {
189 style: BarStyle(),
190 name: "t,c{60,59}trim",
191 total: 60,
192 current: 59,
193 trim: true,
194 want: "[==>]",
195 },
196 {
197 style: BarStyle(),
198 name: "t,c{60,60}",
199 total: 60,
200 current: 60,
201 want: " [=] ",
202 },
203 {
204 style: BarStyle(),
205 name: "t,c{60,60}trim",
206 total: 60,
207 current: 60,
208 trim: true,
209 want: "[===]",
210 },
110211 },
111212 6: {
112213 {
214 style: BarStyle(),
113215 name: "t,c{60,20}",
114216 total: 60,
115217 current: 20,
116218 want: " [>-] ",
117219 },
118220 {
221 style: BarStyle(),
119222 name: "t,c{60,20}trim",
120223 total: 60,
121224 current: 20,
122225 trim: true,
123226 want: "[>---]",
124227 },
228 {
229 style: BarStyle(),
230 name: "t,c{60,59}",
231 total: 60,
232 current: 59,
233 want: " [=>] ",
234 },
235 {
236 style: BarStyle(),
237 name: "t,c{60,59}trim",
238 total: 60,
239 current: 59,
240 trim: true,
241 want: "[===>]",
242 },
243 {
244 style: BarStyle(),
245 name: "t,c{60,60}",
246 total: 60,
247 current: 60,
248 want: " [==] ",
249 },
250 {
251 style: BarStyle(),
252 name: "t,c{60,60}trim",
253 total: 60,
254 current: 60,
255 trim: true,
256 want: "[====]",
257 },
125258 },
126259 7: {
127260 {
261 style: BarStyle(),
128262 name: "t,c{60,20}",
129263 total: 60,
130264 current: 20,
131265 want: " [>--] ",
132266 },
133267 {
268 style: BarStyle(),
134269 name: "t,c{60,20}trim",
135270 total: 60,
136271 current: 20,
137272 trim: true,
138273 want: "[=>---]",
139274 },
275 {
276 style: BarStyle(),
277 name: "t,c{60,59}",
278 total: 60,
279 current: 59,
280 want: " [==>] ",
281 },
282 {
283 style: BarStyle(),
284 name: "t,c{60,59}trim",
285 total: 60,
286 current: 59,
287 trim: true,
288 want: "[====>]",
289 },
290 {
291 style: BarStyle(),
292 name: "t,c{60,60}",
293 total: 60,
294 current: 60,
295 want: " [===] ",
296 },
297 {
298 style: BarStyle(),
299 name: "t,c{60,60}trim",
300 total: 60,
301 current: 60,
302 trim: true,
303 want: "[=====]",
304 },
140305 },
141306 8: {
142307 {
308 style: BarStyle(),
143309 name: "t,c{60,20}",
144310 total: 60,
145311 current: 20,
146312 want: " [>---] ",
147313 },
148314 {
315 style: BarStyle(),
149316 name: "t,c{60,20}trim",
150317 total: 60,
151318 current: 20,
152319 trim: true,
153320 want: "[=>----]",
154321 },
322 {
323 style: BarStyle(),
324 name: "t,c{60,59}",
325 total: 60,
326 current: 59,
327 want: " [===>] ",
328 },
329 {
330 style: BarStyle(),
331 name: "t,c{60,59}trim",
332 total: 60,
333 current: 59,
334 trim: true,
335 want: "[=====>]",
336 },
337 {
338 style: BarStyle(),
339 name: "t,c{60,60}",
340 total: 60,
341 current: 60,
342 want: " [====] ",
343 },
344 {
345 style: BarStyle(),
346 name: "t,c{60,60}trim",
347 total: 60,
348 current: 60,
349 trim: true,
350 want: "[======]",
351 },
155352 },
156353 80: {
157354 {
355 style: BarStyle(),
158356 name: "t,c{60,20}",
159357 total: 60,
160358 current: 20,
161359 want: " [========================>---------------------------------------------------] ",
162360 },
163361 {
362 style: BarStyle(),
164363 name: "t,c{60,20}trim",
165364 total: 60,
166365 current: 20,
168367 want: "[=========================>----------------------------------------------------]",
169368 },
170369 {
370 style: BarStyle(),
171371 name: "t,c,bw{60,20,60}",
172372 total: 60,
173373 current: 20,
175375 want: " [==================>---------------------------------------] ",
176376 },
177377 {
378 style: BarStyle(),
178379 name: "t,c,bw{60,20,60}trim",
179380 total: 60,
180381 current: 20,
182383 trim: true,
183384 want: "[==================>---------------------------------------]",
184385 },
386 {
387 style: BarStyle(),
388 name: "t,c{60,59}",
389 total: 60,
390 current: 59,
391 want: " [==========================================================================>-] ",
392 },
393 {
394 style: BarStyle(),
395 name: "t,c{60,59}trim",
396 total: 60,
397 current: 59,
398 trim: true,
399 want: "[============================================================================>-]",
400 },
401 {
402 style: BarStyle(),
403 name: "t,c,bw{60,59,60}",
404 total: 60,
405 current: 59,
406 barWidth: 60,
407 want: " [========================================================>-] ",
408 },
409 {
410 style: BarStyle(),
411 name: "t,c,bw{60,59,60}trim",
412 total: 60,
413 current: 59,
414 barWidth: 60,
415 trim: true,
416 want: "[========================================================>-]",
417 },
418 {
419 style: BarStyle(),
420 name: "t,c{60,60}",
421 total: 60,
422 current: 60,
423 want: " [============================================================================] ",
424 },
425 {
426 style: BarStyle(),
427 name: "t,c{60,60}trim",
428 total: 60,
429 current: 60,
430 trim: true,
431 want: "[==============================================================================]",
432 },
433 {
434 style: BarStyle(),
435 name: "t,c,bw{60,60,60}",
436 total: 60,
437 current: 60,
438 barWidth: 60,
439 want: " [==========================================================] ",
440 },
441 {
442 style: BarStyle(),
443 name: "t,c,bw{60,60,60}trim",
444 total: 60,
445 current: 60,
446 barWidth: 60,
447 trim: true,
448 want: "[==========================================================]",
449 },
450 },
451 99: {
452 {
453 style: BarStyle(),
454 name: "t,c{100,1}",
455 total: 100,
456 current: 1,
457 want: " [>----------------------------------------------------------------------------------------------] ",
458 },
459 {
460 style: BarStyle(),
461 name: "t,c{100,1}trim",
462 total: 100,
463 current: 1,
464 trim: true,
465 want: "[>------------------------------------------------------------------------------------------------]",
466 },
467 {
468 style: BarStyle(),
469 name: "t,c,r{100,40,33}",
470 total: 100,
471 current: 40,
472 refill: 33,
473 want: " [+++++++++++++++++++++++++++++++======>---------------------------------------------------------] ",
474 },
475 {
476 style: BarStyle(),
477 name: "t,c,r{100,40,33}trim",
478 total: 100,
479 current: 40,
480 refill: 33,
481 trim: true,
482 want: "[++++++++++++++++++++++++++++++++======>----------------------------------------------------------]",
483 },
484 {
485 style: BarStyle().Tip("<").Reverse(),
486 name: "t,c,r{100,40,33},rev",
487 total: 100,
488 current: 40,
489 refill: 33,
490 want: " [---------------------------------------------------------<======+++++++++++++++++++++++++++++++] ",
491 },
492 {
493 style: BarStyle().Tip("<").Reverse(),
494 name: "t,c,r{100,40,33}trim,rev",
495 total: 100,
496 current: 40,
497 refill: 33,
498 trim: true,
499 want: "[----------------------------------------------------------<======++++++++++++++++++++++++++++++++]",
500 },
501 {
502 style: BarStyle(),
503 name: "t,c{100,99}",
504 total: 100,
505 current: 99,
506 want: " [=============================================================================================>-] ",
507 },
508 {
509 style: BarStyle(),
510 name: "t,c{100,99}trim",
511 total: 100,
512 current: 99,
513 trim: true,
514 want: "[===============================================================================================>-]",
515 },
516 {
517 style: BarStyle(),
518 name: "t,c{100,100}",
519 total: 100,
520 current: 100,
521 want: " [===============================================================================================] ",
522 },
523 {
524 style: BarStyle(),
525 name: "t,c{100,100}trim",
526 total: 100,
527 current: 100,
528 trim: true,
529 want: "[=================================================================================================]",
530 },
531 {
532 style: BarStyle(),
533 name: "t,c,r{100,100,99}",
534 total: 100,
535 current: 100,
536 refill: 99,
537 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=] ",
538 },
539 {
540 style: BarStyle(),
541 name: "t,c,r{100,100,99}trim",
542 total: 100,
543 current: 100,
544 refill: 99,
545 trim: true,
546 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=]",
547 },
548 {
549 style: BarStyle().Reverse(),
550 name: "t,c,r{100,100,99}rev",
551 total: 100,
552 current: 100,
553 refill: 99,
554 want: " [=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
555 },
556 {
557 style: BarStyle().Reverse(),
558 name: "t,c,r{100,100,99}trim,rev",
559 total: 100,
560 current: 100,
561 refill: 99,
562 trim: true,
563 want: "[=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
564 },
565 {
566 style: BarStyle(),
567 name: "t,c,r{100,100,100}",
568 total: 100,
569 current: 100,
570 refill: 100,
571 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
572 },
573 {
574 style: BarStyle(),
575 name: "t,c,r{100,100,100}trim",
576 total: 100,
577 current: 100,
578 refill: 100,
579 trim: true,
580 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
581 },
582 {
583 style: BarStyle().Reverse(),
584 name: "t,c,r{100,100,100}rev",
585 total: 100,
586 current: 100,
587 refill: 100,
588 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
589 },
590 {
591 style: BarStyle().Reverse(),
592 name: "t,c,r{100,100,100}trim",
593 total: 100,
594 current: 100,
595 refill: 100,
596 trim: true,
597 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
598 },
185599 },
186600 100: {
187601 {
602 style: BarStyle(),
188603 name: "t,c{100,0}",
189604 total: 100,
190605 current: 0,
191606 want: " [------------------------------------------------------------------------------------------------] ",
192607 },
193608 {
609 style: BarStyle(),
194610 name: "t,c{100,0}trim",
195611 total: 100,
196612 current: 0,
198614 want: "[--------------------------------------------------------------------------------------------------]",
199615 },
200616 {
617 style: BarStyle(),
201618 name: "t,c{100,1}",
202619 total: 100,
203620 current: 1,
204621 want: " [>-----------------------------------------------------------------------------------------------] ",
205622 },
206623 {
624 style: BarStyle().Tip(""),
625 name: "t,c{100,1}empty_tip",
626 total: 100,
627 current: 1,
628 want: " [=-----------------------------------------------------------------------------------------------] ",
629 },
630 {
631 style: BarStyle(),
207632 name: "t,c{100,1}trim",
208633 total: 100,
209634 current: 1,
211636 want: "[>-------------------------------------------------------------------------------------------------]",
212637 },
213638 {
639 style: BarStyle(),
214640 name: "t,c{100,99}",
215641 total: 100,
216642 current: 99,
217643 want: " [==============================================================================================>-] ",
218644 },
219645 {
646 style: BarStyle().Tip(""),
647 name: "t,c{100,99}empty_tip",
648 total: 100,
649 current: 99,
650 want: " [===============================================================================================-] ",
651 },
652 {
653 style: BarStyle(),
220654 name: "t,c{100,99}trim",
221655 total: 100,
222656 current: 99,
224658 want: "[================================================================================================>-]",
225659 },
226660 {
661 style: BarStyle(),
227662 name: "t,c{100,100}",
228663 total: 100,
229664 current: 100,
230665 want: " [================================================================================================] ",
231666 },
232667 {
668 style: BarStyle(),
233669 name: "t,c{100,100}trim",
234670 total: 100,
235671 current: 100,
237673 want: "[==================================================================================================]",
238674 },
239675 {
676 style: BarStyle(),
677 name: "t,c,r{100,100,99}",
678 total: 100,
679 current: 100,
680 refill: 99,
681 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=] ",
682 },
683 {
684 style: BarStyle(),
685 name: "t,c,r{100,100,99}trim",
686 total: 100,
687 current: 100,
688 refill: 99,
689 trim: true,
690 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=]",
691 },
692 {
693 style: BarStyle(),
694 name: "t,c,r{100,100,100}",
695 total: 100,
696 current: 100,
697 refill: 100,
698 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
699 },
700 {
701 style: BarStyle(),
240702 name: "t,c,r{100,100,100}trim",
241703 total: 100,
242704 current: 100,
245707 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
246708 },
247709 {
248 name: "t,c{100,33}",
249 total: 100,
250 current: 33,
251 want: " [===============================>----------------------------------------------------------------] ",
252 },
253 {
254 name: "t,c{100,33}trim",
255 total: 100,
256 current: 33,
257 trim: true,
258 want: "[===============================>------------------------------------------------------------------]",
259 },
260 {
261 name: "t,c{100,33}trim,rev",
262 total: 100,
263 current: 33,
264 trim: true,
265 reverse: true,
266 want: "[------------------------------------------------------------------<===============================]",
267 },
268 {
269 name: "t,c,r{100,33,33}",
270 total: 100,
271 current: 33,
272 refill: 33,
273 want: " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ",
274 },
275 {
276 name: "t,c,r{100,33,33}trim",
277 total: 100,
278 current: 33,
279 refill: 33,
280 trim: true,
281 want: "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]",
282 },
283 {
284 name: "t,c,r{100,33,33}trim,rev",
285 total: 100,
286 current: 33,
287 refill: 33,
288 trim: true,
289 reverse: true,
290 want: "[------------------------------------------------------------------<+++++++++++++++++++++++++++++++]",
291 },
292 {
710 style: BarStyle().Reverse(),
711 name: "t,c,r{100,100,99}rev",
712 total: 100,
713 current: 100,
714 refill: 99,
715 want: " [=+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
716 },
717 {
718 style: BarStyle().Reverse(),
719 name: "t,c,r{100,100,99}trim,rev",
720 total: 100,
721 current: 100,
722 refill: 99,
723 trim: true,
724 want: "[=+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
725 },
726 {
727 style: BarStyle().Reverse(),
728 name: "t,c,r{100,100,100}rev",
729 total: 100,
730 current: 100,
731 refill: 100,
732 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
733 },
734 {
735 style: BarStyle().Reverse(),
736 name: "t,c,r{100,100,100}trim",
737 total: 100,
738 current: 100,
739 refill: 100,
740 trim: true,
741 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
742 },
743 {
744 style: BarStyle(),
293745 name: "t,c,r{100,40,33}",
294746 total: 100,
295747 current: 40,
297749 want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ",
298750 },
299751 {
752 style: BarStyle(),
300753 name: "t,c,r{100,40,33}trim",
301754 total: 100,
302755 current: 40,
305758 want: "[++++++++++++++++++++++++++++++++======>-----------------------------------------------------------]",
306759 },
307760 {
761 style: BarStyle().Tip("<").Reverse(),
308762 name: "t,c,r{100,40,33},rev",
309763 total: 100,
310764 current: 40,
311765 refill: 33,
312 reverse: true,
313766 want: " [----------------------------------------------------------<=====++++++++++++++++++++++++++++++++] ",
314767 },
315768 {
769 style: BarStyle().Tip("<").Reverse(),
316770 name: "t,c,r{100,40,33}trim,rev",
317771 total: 100,
318772 current: 40,
319773 refill: 33,
320774 trim: true,
321 reverse: true,
322775 want: "[-----------------------------------------------------------<======++++++++++++++++++++++++++++++++]",
323 },
324 {
325 name: "[=の-] t,c{100,1}",
326 style: "[=の-]",
327 total: 100,
328 current: 1,
329 want: " [の---------------------------------------------------------------------------------------------…] ",
330 },
331 },
332 197: {
333 {
334 name: "t,c,r{97486999,2805950,2805483}trim",
335 total: 97486999,
336 current: 2805950,
337 refill: 2805483,
338 barWidth: 60,
339 trim: true,
340 want: "[+>--------------------------------------------------------]",
341776 },
342777 },
343778 }
345780 var tmpBuf bytes.Buffer
346781 for tw, cases := range testSuite {
347782 for _, tc := range cases {
348 s := newTestState(tc.style, tc.reverse)
349 s.reqWidth = tc.barWidth
350 s.total = tc.total
783 ps := pState{reqWidth: tc.barWidth}
784 s := ps.makeBarState(tc.total, tc.style.Build())
351785 s.current = tc.current
352786 s.trimSpace = tc.trim
353787 s.refill = tc.refill
788 r, err := s.draw(s.newStatistics(tw))
789 if err != nil {
790 t.Fatalf("tw: %d case %q draw error: %s", tw, tc.name, err.Error())
791 }
354792 tmpBuf.Reset()
355 tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
793 _, err = tmpBuf.ReadFrom(r)
794 if err != nil {
795 t.FailNow()
796 }
797 by := tmpBuf.Bytes()
798 got := string(by[:len(by)-1])
799 if !utf8.ValidString(got) {
800 t.Fail()
801 }
802 if got != tc.want {
803 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))
804 }
805 }
806 }
807 }
808
809 func TestDrawTipOnComplete(t *testing.T) {
810 // key is termWidth
811 testSuite := map[int][]struct {
812 style BarStyleComposer
813 name string
814 total int64
815 current int64
816 refill int64
817 barWidth int
818 trim bool
819 want string
820 }{
821 3: {
822 {
823 style: BarStyle().TipOnComplete(),
824 name: "t,c{60,60}",
825 total: 60,
826 current: 60,
827 want: " ",
828 },
829 {
830 style: BarStyle().TipOnComplete(),
831 name: "t,c{60,60}trim",
832 total: 60,
833 current: 60,
834 trim: true,
835 want: "[>]",
836 },
837 },
838 4: {
839 {
840 style: BarStyle().TipOnComplete(),
841 name: "t,c{60,59}",
842 total: 60,
843 current: 59,
844 want: " [] ",
845 },
846 {
847 style: BarStyle().TipOnComplete(),
848 name: "t,c{60,59}trim",
849 total: 60,
850 current: 59,
851 trim: true,
852 want: "[=>]",
853 },
854 {
855 style: BarStyle().TipOnComplete(),
856 name: "t,c{60,60}",
857 total: 60,
858 current: 60,
859 want: " [] ",
860 },
861 {
862 style: BarStyle().TipOnComplete(),
863 name: "t,c{60,60}trim",
864 total: 60,
865 current: 60,
866 trim: true,
867 want: "[=>]",
868 },
869 },
870 5: {
871 {
872 style: BarStyle().TipOnComplete(),
873 name: "t,c{60,59}",
874 total: 60,
875 current: 59,
876 want: " [>] ",
877 },
878 {
879 style: BarStyle().TipOnComplete(),
880 name: "t,c{60,59}trim",
881 total: 60,
882 current: 59,
883 trim: true,
884 want: "[==>]",
885 },
886 {
887 style: BarStyle().TipOnComplete(),
888 name: "t,c{60,60}",
889 total: 60,
890 current: 60,
891 want: " [>] ",
892 },
893 {
894 style: BarStyle().TipOnComplete(),
895 name: "t,c{60,60}trim",
896 total: 60,
897 current: 60,
898 trim: true,
899 want: "[==>]",
900 },
901 },
902 6: {
903 {
904 style: BarStyle().TipOnComplete(),
905 name: "t,c{60,59}",
906 total: 60,
907 current: 59,
908 want: " [=>] ",
909 },
910 {
911 style: BarStyle().TipOnComplete(),
912 name: "t,c{60,59}trim",
913 total: 60,
914 current: 59,
915 trim: true,
916 want: "[===>]",
917 },
918 {
919 style: BarStyle().TipOnComplete(),
920 name: "t,c{60,60}",
921 total: 60,
922 current: 60,
923 want: " [=>] ",
924 },
925 {
926 style: BarStyle().TipOnComplete(),
927 name: "t,c{60,60}trim",
928 total: 60,
929 current: 60,
930 trim: true,
931 want: "[===>]",
932 },
933 },
934 7: {
935 {
936 style: BarStyle().TipOnComplete(),
937 name: "t,c{60,59}",
938 total: 60,
939 current: 59,
940 want: " [==>] ",
941 },
942 {
943 style: BarStyle().TipOnComplete(),
944 name: "t,c{60,59}trim",
945 total: 60,
946 current: 59,
947 trim: true,
948 want: "[====>]",
949 },
950 {
951 style: BarStyle().TipOnComplete(),
952 name: "t,c{60,60}",
953 total: 60,
954 current: 60,
955 want: " [==>] ",
956 },
957 {
958 style: BarStyle().TipOnComplete(),
959 name: "t,c{60,60}trim",
960 total: 60,
961 current: 60,
962 trim: true,
963 want: "[====>]",
964 },
965 },
966 8: {
967 {
968 style: BarStyle().TipOnComplete(),
969 name: "t,c{60,59}",
970 total: 60,
971 current: 59,
972 want: " [===>] ",
973 },
974 {
975 style: BarStyle().TipOnComplete(),
976 name: "t,c{60,59}trim",
977 total: 60,
978 current: 59,
979 trim: true,
980 want: "[=====>]",
981 },
982 {
983 style: BarStyle().TipOnComplete(),
984 name: "t,c{60,60}",
985 total: 60,
986 current: 60,
987 want: " [===>] ",
988 },
989 {
990 style: BarStyle().TipOnComplete(),
991 name: "t,c{60,60}trim",
992 total: 60,
993 current: 60,
994 trim: true,
995 want: "[=====>]",
996 },
997 },
998 80: {
999 {
1000 style: BarStyle().TipOnComplete(),
1001 name: "t,c{60,59}",
1002 total: 60,
1003 current: 59,
1004 want: " [==========================================================================>-] ",
1005 },
1006 {
1007 style: BarStyle().TipOnComplete(),
1008 name: "t,c{60,59}trim",
1009 total: 60,
1010 current: 59,
1011 trim: true,
1012 want: "[============================================================================>-]",
1013 },
1014 {
1015 style: BarStyle().TipOnComplete(),
1016 name: "t,c,bw{60,59,60}",
1017 total: 60,
1018 current: 59,
1019 barWidth: 60,
1020 want: " [========================================================>-] ",
1021 },
1022 {
1023 style: BarStyle().TipOnComplete(),
1024 name: "t,c,bw{60,59,60}trim",
1025 total: 60,
1026 current: 59,
1027 barWidth: 60,
1028 trim: true,
1029 want: "[========================================================>-]",
1030 },
1031 {
1032 style: BarStyle().TipOnComplete(),
1033 name: "t,c{60,60}",
1034 total: 60,
1035 current: 60,
1036 want: " [===========================================================================>] ",
1037 },
1038 {
1039 style: BarStyle().TipOnComplete(),
1040 name: "t,c{60,60}trim",
1041 total: 60,
1042 current: 60,
1043 trim: true,
1044 want: "[=============================================================================>]",
1045 },
1046 {
1047 style: BarStyle().TipOnComplete(),
1048 name: "t,c,bw{60,60,60}",
1049 total: 60,
1050 current: 60,
1051 barWidth: 60,
1052 want: " [=========================================================>] ",
1053 },
1054 {
1055 style: BarStyle().TipOnComplete(),
1056 name: "t,c,bw{60,60,60}trim",
1057 total: 60,
1058 current: 60,
1059 barWidth: 60,
1060 trim: true,
1061 want: "[=========================================================>]",
1062 },
1063 },
1064 99: {
1065 {
1066 style: BarStyle().TipOnComplete(),
1067 name: "t,c{100,99}",
1068 total: 100,
1069 current: 99,
1070 want: " [=============================================================================================>-] ",
1071 },
1072 {
1073 style: BarStyle().TipOnComplete(),
1074 name: "t,c{100,99}trim",
1075 total: 100,
1076 current: 99,
1077 trim: true,
1078 want: "[===============================================================================================>-]",
1079 },
1080 {
1081 style: BarStyle().TipOnComplete(),
1082 name: "t,c{100,100}",
1083 total: 100,
1084 current: 100,
1085 want: " [==============================================================================================>] ",
1086 },
1087 {
1088 style: BarStyle().TipOnComplete(),
1089 name: "t,c{100,100}trim",
1090 total: 100,
1091 current: 100,
1092 trim: true,
1093 want: "[================================================================================================>]",
1094 },
1095 {
1096 style: BarStyle().TipOnComplete(),
1097 name: "t,c,r{100,100,99}",
1098 total: 100,
1099 current: 100,
1100 refill: 99,
1101 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1102 },
1103 {
1104 style: BarStyle().TipOnComplete(),
1105 name: "t,c,r{100,100,99}trim",
1106 total: 100,
1107 current: 100,
1108 refill: 99,
1109 trim: true,
1110 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1111 },
1112 {
1113 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1114 name: `t,c,r{100,100,99}.Tip("<").TipOnComplete().Reverse()`,
1115 total: 100,
1116 current: 100,
1117 refill: 99,
1118 want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
1119 },
1120 {
1121 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1122 name: `t,c,r{100,100,99}.Tip("<").TipOnComplete().Reverse()trim`,
1123 total: 100,
1124 current: 100,
1125 refill: 99,
1126 trim: true,
1127 want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
1128 },
1129 {
1130 style: BarStyle().TipOnComplete(),
1131 name: "t,c,r{100,100,100}",
1132 total: 100,
1133 current: 100,
1134 refill: 100,
1135 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1136 },
1137 {
1138 style: BarStyle().TipOnComplete(),
1139 name: "t,c,r{100,100,100}trim",
1140 total: 100,
1141 current: 100,
1142 refill: 100,
1143 trim: true,
1144 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1145 },
1146 {
1147 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1148 name: `t,c,r{100,100,100}.Tip("<").TipOnComplete().Reverse()`,
1149 total: 100,
1150 current: 100,
1151 refill: 100,
1152 want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
1153 },
1154 {
1155 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1156 name: `t,c,r{100,100,100}.Tip("<").TipOnComplete().Reverse()trim`,
1157 total: 100,
1158 current: 100,
1159 refill: 100,
1160 trim: true,
1161 want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
1162 },
1163 },
1164 100: {
1165 {
1166 style: BarStyle().TipOnComplete(),
1167 name: "t,c{100,99}",
1168 total: 100,
1169 current: 99,
1170 want: " [==============================================================================================>-] ",
1171 },
1172 {
1173 style: BarStyle().TipOnComplete(),
1174 name: "t,c{100,99}trim",
1175 total: 100,
1176 current: 99,
1177 trim: true,
1178 want: "[================================================================================================>-]",
1179 },
1180 {
1181 style: BarStyle().TipOnComplete(),
1182 name: "t,c{100,100}",
1183 total: 100,
1184 current: 100,
1185 want: " [===============================================================================================>] ",
1186 },
1187 {
1188 style: BarStyle().TipOnComplete(),
1189 name: "t,c{100,100}trim",
1190 total: 100,
1191 current: 100,
1192 trim: true,
1193 want: "[=================================================================================================>]",
1194 },
1195 {
1196 style: BarStyle().TipOnComplete(),
1197 name: "t,c,r{100,100,99}",
1198 total: 100,
1199 current: 100,
1200 refill: 99,
1201 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1202 },
1203 {
1204 style: BarStyle().TipOnComplete(),
1205 name: "t,c,r{100,100,99}trim",
1206 total: 100,
1207 current: 100,
1208 refill: 99,
1209 trim: true,
1210 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1211 },
1212 {
1213 style: BarStyle().TipOnComplete(),
1214 name: "t,c,r{100,100,100}",
1215 total: 100,
1216 current: 100,
1217 refill: 100,
1218 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1219 },
1220 {
1221 style: BarStyle().TipOnComplete(),
1222 name: "t,c,r{100,100,100}trim",
1223 total: 100,
1224 current: 100,
1225 refill: 100,
1226 trim: true,
1227 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1228 },
1229 },
1230 }
1231
1232 var tmpBuf bytes.Buffer
1233 for tw, cases := range testSuite {
1234 for _, tc := range cases {
1235 ps := pState{reqWidth: tc.barWidth}
1236 s := ps.makeBarState(tc.total, tc.style.Build())
1237 s.current = tc.current
1238 s.trimSpace = tc.trim
1239 s.refill = tc.refill
1240 r, err := s.draw(s.newStatistics(tw))
1241 if err != nil {
1242 t.Fatalf("tw: %d case %q draw error: %s", tw, tc.name, err.Error())
1243 }
1244 tmpBuf.Reset()
1245 _, err = tmpBuf.ReadFrom(r)
1246 if err != nil {
1247 t.FailNow()
1248 }
3561249 by := tmpBuf.Bytes()
3571250
3581251 got := string(by[:len(by)-1])
3661259 }
3671260 }
3681261
369 func newTestState(style string, rev bool) *bState {
370 s := &bState{
371 filler: NewBarFillerPick(style, rev),
372 bufP: new(bytes.Buffer),
373 bufB: new(bytes.Buffer),
374 bufA: new(bytes.Buffer),
1262 func TestDrawDoubleWidth(t *testing.T) {
1263 // key is termWidth
1264 testSuite := map[int][]struct {
1265 style BarStyleComposer
1266 name string
1267 total int64
1268 current int64
1269 refill int64
1270 barWidth int
1271 trim bool
1272 want string
1273 }{
1274 99: {
1275 {
1276 style: BarStyle().Lbound("の").Rbound("の"),
1277 name: `t,c{100,1}.Lbound("の").Rbound("の")`,
1278 total: 100,
1279 current: 1,
1280 want: " の>--------------------------------------------------------------------------------------------の ",
1281 },
1282 {
1283 style: BarStyle().Lbound("の").Rbound("の"),
1284 name: `t,c{100,1}.Lbound("の").Rbound("の")`,
1285 total: 100,
1286 current: 2,
1287 want: " の=>-------------------------------------------------------------------------------------------の ",
1288 },
1289 {
1290 style: BarStyle().Tip("だ"),
1291 name: `t,c{100,1}Tip("だ")`,
1292 total: 100,
1293 current: 1,
1294 want: " [だ---------------------------------------------------------------------------------------------] ",
1295 },
1296 {
1297 style: BarStyle().Tip("だ"),
1298 name: `t,c{100,2}Tip("だ")`,
1299 total: 100,
1300 current: 2,
1301 want: " [だ---------------------------------------------------------------------------------------------] ",
1302 },
1303 {
1304 style: BarStyle().Tip("だ"),
1305 name: `t,c{100,3}Tip("だ")`,
1306 total: 100,
1307 current: 3,
1308 want: " [=だ--------------------------------------------------------------------------------------------] ",
1309 },
1310 {
1311 style: BarStyle().Tip("だ"),
1312 name: `t,c{100,99}Tip("だ")`,
1313 total: 100,
1314 current: 99,
1315 want: " [============================================================================================だ-] ",
1316 },
1317 {
1318 style: BarStyle().Tip("だ"),
1319 name: `t,c{100,100}Tip("だ")`,
1320 total: 100,
1321 current: 100,
1322 want: " [===============================================================================================] ",
1323 },
1324 {
1325 style: BarStyle().Tip("だ").TipOnComplete(),
1326 name: `t,c{100,100}.Tip("だ").TipOnComplete()`,
1327 total: 100,
1328 current: 100,
1329 want: " [=============================================================================================だ] ",
1330 },
1331 {
1332 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1333 name: `t,c{100,1}Filler("の").Tip("だ").Padding("つ")`,
1334 total: 100,
1335 current: 1,
1336 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
1337 },
1338 {
1339 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1340 name: `t,c{100,2}Filler("の").Tip("だ").Padding("つ")`,
1341 total: 100,
1342 current: 2,
1343 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
1344 },
1345 {
1346 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1347 name: `t,c{100,99}Filler("の").Tip("だ").Padding("つ")`,
1348 total: 100,
1349 current: 99,
1350 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1351 },
1352 {
1353 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1354 name: `t,c{100,100}.Filler("の").Tip("だ").Padding("つ")`,
1355 total: 100,
1356 current: 100,
1357 want: " [ののののののののののののののののののののののののののののののののののののののののののののののの…] ",
1358 },
1359 {
1360 style: BarStyle().Filler("の").Tip("だ").Padding("つ").Reverse(),
1361 name: `t,c{100,100}Filler("の").Tip("だ").Padding("つ").Reverse()`,
1362 total: 100,
1363 current: 100,
1364 want: " […ののののののののののののののののののののののののののののののののののののののののののののののの] ",
1365 },
1366 {
1367 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ"),
1368 name: `t,c{100,99}Filler("の").Tip("だ").TipOnComplete().Padding("つ")`,
1369 total: 100,
1370 current: 99,
1371 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1372 },
1373 {
1374 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ"),
1375 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete().Padding("つ")`,
1376 total: 100,
1377 current: 100,
1378 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1379 },
1380 {
1381 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ").Reverse(),
1382 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete().Padding("つ").Reverse()`,
1383 total: 100,
1384 current: 100,
1385 want: " […だのののののののののののののののののののののののののののののののののののののののののののののの] ",
1386 },
1387 {
1388 style: BarStyle().Refiller("の"),
1389 name: `t,c,r{100,100,99}Refiller("の")`,
1390 total: 100,
1391 current: 100,
1392 refill: 99,
1393 want: " [ののののののののののののののののののののののののののののののののののののののののののののののの=] ",
1394 },
1395 {
1396 style: BarStyle().Refiller("の"),
1397 name: `t,c,r{100,100,99}Refiller("の")trim`,
1398 total: 100,
1399 current: 100,
1400 refill: 99,
1401 trim: true,
1402 want: "[のののののののののののののののののののののののののののののののののののののののののののののののの=]",
1403 },
1404 },
3751405 }
376 return s
1406
1407 var tmpBuf bytes.Buffer
1408 for tw, cases := range testSuite {
1409 for _, tc := range cases {
1410 ps := pState{reqWidth: tc.barWidth}
1411 s := ps.makeBarState(tc.total, tc.style.Build())
1412 s.current = tc.current
1413 s.trimSpace = tc.trim
1414 s.refill = tc.refill
1415 r, err := s.draw(s.newStatistics(tw))
1416 if err != nil {
1417 t.Fatalf("tw: %d case %q draw error: %s", tw, tc.name, err.Error())
1418 }
1419 tmpBuf.Reset()
1420 _, err = tmpBuf.ReadFrom(r)
1421 if err != nil {
1422 t.FailNow()
1423 }
1424 by := tmpBuf.Bytes()
1425
1426 got := string(by[:len(by)-1])
1427 if !utf8.ValidString(got) {
1428 t.Fail()
1429 }
1430 if got != tc.want {
1431 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))
1432 }
1433 }
1434 }
3771435 }
22 import (
33 crand "crypto/rand"
44 "io"
5 "io/ioutil"
65 "math/rand"
76 "time"
87
9 "github.com/vbauerster/mpb/v6"
10 "github.com/vbauerster/mpb/v6/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("╢▌▌░╟"),
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
25 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
24 decor.Name(name, decor.WC{C: decor.DindentRight | decor.DextraSpace}),
2625 // replace ETA decorator with "done" message, OnComplete event
27 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",
30 ),
26 decor.OnComplete(decor.AverageETA(decor.ET_STYLE_GO), "done"),
3127 ),
3228 mpb.AppendDecorators(decor.Percentage()),
3329 )
3430 // simulating some work
3531 max := 100 * time.Millisecond
3632 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()
4033 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
4134 bar.Increment()
42 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
43 bar.DecoratorEwmaUpdate(time.Since(start))
4435 }
4536 // wait for our bar to complete and flush
4637 p.Wait()
7768 defer proxyReader.Close()
7869
7970 // and copy from reader, ignoring errors
80 io.Copy(ioutil.Discard, proxyReader)
71 _, _ = io.Copy(io.Discard, proxyReader)
8172
8273 p.Wait()
8374 }
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/v6
0 module github.com/vbauerster/mpb/v8
11
22 require (
3 github.com/VividCortex/ewma v1.1.1
3 github.com/VividCortex/ewma v1.2.0
44 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
5 github.com/mattn/go-runewidth v0.0.10
6 github.com/rivo/uniseg v0.2.0
7 golang.org/x/sys v0.0.0-20210324051608-47abb6519492
5 github.com/mattn/go-runewidth v0.0.16
6 golang.org/x/sys v0.24.0
87 )
98
10 go 1.14
9 require github.com/rivo/uniseg v0.4.7 // indirect
10
11 go 1.17
0 github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
1 github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
0 github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
1 github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
22 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
33 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
4 github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
5 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
6 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
7 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
4 github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
5 github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
86 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
9 golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg=
10 golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
7 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
8 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
9 golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
10 golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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 {
6 if total <= 0 {
5 func Percentage(total, current, width uint) float64 {
6 if total == 0 {
77 return 0
88 }
99 if current >= total {
1010 return float64(width)
1111 }
12 return float64(int64(width)*current) / float64(total)
12 return float64(width*current) / float64(total)
1313 }
1414
1515 // PercentageRound same as Percentage but with math.Round.
16 func PercentageRound(total, current int64, width int) float64 {
17 return math.Round(Percentage(total, current, width))
16 func PercentageRound(total, current int64, width uint) float64 {
17 if total < 0 || current < 0 {
18 return 0
19 }
20 return math.Round(Percentage(uint(total), uint(current), width))
1821 }
33
44 func TestPercentage(t *testing.T) {
55 // key is barWidth
6 testSuite := map[int][]struct {
6 testSuite := map[uint][]struct {
77 name string
88 total int64
99 current int64
+0
-6
internal/predicate.go less more
0 package internal
1
2 // Predicate helper for internal use.
3 func Predicate(pick bool) func() bool {
4 return func() bool { return pick }
5 }
22 // CheckRequestedWidth checks that requested width doesn't overflow
33 // available width
44 func CheckRequestedWidth(requested, available int) int {
5 if requested <= 0 || requested >= available {
5 if requested < 1 || requested > available {
66 return available
77 }
88 return requested
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/v6/cwriter"
16 "github.com/vbauerster/mpb/v6/decor"
12 "github.com/vbauerster/mpb/v8/cwriter"
13 "github.com/vbauerster/mpb/v8/decor"
1714 )
1815
19 const (
20 // default RefreshRate
21 prr = 120 * time.Millisecond
22 )
23
24 // Progress represents a container that renders one or more progress
25 // bars.
16 const defaultRefreshRate = 150 * time.Millisecond
17
18 // DoneError represents use after `(*Progress).Wait()` error.
19 var DoneError = fmt.Errorf("%T instance can't be reused after %[1]T.Wait()", (*Progress)(nil))
20
21 // Progress represents a container that renders one or more progress bars.
2622 type Progress struct {
23 uwg *sync.WaitGroup
24 pwg, bwg sync.WaitGroup
25 operateState chan func(*pState)
26 interceptIO chan func(io.Writer)
27 done <-chan struct{}
28 cancel func()
29 }
30
31 // pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
32 type pState struct {
2733 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
34 hm heapManager
35 dropS, dropD chan struct{}
36 renderReq chan time.Time
37 idCount int
38 popPriority int
4639
4740 // following are provided/overrided by user
48 idCount int
41 refreshRate time.Duration
4942 reqWidth int
5043 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
44 autoRefresh bool
45 delayRC <-chan struct{}
46 manualRC <-chan interface{}
47 shutdownNotifier chan<- interface{}
48 queueBars map[*Bar]*Bar
5849 output io.Writer
5950 debugOut io.Writer
51 uwg *sync.WaitGroup
6052 }
6153
6254 // New creates new Progress container instance. It's not possible to
63 // reuse instance after *Progress.Wait() method has been called.
55 // reuse instance after `(*Progress).Wait` method has been called.
6456 func New(options ...ContainerOption) *Progress {
6557 return NewWithContext(context.Background(), options...)
6658 }
6759
6860 // NewWithContext creates new Progress container instance with provided
69 // context. It's not possible to reuse instance after *Progress.Wait()
61 // context. It's not possible to reuse instance after `(*Progress).Wait`
7062 // method has been called.
7163 func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
64 if ctx == nil {
65 ctx = context.Background()
66 }
67 ctx, cancel := context.WithCancel(ctx)
7268 s := &pState{
73 bHeap: priorityQueue{},
74 rr: prr,
75 parkedBars: make(map[*Bar]*Bar),
76 output: os.Stdout,
77 debugOut: ioutil.Discard,
69 ctx: ctx,
70 hm: make(heapManager),
71 dropS: make(chan struct{}),
72 dropD: make(chan struct{}),
73 renderReq: make(chan time.Time),
74 popPriority: math.MinInt32,
75 refreshRate: defaultRefreshRate,
76 queueBars: make(map[*Bar]*Bar),
77 output: os.Stdout,
78 debugOut: io.Discard,
7879 }
7980
8081 for _, opt := range options {
8485 }
8586
8687 p := &Progress{
87 ctx: ctx,
8888 uwg: s.uwg,
89 cwg: new(sync.WaitGroup),
90 bwg: new(sync.WaitGroup),
9189 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))
90 interceptIO: make(chan func(io.Writer)),
91 cancel: cancel,
92 }
93
94 cw := cwriter.New(s.output)
95 if s.manualRC != nil {
96 done := make(chan struct{})
97 p.done = done
98 s.autoRefresh = false
99 go s.manualRefreshListener(done)
100 } else if cw.IsTerminal() || s.autoRefresh {
101 done := make(chan struct{})
102 p.done = done
103 s.autoRefresh = true
104 go s.autoRefreshListener(done)
105 } else {
106 p.done = ctx.Done()
107 s.autoRefresh = false
108 }
109
110 p.pwg.Add(1)
111 go p.serve(s, cw)
112 go s.hm.run()
98113 return p
99114 }
100115
101 // AddBar creates a bar with default bar filler. Different filler can
102 // be choosen and applied via `*Progress.Add(...) *Bar` method.
116 // AddBar creates a bar with default bar filler.
103117 func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
104 return p.Add(total, NewBarFiller(BarDefaultStyle), options...)
105 }
106
107 // AddSpinner creates a bar with default spinner filler. Different
108 // filler can be choosen and applied via `*Progress.Add(...) *Bar`
109 // method.
110 func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
111 return p.Add(total, NewSpinnerFiller(SpinnerDefaultStyle, alignment), options...)
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 {
118 return p.New(total, BarStyle(), options...)
119 }
120
121 // AddSpinner creates a bar with default spinner filler.
122 func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
123 return p.New(total, SpinnerStyle(), options...)
124 }
125
126 // New creates a bar by calling `Build` method on provided `BarFillerBuilder`.
127 func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
128 if builder == nil {
129 return p.MustAdd(total, nil, options...)
130 }
131 return p.MustAdd(total, builder.Build(), options...)
132 }
133
134 // MustAdd creates a bar which renders itself by provided BarFiller.
135 // If `total <= 0` triggering complete event by increment methods is
136 // disabled. Panics if called after `(*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 called after `(*Progress).Wait()` then
148 // `(nil, DoneError)` is returned.
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 } else if f, ok := filler.(BarFillerFunc); ok && f == nil {
153 filler = NopStyle().Build()
154 }
155 ch := make(chan *Bar)
123156 select {
124157 case p.operateState <- func(ps *pState) {
125158 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
159 bar := newBar(ps.ctx, p, bs)
160 if bs.waitBar != nil {
161 ps.queueBars[bs.waitBar] = bar
130162 } else {
131 heap.Push(&ps.bHeap, bar)
132 ps.heapUpdated = true
163 ps.hm.push(bar, true)
133164 }
134165 ps.idCount++
135 result <- bar
166 ch <- bar
136167 }:
137 bar := <-result
138 bar.subscribeDecorators()
139 return bar
168 return <-ch, nil
140169 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) {
170 return nil, DoneError
171 }
172 }
173
174 func (p *Progress) traverseBars(cb func(b *Bar) bool) {
175 iter, drop := make(chan *Bar), make(chan struct{})
147176 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
177 case p.operateState <- func(s *pState) { s.hm.iter(iter, drop) }:
178 for b := range iter {
179 if !cb(b) {
180 close(drop)
181 break
182 }
183 }
184 case <-p.done:
185 }
186 }
187
188 // UpdateBarPriority either immediately or lazy.
189 // With lazy flag order is updated after the next refresh cycle.
190 // If you don't care about laziness just use `(*Bar).SetPriority(int)`.
191 func (p *Progress) UpdateBarPriority(b *Bar, priority int, lazy bool) {
192 if b == nil {
193 return
194 }
195 select {
196 case p.operateState <- func(s *pState) { s.hm.fix(b, priority, lazy) }:
197 case <-p.done:
198 }
199 }
200
201 // Write is implementation of io.Writer.
202 // Writing to `*Progress` will print lines above a running bar.
203 // Writes aren't flushed immediately, but at next refresh cycle.
204 // If called after `(*Progress).Wait()` then `(0, DoneError)`
205 // is returned.
206 func (p *Progress) Write(b []byte) (int, error) {
207 type result struct {
208 n int
209 err error
210 }
211 ch := make(chan result)
212 select {
213 case p.interceptIO <- func(w io.Writer) {
214 n, err := w.Write(b)
215 ch <- result{n, err}
154216 }:
217 res := <-ch
218 return res.n, res.err
155219 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.
220 return 0, DoneError
221 }
222 }
223
224 // Wait waits for all bars to complete and finally shutdowns container. After
225 // this method has been called, there is no way to reuse `*Progress` instance.
191226 func (p *Progress) Wait() {
227 p.bwg.Wait()
228 p.Shutdown()
229 // wait for user wg, if any
192230 if p.uwg != nil {
193 // wait for user wg
194231 p.uwg.Wait()
195232 }
196
197 // wait for bars to quit, if any
198 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)
233 }
234
235 // Shutdown cancels any running bar immediately and then shutdowns `*Progress`
236 // instance. Normally this method shouldn't be called unless you know what you
237 // are doing. Proper way to shutdown is to call `(*Progress).Wait()` instead.
238 func (p *Progress) Shutdown() {
239 p.cancel()
240 p.pwg.Wait()
208241 }
209242
210243 func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
211 defer p.cwg.Done()
212
213 p.refreshCh = s.newTicker(p.done)
244 defer p.pwg.Done()
245 var err error
246 var w *cwriter.Writer
247 renderReq := s.renderReq
248 operateState := p.operateState
249 interceptIO := p.interceptIO
250
251 if s.delayRC != nil {
252 w = cwriter.New(io.Discard)
253 } else {
254 w, cw = cw, nil
255 }
214256
215257 for {
216258 select {
217 case op := <-p.operateState:
259 case <-s.delayRC:
260 w, cw = cw, nil
261 s.delayRC = nil
262 case op := <-operateState:
218263 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)
264 case fn := <-interceptIO:
265 fn(w)
266 case <-renderReq:
267 err = s.render(w)
268 if err != nil {
269 // (*pState).(autoRefreshListener|manualRefreshListener) may block
270 // if not launching following short lived goroutine
271 go func() {
272 for {
273 select {
274 case <-s.renderReq:
275 case <-p.done:
276 return
277 }
278 }
279 }()
280 p.cancel() // cancel all bars
281 renderReq = nil
282 operateState = nil
283 interceptIO = nil
284 }
285 case <-p.done:
286 if err != nil {
287 _, _ = fmt.Fprintln(s.debugOut, err.Error())
288 } else if s.autoRefresh {
289 update := make(chan bool)
290 for i := 0; i == 0 || <-update; i++ {
291 if err := s.render(w); err != nil {
292 _, _ = fmt.Fprintln(s.debugOut, err.Error())
293 break
294 }
295 s.hm.state(update)
227296 }
228297 }
298 s.hm.end(s.shutdownNotifier)
229299 return
230300 }
231301 }
232302 }
233303
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 }
304 func (s *pState) autoRefreshListener(done chan struct{}) {
305 ticker := time.NewTicker(s.refreshRate)
306 defer ticker.Stop()
307 for {
308 select {
309 case t := <-ticker.C:
310 s.renderReq <- t
311 case <-s.ctx.Done():
312 close(done)
313 return
314 }
315 }
316 }
317
318 func (s *pState) manualRefreshListener(done chan struct{}) {
319 for {
320 select {
321 case x := <-s.manualRC:
322 if t, ok := x.(time.Time); ok {
323 s.renderReq <- t
324 } else {
325 s.renderReq <- time.Now()
326 }
327 case <-s.ctx.Done():
328 close(done)
329 return
330 }
331 }
332 }
333
334 func (s *pState) render(cw *cwriter.Writer) (err error) {
335 s.hm.sync(s.dropS)
336 iter := make(chan *Bar)
337 go s.hm.iter(iter, s.dropS)
338
339 var width, height int
340 if cw.IsTerminal() {
341 width, height, err = cw.GetTermSize()
342 if err != nil {
343 close(s.dropS)
344 return err
345 }
346 } else {
347 if s.reqWidth > 0 {
348 width = s.reqWidth
250349 } 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()
262 }
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
350 width = 80
351 }
352 height = width
353 }
354
355 for b := range iter {
356 go b.render(width)
357 }
358
359 return s.flush(cw, height)
360 }
361
362 func (s *pState) flush(cw *cwriter.Writer, height int) error {
363 var wg sync.WaitGroup
364 defer wg.Wait() // waiting for all s.push to complete
365
366 var popCount int
367 var rows []io.Reader
368
369 iter := make(chan *Bar)
370 s.hm.drain(iter, s.dropD)
371
372 for b := range iter {
373 frame := <-b.frameCh
374 if frame.err != nil {
375 close(s.dropD)
376 b.cancel()
377 return frame.err // b.frameCh is buffered it's ok to return here
378 }
379 var usedRows int
380 for i := len(frame.rows) - 1; i >= 0; i-- {
381 if row := frame.rows[i]; len(rows) < height {
382 rows = append(rows, row)
383 usedRows++
302384 } 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 {
385 _, _ = io.Copy(io.Discard, row)
386 }
387 }
388
389 switch frame.shutdown {
390 case 1:
391 b.cancel()
392 if qb, ok := s.queueBars[b]; ok {
393 delete(s.queueBars, b)
394 qb.priority = b.priority
395 wg.Add(1)
396 go s.push(&wg, qb, true)
397 } else if s.popCompleted && !frame.noPop {
398 b.priority = s.popPriority
399 s.popPriority++
400 wg.Add(1)
401 go s.push(&wg, b, false)
402 } else if !frame.rmOnComplete {
403 wg.Add(1)
404 go s.push(&wg, b, false)
405 }
406 case 2:
407 if s.popCompleted && !frame.noPop {
408 popCount += usedRows
409 continue
410 }
411 fallthrough
412 default:
413 wg.Add(1)
414 go s.push(&wg, b, false)
415 }
416 }
417
418 for i := len(rows) - 1; i >= 0; i-- {
419 _, err := cw.ReadFrom(rows[i])
420 if err != nil {
421 return err
422 }
423 }
424
425 return cw.Flush(len(rows) - popCount)
426 }
427
428 func (s *pState) push(wg *sync.WaitGroup, b *Bar, sync bool) {
429 s.hm.push(b, sync)
430 wg.Done()
431 }
432
433 func (s pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
359434 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,
435 id: s.idCount,
436 priority: s.idCount,
437 reqWidth: s.reqWidth,
438 total: total,
439 filler: filler,
440 renderReq: s.renderReq,
441 autoRefresh: s.autoRefresh,
442 extender: func(_ decor.Statistics, rows ...io.Reader) ([]io.Reader, error) {
443 return rows, nil
444 },
367445 }
368446
369447 if total > 0 {
376454 }
377455 }
378456
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))
457 bs.buffers[0] = bytes.NewBuffer(make([]byte, 0, 128)) // prepend
458 bs.buffers[1] = bytes.NewBuffer(make([]byte, 0, 128)) // append
459 bs.buffers[2] = bytes.NewBuffer(make([]byte, 0, 256)) // filler
391460
392461 return bs
393462 }
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/v6"
12 "github.com/vbauerster/mpb/v6/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 io.NopCloser(r)
8972 }
22 import (
33 "bytes"
44 "io"
5 "io/ioutil"
65 "strings"
76 "testing"
87
9 "github.com/vbauerster/mpb/v6"
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 }