Codebase list golang-github-vbauerster-mpb / e1288b3
New upstream version 8.6.1 Reinhard Tartler 2 years ago
106 changed file(s) with 3934 addition(s) and 2618 deletion(s). Raw diff Collapse all Expand all
0 name: golangci-lint
1
2 on:
3 push:
4 tags:
5 - v*
6 branches:
7 - master
8 - main
9 pull_request:
10
11 permissions:
12 contents: read
13 # Optional: allow read access to pull request. Use with `only-new-issues` option.
14 pull-requests: read
15
16 jobs:
17 golangci:
18 strategy:
19 matrix:
20 go-version: ['stable']
21 os: [ubuntu-latest, macos-latest]
22 runs-on: ${{ matrix.os }}
23 steps:
24 - uses: actions/checkout@v3
25 - uses: actions/setup-go@v4
26 with:
27 go-version: ${{ matrix.go-version }}
28 cache: false
29 - uses: golangci/golangci-lint-action@v3
30 with:
31 # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
32 version: latest
33 # Optional: working directory, useful for monorepos
34 # working-directory: somedir
35
36 # Optional: golangci-lint command line arguments.
37 # args: --issues-exit-code=0
38
39 # Optional: show only new issues if it's a pull request. The default value is `false`.
40 only-new-issues: true
0 name: Test
0 name: test
11
2 on: [push, pull_request]
2 on:
3 push:
4 tags:
5 - v*
6 branches:
7 - master
8 - v*
9 pull_request:
10
11 permissions:
12 contents: read
13 pull-requests: read
314
415 jobs:
516 test:
617 strategy:
718 matrix:
8 go-version: [1.16, 1.17]
19 go-version: ['stable', 'oldstable']
920 os: [ubuntu-latest, macos-latest, windows-latest]
1021 runs-on: ${{ matrix.os }}
1122 steps:
12 - name: Setup Go
13 uses: actions/setup-go@v2
23 - uses: actions/checkout@v3
24 - uses: actions/setup-go@v4
1425 with:
1526 go-version: ${{ matrix.go-version }}
16 - name: Checkout code
17 uses: actions/checkout@v2
18 - uses: actions/cache@v2
19 with:
20 # In order:
21 # * Module download cache
22 # * Build cache (Linux)
23 # * Build cache (Mac)
24 # * Build cache (Windows)
25 path: |
26 ~/go/pkg/mod
27 ~/.cache/go-build
28 ~/Library/Caches/go-build
29 %LocalAppData%\go-build
30 key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
31 restore-keys: |
32 ${{ runner.os }}-go-
33 - name: Test
34 run: go test -race ./...
27 - run: go test -race ./...
0 When contributing your first changes, please include an empty commit for
1 copyright waiver using the following message (replace 'John Doe' with
2 your name or nickname):
3
4 John Doe Copyright Waiver
5
6 I dedicate any and all copyright interest in this software to the
7 public domain. I make this dedication for the benefit of the public at
8 large and to the detriment of my heirs and successors. I intend this
9 dedication to be an overt act of relinquishment in perpetuity of all
10 present and future rights to this software under copyright law.
11
12 The command to create an empty commit from the command-line is:
13
14 git commit --allow-empty
00 # Multi Progress Bar
11
2 [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
2 [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v8)
33 [![Test status](https://github.com/vbauerster/mpb/actions/workflows/test.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
4 [![Donate with PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/vbauerster)
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/v7"
29 "github.com/vbauerster/mpb/v7/decor"
28 "github.com/vbauerster/mpb/v8"
29 "github.com/vbauerster/mpb/v8/decor"
3030 )
3131
3232 func main() {
8181 mpb.AppendDecorators(
8282 // replace ETA decorator with "done" message, OnComplete event
8383 decor.OnComplete(
84 // ETA decorator with ewma age of 60
85 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncWidth), "done",
84 // ETA decorator with ewma age of 30
85 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
8686 ),
8787 ),
8888 )
9696 // EWMA's unit of measure is an iteration's duration
9797 start := time.Now()
9898 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))
99 // we need to call EwmaIncrement to fulfill ewma decorator's contract
100 bar.EwmaIncrement(time.Since(start))
102101 }
103102 }()
104103 }
00 module github.com/vbauerster/mpb/_examples/barExtender
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
66 "sync"
77 "time"
88
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
9 "github.com/vbauerster/mpb/v8"
10 "github.com/vbauerster/mpb/v8/decor"
1111 )
1212
1313 func main() {
1919
2020 for i := 0; i < numBars; i++ {
2121 name := fmt.Sprintf("Bar#%d:", i)
22 efn := func(w io.Writer, _ int, s decor.Statistics) {
22 efn := func(w io.Writer, s decor.Statistics) (err error) {
2323 if s.Completed {
24 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)
2525 }
26 return err
2627 }
27 bar := p.AddBar(int64(total), mpb.BarExtender(mpb.BarFillerFunc(efn)),
28 bar := p.AddBar(int64(total),
29 mpb.BarExtender(mpb.BarFillerFunc(efn), false),
2830 mpb.PrependDecorators(
2931 // simple name decorator
3032 decor.Name(name),
3436 mpb.AppendDecorators(
3537 // replace ETA decorator with "done" message, OnComplete event
3638 decor.OnComplete(
37 // ETA decorator with ewma age of 60
38 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
39 // ETA decorator with ewma age of 30
40 decor.EwmaETA(decor.ET_STYLE_GO, 30), "done",
3941 ),
4042 ),
4143 )
4951 // EWMA's unit of measure is an iteration's duration
5052 start := time.Now()
5153 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
52 bar.Increment()
53 // since EWMA based decorator is used, DecoratorEwmaUpdate should be called
54 bar.DecoratorEwmaUpdate(time.Since(start))
54 // we need to call EwmaIncrement to fulfill ewma decorator's contract
55 bar.EwmaIncrement(time.Since(start))
5556 }
5657 }()
5758 }
0 module github.com/vbauerster/mpb/_examples/barExtenderRev
1
2 go 1.17
3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
0 package main
1
2 import (
3 "fmt"
4 "io"
5 "math/rand"
6 "sync/atomic"
7 "time"
8
9 "github.com/vbauerster/mpb/v8"
10 "github.com/vbauerster/mpb/v8/decor"
11 )
12
13 var curTask uint32
14 var doneTasks uint32
15
16 type task struct {
17 id uint32
18 total int64
19 bar *mpb.Bar
20 }
21
22 func main() {
23 numTasks := 4
24
25 var total int64
26 var filler mpb.BarFiller
27 tasks := make([]*task, numTasks)
28
29 for i := 0; i < numTasks; i++ {
30 task := &task{
31 id: uint32(i),
32 total: rand.Int63n(666) + 100,
33 }
34 total += task.total
35 filler = middleware(filler, task.id)
36 tasks[i] = task
37 }
38
39 filler = newLineMiddleware(filler)
40
41 p := mpb.New()
42
43 for i := 0; i < numTasks; i++ {
44 bar := p.AddBar(tasks[i].total,
45 mpb.BarExtender(filler, true), // all bars share same extender filler
46 mpb.BarFuncOptional(func() mpb.BarOption {
47 return mpb.BarQueueAfter(tasks[i-1].bar)
48 }, i != 0),
49 mpb.PrependDecorators(
50 decor.Name("current:", decor.WCSyncWidthR),
51 ),
52 mpb.AppendDecorators(
53 decor.Percentage(decor.WCSyncWidth),
54 ),
55 )
56 tasks[i].bar = bar
57 }
58
59 tb := p.AddBar(0,
60 mpb.PrependDecorators(
61 decor.Any(func(st decor.Statistics) string {
62 return fmt.Sprintf("TOTAL(%d/%d)", atomic.LoadUint32(&doneTasks), len(tasks))
63 }, decor.WCSyncWidthR),
64 ),
65 mpb.AppendDecorators(
66 decor.Percentage(decor.WCSyncWidth),
67 ),
68 )
69
70 tb.SetTotal(total, false)
71
72 for _, t := range tasks {
73 atomic.StoreUint32(&curTask, t.id)
74 complete(t.bar, tb)
75 atomic.AddUint32(&doneTasks, 1)
76 }
77
78 tb.EnableTriggerComplete()
79
80 p.Wait()
81 }
82
83 func middleware(base mpb.BarFiller, id uint32) mpb.BarFiller {
84 var done bool
85 fn := func(w io.Writer, st decor.Statistics) error {
86 if !done {
87 if atomic.LoadUint32(&curTask) != id {
88 _, err := fmt.Fprintf(w, " Taksk %02d\n", id)
89 return err
90 }
91 if !st.Completed {
92 _, err := fmt.Fprintf(w, "=> Taksk %02d\n", id)
93 return err
94 }
95 done = true
96 }
97 _, err := fmt.Fprintf(w, " Taksk %02d: Done!\n", id)
98 return err
99 }
100 if base == nil {
101 return mpb.BarFillerFunc(fn)
102 }
103 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
104 err := fn(w, st)
105 if err != nil {
106 return err
107 }
108 return base.Fill(w, st)
109 })
110 }
111
112 func newLineMiddleware(base mpb.BarFiller) mpb.BarFiller {
113 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
114 _, err := fmt.Fprintln(w)
115 if err != nil {
116 return err
117 }
118 return base.Fill(w, st)
119 })
120 }
121
122 func complete(bar, totalBar *mpb.Bar) {
123 max := 100 * time.Millisecond
124 for !bar.Completed() {
125 n := rand.Int63n(10) + 1
126 incrementBars(n, bar, totalBar)
127 time.Sleep(time.Duration(n) * max / 10)
128 }
129 bar.Wait()
130 }
131
132 func incrementBars(n int64, bb ...*mpb.Bar) {
133 for _, b := range bb {
134 b.IncrInt64(n)
135 }
136 }
00 module github.com/vbauerster/mpb/_examples/cancel
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
66 "sync"
77 "time"
88
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
9 "github.com/vbauerster/mpb/v8"
10 "github.com/vbauerster/mpb/v8/decor"
1111 )
1212
1313 func main() {
2222 wg.Add(numBars)
2323
2424 for i := 0; i < numBars; i++ {
25 name := fmt.Sprintf("Bar#%d:", i)
25 name := fmt.Sprintf("Bar#%02d: ", i)
2626 bar := p.AddBar(int64(total),
2727 mpb.PrependDecorators(
28 decor.Name(name),
29 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),
3030 ),
3131 mpb.AppendDecorators(
3232 // note that OnComplete will not be fired, because of cancel
3838 defer wg.Done()
3939 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
4040 max := 100 * time.Millisecond
41 for !bar.Completed() {
41 for bar.IsRunning() {
4242 // start variable is solely for EWMA calculation
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 // since EWMA based decorator is used, DecoratorEwmaUpdate should be called
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 }
00 module github.com/vbauerster/mpb/_examples/complex
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require (
5 github.com/fatih/color v1.15.0
6 github.com/vbauerster/mpb/v8 v8.6.1
7 )
8
9 require (
10 github.com/VividCortex/ewma v1.2.0 // indirect
11 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
12 github.com/mattn/go-colorable v0.1.13 // indirect
13 github.com/mattn/go-isatty v0.0.19 // indirect
14 github.com/mattn/go-runewidth v0.0.15 // indirect
15 github.com/rivo/uniseg v0.4.4 // indirect
16 golang.org/x/sys v0.11.0 // indirect
17 )
22 import (
33 "fmt"
44 "math/rand"
5 "sync"
65 "time"
76
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/fatih/color"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
12 func init() {
13 rand.Seed(time.Now().UnixNano())
14 }
12 func main() {
13 numBars := 4
14 // to support color in Windows following both options are required
15 p := mpb.New(
16 mpb.WithOutput(color.Output),
17 mpb.WithAutoRefresh(),
18 )
1519
16 func main() {
17 doneWg := new(sync.WaitGroup)
18 // passed doneWg will be accounted at p.Wait() call
19 p := mpb.New(mpb.WithWaitGroup(doneWg))
20 numBars := 4
20 red, green := color.New(color.FgRed), color.New(color.FgGreen)
2121
22 var bars []*mpb.Bar
23 var downloadWgg []*sync.WaitGroup
2422 for i := 0; i < numBars; i++ {
25 wg := new(sync.WaitGroup)
26 wg.Add(1)
27 downloadWgg = append(downloadWgg, wg)
2823 task := fmt.Sprintf("Task#%02d:", i)
29 job := "downloading"
30 b := p.AddBar(rand.Int63n(201)+100,
24 queue := make([]*mpb.Bar, 2)
25 queue[0] = p.AddBar(rand.Int63n(201)+100,
3126 mpb.PrependDecorators(
3227 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
33 decor.Name(job, decor.WCSyncSpaceR),
28 decor.Name("downloading", decor.WCSyncSpaceR),
3429 decor.CountersNoUnit("%d / %d", decor.WCSyncWidth),
3530 ),
36 mpb.AppendDecorators(decor.Percentage(decor.WC{W: 5})),
31 mpb.AppendDecorators(
32 decor.OnComplete(decor.Percentage(decor.WC{W: 5}), "done"),
33 ),
3734 )
38 go newTask(wg, b, i+1)
39 bars = append(bars, b)
35 queue[1] = p.AddBar(rand.Int63n(101)+100,
36 mpb.BarQueueAfter(queue[0]), // this bar is queued
37 mpb.BarFillerClearOnComplete(),
38 mpb.PrependDecorators(
39 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
40 decor.OnCompleteMeta(
41 decor.OnComplete(
42 decor.Meta(decor.Name("installing", decor.WCSyncSpaceR), toMetaFunc(red)),
43 "done!",
44 ),
45 toMetaFunc(green),
46 ),
47 decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_MMSS, 0, decor.WCSyncWidth), ""),
48 ),
49 mpb.AppendDecorators(
50 decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""),
51 ),
52 )
53
54 go func() {
55 for _, b := range queue {
56 complete(b)
57 }
58 }()
4059 }
4160
42 for i := 0; i < numBars; i++ {
43 doneWg.Add(1)
44 i := i
45 go func() {
46 task := fmt.Sprintf("Task#%02d:", i)
47 // ANSI escape sequences are not supported on Windows OS
48 job := "\x1b[31;1;4mつのだ☆HIRO\x1b[0m"
49 // preparing delayed bars
50 b := p.AddBar(rand.Int63n(101)+100,
51 mpb.BarQueueAfter(bars[i]),
52 mpb.BarFillerClearOnComplete(),
53 mpb.PrependDecorators(
54 decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
55 decor.OnComplete(decor.Name(job, decor.WCSyncSpaceR), "done!"),
56 decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_MMSS, 0, decor.WCSyncWidth), ""),
57 ),
58 mpb.AppendDecorators(
59 decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""),
60 ),
61 )
62 // waiting for download to complete, before starting install job
63 downloadWgg[i].Wait()
64 go newTask(doneWg, b, numBars-i)
65 }()
66 }
67 // wait for passed doneWg and for all bars to complete and flush
6861 p.Wait()
6962 }
7063
71 func newTask(wg *sync.WaitGroup, bar *mpb.Bar, incrBy int) {
72 defer wg.Done()
64 func complete(bar *mpb.Bar) {
7365 max := 100 * time.Millisecond
7466 for !bar.Completed() {
7567 // start variable is solely for EWMA calculation
7668 // EWMA's unit of measure is an iteration's duration
7769 start := time.Now()
7870 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
79 bar.IncrBy(incrBy)
80 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
81 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))
8273 }
8374 }
75
76 func toMetaFunc(c *color.Color) func(string) string {
77 return func(s string) string {
78 return c.Sprint(s)
79 }
80 }
00 module github.com/vbauerster/mpb/_examples/decoratorsOnTop
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v7"
8 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v8"
8 "github.com/vbauerster/mpb/v8/decor"
99 )
1010
1111 func main() {
1414 total := 100
1515 bar := p.New(int64(total),
1616 mpb.NopStyle(), // make main bar style nop, so there are just decorators
17 mpb.BarExtender(extended(mpb.BarStyle())), // extend wtih normal bar on the next line
17 mpb.BarExtender(extended(mpb.BarStyle().Build()), false), // extend wtih normal bar on the next line
1818 mpb.PrependDecorators(
1919 decor.Name("Percentage: "),
2020 decor.NewPercentage("%d"),
3636 p.Wait()
3737 }
3838
39 func extended(builder mpb.BarFillerBuilder) mpb.BarFiller {
40 filler := builder.Build()
41 return mpb.BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
42 filler.Fill(w, reqWidth, st)
43 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
4447 })
4548 }
00 module github.com/vbauerster/mpb/_examples/differentWidth
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
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 }
00 module github.com/vbauerster/mpb/_examples/dynTotal
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v7"
8 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v8"
8 "github.com/vbauerster/mpb/v8/decor"
99 )
10
11 func init() {
12 rand.Seed(time.Now().UnixNano())
13 }
1410
1511 func main() {
1612 p := mpb.New(mpb.WithWidth(64))
1713
18 var total int64
1914 // new bar with 'trigger complete event' disabled, because total is zero
20 bar := p.AddBar(total,
21 mpb.PrependDecorators(decor.Counters(decor.UnitKiB, "% .1f / % .1f")),
15 bar := p.AddBar(0,
16 mpb.PrependDecorators(decor.Counters(decor.SizeB1024(0), "% .1f / % .1f")),
2217 mpb.AppendDecorators(decor.Percentage()),
2318 )
2419
2621 read := makeStream(200)
2722 for {
2823 n, err := read()
29 total += int64(n)
3024 if err == io.EOF {
25 // triggering complete event now
26 bar.SetTotal(-1, true)
3127 break
3228 }
33 // while total is unknown,
34 // set it to a positive number which is greater than current total,
35 // to make sure no complete event is triggered by next IncrBy call.
36 bar.SetTotal(total+2048, false)
29 // increment methods won't trigger complete event because bar was constructed with total = 0
3730 bar.IncrBy(n)
31 // following call is not required, it's called to show some progress instead of an empty bar
32 bar.SetTotal(bar.Current()+2048, false)
3833 time.Sleep(time.Duration(rand.Intn(10)+1) * maxSleep / 10)
3934 }
40
41 // force bar complete event, note true flag
42 bar.SetTotal(total, true)
4335
4436 p.Wait()
4537 }
0 #!/bin/sh
1
2 for d in *; do
3 [ ! -d "$d" ] && continue
4 pushd "$d" >/dev/null 2>&1
5 go mod tidy
6 popd >/dev/null 2>&1
7 done
00 module github.com/vbauerster/mpb/_examples/io
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "io/ioutil"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
2121 bar := p.New(total,
2222 mpb.BarStyle().Rbound("|"),
2323 mpb.PrependDecorators(
24 decor.CountersKibiByte("% .2f / % .2f"),
24 decor.Counters(decor.SizeB1024(0), "% .2f / % .2f"),
2525 ),
2626 mpb.AppendDecorators(
27 decor.EwmaETA(decor.ET_STYLE_GO, 90),
27 decor.EwmaETA(decor.ET_STYLE_GO, 30),
2828 decor.Name(" ] "),
29 decor.EwmaSpeed(decor.UnitKiB, "% .2f", 60),
29 decor.EwmaSpeed(decor.SizeB1024(0), "% .2f", 30),
3030 ),
3131 )
3232
3535 defer proxyReader.Close()
3636
3737 // copy from proxyReader, ignoring errors
38 io.Copy(ioutil.Discard, proxyReader)
38 _, _ = io.Copy(ioutil.Discard, proxyReader)
3939
4040 p.Wait()
4141 }
+0
-5
_examples/merge/go.mod less more
0 module github.com/vbauerster/mpb/_examples/merge
1
2 go 1.14
3
4 require github.com/vbauerster/mpb/v7 v7.3.2
+0
-72
_examples/merge/main.go less more
0 package main
1
2 import (
3 "math/rand"
4 "strings"
5 "sync"
6 "time"
7
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
10 )
11
12 func main() {
13 var wg sync.WaitGroup
14 // passed wg will be accounted at p.Wait() call
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 // wait for passed wg and for all bars to complete and flush
62 p.Wait()
63 }
64
65 func newVariadicSpinner(wc decor.WC) decor.Decorator {
66 spinner := decor.Spinner(nil)
67 fn := func(s decor.Statistics) string {
68 return strings.Repeat(spinner.Decor(s), int(s.Current/3))
69 }
70 return decor.Any(fn, wc)
71 }
00 module github.com/vbauerster/mpb/_examples/mexicanBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
33 "math/rand"
44 "time"
55
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
6 "github.com/vbauerster/mpb/v8"
7 "github.com/vbauerster/mpb/v8/decor"
88 )
99
1010 func main() {
1414 total := 100
1515 name := "Complex Filler:"
1616 bs := mpb.BarStyle()
17 bs.Lbound("[\u001b[36;1m")
18 bs.Filler("_")
19 bs.Tip("\u001b[0m⛵\u001b[36;1m")
20 bs.Padding("_")
21 bs.Rbound("\u001b[0m]")
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 })
2233 bar := p.New(int64(total), bs,
2334 mpb.PrependDecorators(decor.Name(name)),
2435 mpb.AppendDecorators(decor.Percentage()),
00 module github.com/vbauerster/mpb/_examples/multiBars
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
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, decor.WCSyncWidth), "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 }
+0
-5
_examples/panic/go.mod less more
0 module github.com/vbauerster/mpb/_examples/panic
1
2 go 1.14
3
4 require github.com/vbauerster/mpb/v7 v7.3.2
+0
-49
_examples/panic/main.go less more
0 package main
1
2 import (
3 "fmt"
4 "os"
5 "strings"
6 "sync"
7 "time"
8
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
11 )
12
13 func main() {
14 var wg sync.WaitGroup
15 // passed wg will be accounted at p.Wait() call
16 p := mpb.New(
17 mpb.WithWaitGroup(&wg),
18 mpb.WithDebugOutput(os.Stderr),
19 )
20
21 wantPanic := strings.Repeat("Panic ", 64)
22 numBars := 3
23 wg.Add(numBars)
24
25 for i := 0; i < numBars; i++ {
26 name := fmt.Sprintf("b#%02d:", i)
27 bar := p.AddBar(100, mpb.BarID(i), mpb.PrependDecorators(panicDecorator(name, wantPanic)))
28
29 go func() {
30 defer wg.Done()
31 for i := 0; i < 100; i++ {
32 time.Sleep(50 * time.Millisecond)
33 bar.Increment()
34 }
35 }()
36 }
37 // wait for passed wg and for all bars to complete and flush
38 p.Wait()
39 }
40
41 func panicDecorator(name, panicMsg string) decor.Decorator {
42 return decor.Any(func(st decor.Statistics) string {
43 if st.ID == 1 && st.Current >= 42 {
44 panic(panicMsg)
45 }
46 return name
47 })
48 }
00 module github.com/vbauerster/mpb/_examples/poplog
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
44 "math/rand"
55 "time"
66
7 "github.com/vbauerster/mpb/v7"
8 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v8"
8 "github.com/vbauerster/mpb/v8/decor"
99 )
1010
1111 func main() {
12 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
1213 p := mpb.New(mpb.PopCompletedMode())
13
1414 total, numBars := 100, 4
1515 for i := 0; i < numBars; i++ {
1616 name := fmt.Sprintf("Bar#%d:", i)
2727 ),
2828 )
2929 // simulating some work
30 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
3130 max := 100 * time.Millisecond
3231 for i := 0; i < total; i++ {
3332 // start variable is solely for EWMA calculation
3433 // EWMA's unit of measure is an iteration's duration
3534 start := time.Now()
3635 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
37 bar.Increment()
38 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
39 bar.DecoratorEwmaUpdate(time.Since(start))
36 // we need to call EwmaIncrement to fulfill ewma decorator's contract
37 bar.EwmaIncrement(time.Since(start))
4038 }
4139 }
4240
0 module github.com/vbauerster/mpb/_examples/progressAsWriter
1
2 go 1.17
3
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
0 package main
1
2 import (
3 "fmt"
4 "log"
5 "math/rand"
6 "sync"
7 "time"
8
9 "github.com/vbauerster/mpb/v8"
10 "github.com/vbauerster/mpb/v8/decor"
11 )
12
13 func main() {
14 total, numBars := 100, 2
15 var wg sync.WaitGroup
16 wg.Add(numBars)
17 done := make(chan interface{})
18 p := mpb.New(
19 mpb.WithWidth(64),
20 mpb.WithWaitGroup(&wg),
21 mpb.WithShutdownNotifier(done),
22 )
23
24 log.SetOutput(p)
25
26 for i := 0; i < numBars; i++ {
27 name := fmt.Sprintf("Bar#%d:", i)
28 bar := p.AddBar(int64(total),
29 mpb.PrependDecorators(
30 decor.Name(name),
31 decor.Percentage(decor.WCSyncSpace),
32 ),
33 mpb.AppendDecorators(
34 decor.OnComplete(
35 decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
36 ),
37 ),
38 )
39 // simulating some work
40 go func() {
41 defer wg.Done()
42 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
43 max := 100 * time.Millisecond
44 for i := 0; i < total; i++ {
45 // start variable is solely for EWMA calculation
46 // EWMA's unit of measure is an iteration's duration
47 start := time.Now()
48 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
49 // we need to call EwmaIncrement to fulfill ewma decorator's contract
50 bar.EwmaIncrement(time.Since(start))
51 }
52 log.Println(name, "done")
53 }()
54 }
55
56 var qwg sync.WaitGroup
57 qwg.Add(1)
58 go func() {
59 quit:
60 for {
61 select {
62 case <-done:
63 // after done, underlying io.Writer returns mpb.DoneError
64 // so following isn't printed
65 log.Println("all done")
66 break quit
67 default:
68 log.Println("waiting for done")
69 time.Sleep(100 * time.Millisecond)
70 }
71 }
72 qwg.Done()
73 }()
74
75 p.Wait()
76 qwg.Wait()
77 }
00 module github.com/vbauerster/mpb/_examples/quietMode
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
22 import (
33 "flag"
44 "fmt"
5 "io"
56 "math/rand"
67 "sync"
78 "time"
89
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
10 "github.com/vbauerster/mpb/v8"
11 "github.com/vbauerster/mpb/v8/decor"
1112 )
1213
1314 var quietMode bool
2223 // 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 }
00 module github.com/vbauerster/mpb/_examples/remove
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
4040 defer wg.Done()
4141 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
4242 max := 100 * time.Millisecond
43 for i := 0; !bar.Completed(); i++ {
43 for i := 0; bar.IsRunning(); i++ {
4444 if bar.ID() == 2 && i >= 42 {
45 bar.Abort(false)
45 go bar.Abort(false)
4646 }
4747 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
4848 bar.Increment()
00 module github.com/vbauerster/mpb/_examples/reverseBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
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.New(int64(total), condBuilder(i == 1),
28 bar := p.New(int64(total),
29 condFillerBuilder(i == 1),
2230 mpb.PrependDecorators(
2331 // simple name decorator
2432 decor.Name(name),
2836 mpb.AppendDecorators(
2937 // replace ETA decorator with "done" message, OnComplete event
3038 decor.OnComplete(
31 // ETA decorator with ewma age of 60
32 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",
3341 ),
3442 ),
3543 )
4351 // EWMA's unit of measure is an iteration's duration
4452 start := time.Now()
4553 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))
54 // we need to call EwmaIncrement to fulfill ewma decorator's contract
55 bar.EwmaIncrement(time.Since(start))
4956 }
5057 }()
5158 }
5259 // wait for passed wg and for all bars to complete and flush
5360 p.Wait()
5461 }
55
56 func condBuilder(cond bool) mpb.BarFillerBuilder {
57 return mpb.BarFillerBuilderFunc(func() mpb.BarFiller {
58 bs := mpb.BarStyle()
59 if cond {
60 // reverse Bar on cond
61 bs = bs.Tip("<").Reverse()
62 }
63 return bs.Build()
64 })
65 }
00 module github.com/vbauerster/mpb/_examples/singleBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
33 "math/rand"
44 "time"
55
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
6 "github.com/vbauerster/mpb/v8"
7 "github.com/vbauerster/mpb/v8/decor"
88 )
99
1010 func main() {
00 module github.com/vbauerster/mpb/_examples/spinTipBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
33 "math/rand"
44 "time"
55
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
6 "github.com/vbauerster/mpb/v8"
7 "github.com/vbauerster/mpb/v8/decor"
88 )
99
1010 func main() {
1414 total := 100
1515 name := "Single Bar:"
1616 bar := p.New(int64(total),
17 mpb.BarStyle().Tip(`-`, `\`, `|`, `/`),
17 mpb.BarStyle().Tip(`-`, `\`, `|`, `/`).TipMeta(func(s string) string {
18 return "\033[31m" + s + "\033[0m" // red
19 }),
1820 mpb.PrependDecorators(decor.Name(name)),
1921 mpb.AppendDecorators(decor.Percentage()),
2022 )
00 module github.com/vbauerster/mpb/_examples/spinnerBar
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
1919 total, numBars := 101, 3
2020 wg.Add(numBars)
2121
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 }
31
2232 for i := 0; i < numBars; i++ {
2333 name := fmt.Sprintf("Bar#%d:", i)
24 bar := p.New(int64(total), condBuilder(i != 0),
34 bar := p.New(int64(total),
35 condFillerBuilder(i != 0),
2536 mpb.PrependDecorators(
2637 // simple name decorator
2738 decor.Name(name),
2940 mpb.AppendDecorators(
3041 // replace ETA decorator with "done" message, OnComplete event
3142 decor.OnComplete(
32 // ETA decorator with ewma age of 60
33 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",
3445 ),
3546 ),
3647 )
4455 // EWMA's unit of measure is an iteration's duration
4556 start := time.Now()
4657 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
47 bar.Increment()
48 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
49 bar.DecoratorEwmaUpdate(time.Since(start))
58 // we need to call EwmaIncrement to fulfill ewma decorator's contract
59 bar.EwmaIncrement(time.Since(start))
5060 }
5161 }()
5262 }
5363 // wait for passed wg and for all bars to complete and flush
5464 p.Wait()
5565 }
56
57 func condBuilder(cond bool) mpb.BarFillerBuilder {
58 return mpb.BarFillerBuilderFunc(func() mpb.BarFiller {
59 if cond {
60 // spinner Bar on cond
61 frames := []string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"}
62 return mpb.SpinnerStyle(frames...).Build()
63 }
64 return mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟").Build()
65 })
66 }
00 module github.com/vbauerster/mpb/_examples/spinnerDecorator
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
55 "sync"
66 "time"
77
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1010 )
1111
1212 func main() {
00 module github.com/vbauerster/mpb/_examples/stress
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require (
5 github.com/pkg/profile v1.7.0
6 github.com/vbauerster/mpb/v8 v8.6.1
7 )
8
9 require (
10 github.com/VividCortex/ewma v1.2.0 // indirect
11 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
12 github.com/felixge/fgprof v0.9.3 // indirect
13 github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
14 github.com/mattn/go-runewidth v0.0.15 // indirect
15 github.com/rivo/uniseg v0.4.4 // indirect
16 golang.org/x/sys v0.11.0 // indirect
17 )
00 package main
11
22 import (
3 "flag"
34 "fmt"
45 "math/rand"
6 "os"
57 "sync"
68 "time"
79
8 "github.com/vbauerster/mpb/v7"
9 "github.com/vbauerster/mpb/v7/decor"
10 "github.com/pkg/profile"
11 "github.com/vbauerster/mpb/v8"
12 "github.com/vbauerster/mpb/v8/decor"
1013 )
1114
1215 const (
1316 totalBars = 32
1417 )
1518
19 var proftype = flag.String("prof", "", "profile type (cpu, mem)")
20
1621 func main() {
22 flag.Parse()
23 switch *proftype {
24 case "cpu":
25 defer profile.Start(profile.CPUProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop()
26 case "mem":
27 defer profile.Start(profile.MemProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop()
28 }
1729 var wg sync.WaitGroup
1830 // passed wg will be accounted at p.Wait() call
19 p := mpb.New(mpb.WithWaitGroup(&wg))
31 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithDebugOutput(os.Stderr))
2032 wg.Add(totalBars)
2133
2234 for i := 0; i < totalBars; i++ {
2436 total := rand.Intn(320) + 10
2537 bar := p.AddBar(int64(total),
2638 mpb.PrependDecorators(
27 decor.Name(name),
28 decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncSpace),
39 decor.Name(name, decor.WCSyncWidthR),
40 decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncWidth),
2941 ),
3042 mpb.AppendDecorators(
3143 decor.OnComplete(
00 module github.com/vbauerster/mpb/_examples/suppressBar
11
2 go 1.14
2 go 1.17
33
44 require (
5 github.com/mattn/go-runewidth v0.0.13
6 github.com/vbauerster/mpb/v7 v7.3.2
5 github.com/mattn/go-runewidth v0.0.15
6 github.com/vbauerster/mpb/v8 v8.6.1
77 )
8
9 require (
10 github.com/VividCortex/ewma v1.2.0 // indirect
11 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
12 github.com/rivo/uniseg v0.4.4 // indirect
13 golang.org/x/sys v0.11.0 // indirect
14 )
88 "time"
99
1010 "github.com/mattn/go-runewidth"
11 "github.com/vbauerster/mpb/v7"
12 "github.com/vbauerster/mpb/v7/decor"
11 "github.com/vbauerster/mpb/v8"
12 "github.com/vbauerster/mpb/v8/decor"
1313 )
1414
1515 func main() {
1919 msgCh := make(chan string)
2020 resumeCh := make(chan struct{})
2121 nextCh := make(chan struct{}, 1)
22 ew := &errorWrapper{}
23 timer := time.AfterFunc(2*time.Second, func() {
24 ew.reset(errors.New("timeout"))
25 })
26 defer timer.Stop()
2227 bar := p.AddBar(int64(total),
2328 mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller {
2429 var msg *string
25 return mpb.BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
26 select {
27 case m := <-msgCh:
30 var times int
31 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
32 if msg == nil {
33 select {
34 case m := <-msgCh:
35 msg = &m
36 times = 10
37 nextCh <- struct{}{}
38 default:
39 }
40 return base.Fill(w, st)
41 }
42 switch {
43 case times == 0, st.Completed, st.Aborted:
2844 defer func() {
29 msg = &m
45 msg = nil
3046 }()
31 nextCh <- struct{}{}
32 case <-resumeCh:
33 msg = nil
47 resumeCh <- struct{}{}
3448 default:
49 times--
3550 }
36 if msg != nil {
37 io.WriteString(w, runewidth.Truncate(*msg, st.AvailableWidth, "…"))
38 nextCh <- struct{}{}
39 } else {
40 base.Fill(w, reqWidth, st)
41 }
51 _, err := io.WriteString(w, runewidth.Truncate(*msg, st.AvailableWidth, "…"))
52 nextCh <- struct{}{}
53 return err
4254 })
4355 }),
4456 mpb.PrependDecorators(decor.Name("my bar:")),
4557 mpb.AppendDecorators(newCustomPercentage(nextCh)),
4658 )
47 ew := &errorWrapper{}
48 time.AfterFunc(2*time.Second, func() {
49 ew.reset(errors.New("timeout"))
50 })
5159 // simulating some work
5260 go func() {
5361 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
5664 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
5765 if ew.isErr() {
5866 msgCh <- fmt.Sprintf("%s at %d, retrying...", ew.Error(), i)
59 go ew.reset(nil)
6067 i--
6168 bar.SetRefill(int64(i))
62 time.Sleep(3 * time.Second)
63 resumeCh <- struct{}{}
69 ew.reset(nil)
70 <-resumeCh
6471 continue
6572 }
6673 bar.Increment()
93100 ew.Unlock()
94101 }
95102
103 type percentage struct {
104 decor.Decorator
105 suspend <-chan struct{}
106 }
107
108 func (d percentage) Decor(s decor.Statistics) (string, int) {
109 select {
110 case <-d.suspend:
111 return d.Format("")
112 default:
113 return d.Decorator.Decor(s)
114 }
115 }
116
96117 func newCustomPercentage(nextCh <-chan struct{}) decor.Decorator {
97 base := decor.Percentage()
98 fn := func(s decor.Statistics) string {
99 select {
100 case <-nextCh:
101 return ""
102 default:
103 return base.Decor(s)
104 }
118 return percentage{
119 Decorator: decor.Percentage(),
120 suspend: nextCh,
105121 }
106 return decor.Any(fn)
107122 }
00 module github.com/vbauerster/mpb/_examples/tipOnComplete
11
2 go 1.14
2 go 1.17
33
4 require github.com/vbauerster/mpb/v7 v7.3.2
4 require github.com/vbauerster/mpb/v8 v8.6.1
5
6 require (
7 github.com/VividCortex/ewma v1.2.0 // indirect
8 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
9 github.com/mattn/go-runewidth v0.0.15 // indirect
10 github.com/rivo/uniseg v0.4.4 // indirect
11 golang.org/x/sys v0.11.0 // indirect
12 )
33 "math/rand"
44 "time"
55
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
6 "github.com/vbauerster/mpb/v8"
7 "github.com/vbauerster/mpb/v8/decor"
88 )
99
1010 func main() {
1414 total := 100
1515 name := "Single Bar:"
1616 bar := p.New(int64(total),
17 mpb.BarStyle().TipOnComplete(">"),
17 mpb.BarStyle().TipOnComplete(),
1818 mpb.PrependDecorators(decor.Name(name)),
1919 mpb.AppendDecorators(decor.Percentage()),
2020 )
+403
-310
bar.go less more
22 import (
33 "bytes"
44 "context"
5 "fmt"
65 "io"
7 "runtime/debug"
86 "strings"
97 "sync"
108 "time"
119
1210 "github.com/acarl005/stripansi"
1311 "github.com/mattn/go-runewidth"
14 "github.com/vbauerster/mpb/v7/decor"
12 "github.com/vbauerster/mpb/v8/decor"
1513 )
1614
1715 // Bar represents a progress bar.
1816 type Bar struct {
19 priority int // used by heap
20 index int // used by heap
21
22 toShutdown bool
23 toDrop bool
24 noPop bool
25 hasEwmaDecorators bool
26 operateState chan func(*bState)
27 frameCh chan *frame
28
29 // cancel is called either by user or on complete event
30 cancel func()
31 // done is closed after cacheState is assigned
32 done chan struct{}
33 // cacheState is populated, right after close(b.done)
34 cacheState *bState
35
36 container *Progress
37 recoveredPanic interface{}
38 }
39
40 type extenderFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int)
41
42 // bState is actual bar state. It gets passed to *Bar.serve(...) monitor
43 // goroutine.
17 index int // used by heap
18 priority int // used by heap
19 frameCh chan *renderFrame
20 operateState chan func(*bState)
21 done chan struct{}
22 container *Progress
23 bs *bState
24 cancel func()
25 }
26
27 type syncTable [2][]chan int
28 type extenderFunc func([]io.Reader, decor.Statistics) ([]io.Reader, error)
29
30 // bState is actual bar's state.
4431 type bState struct {
4532 id int
4633 priority int
4734 reqWidth int
35 shutdown int
4836 total int64
4937 current int64
5038 refill int64
51 lastIncrement int64
5239 trimSpace bool
5340 completed bool
54 completeFlushed bool
5541 aborted bool
5642 triggerComplete bool
57 dropOnComplete bool
43 rmOnComplete bool
5844 noPop bool
45 autoRefresh bool
5946 aDecorators []decor.Decorator
6047 pDecorators []decor.Decorator
6148 averageDecorators []decor.AverageDecorator
6350 shutdownListeners []decor.ShutdownListener
6451 buffers [3]*bytes.Buffer
6552 filler BarFiller
66 middleware func(BarFiller) BarFiller
6753 extender extenderFunc
68
69 // runningBar is a key for *pState.parkedBars
70 runningBar *Bar
71
72 debugOut io.Writer
73 }
74
75 type frame struct {
76 reader io.Reader
77 lines int
78 }
79
80 func newBar(container *Progress, bs *bState) *Bar {
81 ctx, cancel := context.WithCancel(container.ctx)
54 renderReq chan<- time.Time
55 waitBar *Bar // key for (*pState).queueBars
56 }
57
58 type renderFrame struct {
59 rows []io.Reader
60 shutdown int
61 rmOnComplete bool
62 noPop bool
63 done bool
64 err error
65 }
66
67 func newBar(ctx context.Context, container *Progress, bs *bState) *Bar {
68 ctx, cancel := context.WithCancel(ctx)
8269
8370 bar := &Bar{
71 priority: bs.priority,
72 frameCh: make(chan *renderFrame, 1),
73 operateState: make(chan func(*bState)),
74 done: make(chan struct{}),
8475 container: container,
85 priority: bs.priority,
86 toDrop: bs.dropOnComplete,
87 noPop: bs.noPop,
88 operateState: make(chan func(*bState)),
89 frameCh: make(chan *frame, 1),
90 done: make(chan struct{}),
9176 cancel: cancel,
9277 }
9378
79 container.bwg.Add(1)
9480 go bar.serve(ctx, bs)
9581 return bar
9682 }
9783
98 // ProxyReader wraps r with metrics required for progress tracking.
99 // Panics if r is nil.
84 // ProxyReader wraps io.Reader with metrics required for progress tracking.
85 // If `r` is 'unknown total/size' reader it's mandatory to call
86 // (*Bar).SetTotal(-1, true) method after (io.Reader).Read returns io.EOF.
87 // If bar is already completed or aborted, returns nil.
88 // Panics if `r` is nil.
10089 func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
10190 if r == nil {
10291 panic("expected non nil io.Reader")
10392 }
104 return b.newProxyReader(r)
93 result := make(chan bool)
94 select {
95 case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }:
96 return newProxyReader(r, b, <-result)
97 case <-b.done:
98 return nil
99 }
100 }
101
102 // ProxyWriter wraps io.Writer with metrics required for progress tracking.
103 // If bar is already completed or aborted, returns nil.
104 // Panics if `w` is nil.
105 func (b *Bar) ProxyWriter(w io.Writer) io.WriteCloser {
106 if w == nil {
107 panic("expected non nil io.Writer")
108 }
109 result := make(chan bool)
110 select {
111 case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }:
112 return newProxyWriter(w, b, <-result)
113 case <-b.done:
114 return nil
115 }
105116 }
106117
107118 // ID returs id of the bar.
111122 case b.operateState <- func(s *bState) { result <- s.id }:
112123 return <-result
113124 case <-b.done:
114 return b.cacheState.id
115 }
116 }
117
118 // Current returns bar's current number, in other words sum of all increments.
125 return b.bs.id
126 }
127 }
128
129 // Current returns bar's current value, in other words sum of all increments.
119130 func (b *Bar) Current() int64 {
120131 result := make(chan int64)
121132 select {
122133 case b.operateState <- func(s *bState) { result <- s.current }:
123134 return <-result
124135 case <-b.done:
125 return b.cacheState.current
136 return b.bs.current
126137 }
127138 }
128139
133144 func (b *Bar) SetRefill(amount int64) {
134145 select {
135146 case b.operateState <- func(s *bState) {
136 s.refill = amount
147 if amount < s.current {
148 s.refill = amount
149 } else {
150 s.refill = s.current
151 }
137152 }:
138153 case <-b.done:
139154 }
141156
142157 // TraverseDecorators traverses all available decorators and calls cb func on each.
143158 func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
144 done := make(chan struct{})
145 select {
146 case b.operateState <- func(s *bState) {
147 for _, decorators := range [...][]decor.Decorator{
159 iter := make(chan decor.Decorator)
160 select {
161 case b.operateState <- func(s *bState) {
162 for _, decorators := range [][]decor.Decorator{
148163 s.pDecorators,
149164 s.aDecorators,
150165 } {
151166 for _, d := range decorators {
152 cb(extractBaseDecorator(d))
167 iter <- d
153168 }
154169 }
155 close(done)
156 }:
157 <-done
158 case <-b.done:
159 }
160 }
161
162 // SetTotal sets total dynamically.
163 // If total is negative 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
170 close(iter)
171 }:
172 for d := range iter {
173 cb(unwrap(d))
174 }
175 case <-b.done:
176 }
177 }
178
179 // EnableTriggerComplete enables triggering complete event. It's
180 // effective only for bars which were constructed with `total <= 0` and
181 // after total has been set with (*Bar).SetTotal(int64, false). If bar
182 // has been incremented to the total, complete event is triggered right
183 // away.
184 func (b *Bar) EnableTriggerComplete() {
185 select {
186 case b.operateState <- func(s *bState) {
187 if s.triggerComplete || s.total <= 0 {
188 return
189 }
190 if s.current >= s.total {
191 s.current = s.total
192 s.completed = true
193 b.triggerCompletion(s)
194 } else {
195 s.triggerComplete = true
196 }
197 }:
198 case <-b.done:
199 }
200 }
201
202 // SetTotal sets total to an arbitrary value. It's effective only for
203 // bar which was constructed with `total <= 0`. Setting total to negative
204 // value is equivalent to (*Bar).SetTotal((*Bar).Current(), bool) but faster.
205 // If triggerCompletion is true, total value is set to current and
206 // complete event is triggered right away.
207 func (b *Bar) SetTotal(total int64, triggerCompletion bool) {
208 select {
209 case b.operateState <- func(s *bState) {
210 if s.triggerComplete {
211 return
212 }
168213 if total < 0 {
169214 s.total = s.current
170215 } else {
171216 s.total = total
172217 }
173 if s.triggerComplete && !s.completed {
218 if triggerCompletion {
174219 s.current = s.total
175220 s.completed = true
176 go b.forceRefreshIfLastUncompleted()
221 b.triggerCompletion(s)
177222 }
178223 }:
179224 case <-b.done:
181226 }
182227
183228 // SetCurrent sets progress' current to an arbitrary value.
184 // Setting a negative value will cause a panic.
185229 func (b *Bar) SetCurrent(current int64) {
186 select {
187 case b.operateState <- func(s *bState) {
188 s.lastIncrement = current - s.current
230 if current < 0 {
231 return
232 }
233 select {
234 case b.operateState <- func(s *bState) {
189235 s.current = current
190236 if s.triggerComplete && s.current >= s.total {
191237 s.current = s.total
192238 s.completed = true
193 go b.forceRefreshIfLastUncompleted()
239 b.triggerCompletion(s)
240 }
241 }:
242 case <-b.done:
243 }
244 }
245
246 // EwmaSetCurrent sets progress' current to an arbitrary value and updates
247 // EWMA based decorators by dur of a single iteration.
248 func (b *Bar) EwmaSetCurrent(current int64, iterDur time.Duration) {
249 if current < 0 {
250 return
251 }
252 select {
253 case b.operateState <- func(s *bState) {
254 if n := current - s.current; n > 0 {
255 s.decoratorEwmaUpdate(n, iterDur)
256 }
257 s.current = current
258 if s.triggerComplete && s.current >= s.total {
259 s.current = s.total
260 s.completed = true
261 b.triggerCompletion(s)
194262 }
195263 }:
196264 case <-b.done:
214282 }
215283 select {
216284 case b.operateState <- func(s *bState) {
217 s.lastIncrement = n
218285 s.current += n
219286 if s.triggerComplete && s.current >= s.total {
220287 s.current = s.total
221288 s.completed = true
222 go b.forceRefreshIfLastUncompleted()
223 }
224 }:
225 case <-b.done:
226 }
227 }
228
229 // DecoratorEwmaUpdate updates all EWMA based decorators. Should be
230 // called on each iteration, because EWMA's unit of measure is an
231 // iteration's duration. Panics if called before *Bar.Incr... family
232 // methods.
233 func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
234 select {
235 case b.operateState <- func(s *bState) {
236 if s.lastIncrement > 0 {
237 s.decoratorEwmaUpdate(dur)
238 s.lastIncrement = 0
239 } else {
240 panic("increment required before ewma iteration update")
241 }
242 }:
243 case <-b.done:
244 if b.cacheState.lastIncrement > 0 {
245 b.cacheState.decoratorEwmaUpdate(dur)
246 b.cacheState.lastIncrement = 0
247 }
289 b.triggerCompletion(s)
290 }
291 }:
292 case <-b.done:
293 }
294 }
295
296 // EwmaIncrement is a shorthand for b.EwmaIncrInt64(1, iterDur).
297 func (b *Bar) EwmaIncrement(iterDur time.Duration) {
298 b.EwmaIncrInt64(1, iterDur)
299 }
300
301 // EwmaIncrBy is a shorthand for b.EwmaIncrInt64(int64(n), iterDur).
302 func (b *Bar) EwmaIncrBy(n int, iterDur time.Duration) {
303 b.EwmaIncrInt64(int64(n), iterDur)
304 }
305
306 // EwmaIncrInt64 increments progress by amount of n and updates EWMA based
307 // decorators by dur of a single iteration.
308 func (b *Bar) EwmaIncrInt64(n int64, iterDur time.Duration) {
309 if n <= 0 {
310 return
311 }
312 select {
313 case b.operateState <- func(s *bState) {
314 s.decoratorEwmaUpdate(n, iterDur)
315 s.current += n
316 if s.triggerComplete && s.current >= s.total {
317 s.current = s.total
318 s.completed = true
319 b.triggerCompletion(s)
320 }
321 }:
322 case <-b.done:
248323 }
249324 }
250325
253328 // or after progress resume.
254329 func (b *Bar) DecoratorAverageAdjust(start time.Time) {
255330 select {
256 case b.operateState <- func(s *bState) {
257 s.decoratorAverageAdjust(start)
258 }:
331 case b.operateState <- func(s *bState) { s.decoratorAverageAdjust(start) }:
259332 case <-b.done:
260333 }
261334 }
264337 // priority, i.e. bar will be on top. If you don't need to set priority
265338 // dynamically, better use BarPriority option.
266339 func (b *Bar) SetPriority(priority int) {
267 b.container.UpdateBarPriority(b, priority)
340 b.container.UpdateBarPriority(b, priority, false)
268341 }
269342
270343 // Abort interrupts bar's running goroutine. Abort won't be engaged
271344 // if bar is already in complete state. If drop is true bar will be
272 // removed as well.
345 // removed as well. To make sure that bar has been removed call
346 // (*Bar).Wait method.
273347 func (b *Bar) Abort(drop bool) {
274 done := make(chan struct{})
275 select {
276 case b.operateState <- func(s *bState) {
277 if s.completed {
278 close(done)
348 select {
349 case b.operateState <- func(s *bState) {
350 if s.completed || s.aborted {
279351 return
280352 }
281353 s.aborted = true
282 b.cancel()
283 // container must be run during lifetime of this inner goroutine
284 // we control this by done channel declared above
285 go func() {
286 if drop {
287 b.container.dropBar(b)
288 } else {
289 var uncompleted int
290 b.container.traverseBars(func(bar *Bar) bool {
291 if b != bar && !bar.Completed() {
292 uncompleted++
293 return false
294 }
295 return true
296 })
297 if uncompleted == 0 {
298 b.container.refreshCh <- time.Now()
299 }
300 }
301 close(done) // release hold of Abort
302 }()
303 }:
304 // guarantee: container is alive during lifetime of this hold
305 <-done
306 case <-b.done:
354 s.rmOnComplete = drop
355 b.triggerCompletion(s)
356 }:
357 case <-b.done:
358 }
359 }
360
361 // Aborted reports whether the bar is in aborted state.
362 func (b *Bar) Aborted() bool {
363 result := make(chan bool)
364 select {
365 case b.operateState <- func(s *bState) { result <- s.aborted }:
366 return <-result
367 case <-b.done:
368 return b.bs.aborted
307369 }
308370 }
309371
314376 case b.operateState <- func(s *bState) { result <- s.completed }:
315377 return <-result
316378 case <-b.done:
317 return true
318 }
319 }
320
321 func (b *Bar) serve(ctx context.Context, s *bState) {
379 return b.bs.completed
380 }
381 }
382
383 // IsRunning reports whether the bar is running, i.e. not yet completed
384 // and not yet aborted.
385 func (b *Bar) IsRunning() bool {
386 result := make(chan bool)
387 select {
388 case b.operateState <- func(s *bState) { result <- !s.completed && !s.aborted }:
389 return <-result
390 case <-b.done:
391 return false
392 }
393 }
394
395 // Wait blocks until bar is completed or aborted.
396 func (b *Bar) Wait() {
397 <-b.done
398 }
399
400 func (b *Bar) serve(ctx context.Context, bs *bState) {
322401 defer b.container.bwg.Done()
323402 for {
324403 select {
325404 case op := <-b.operateState:
326 op(s)
405 op(bs)
327406 case <-ctx.Done():
328 s.decoratorShutdownNotify()
329 b.cacheState = s
407 bs.aborted = !bs.completed
408 bs.decoratorShutdownNotify()
409 b.bs = bs
330410 close(b.done)
331411 return
332412 }
334414 }
335415
336416 func (b *Bar) render(tw int) {
337 select {
338 case b.operateState <- func(s *bState) {
417 var done bool
418 fn := func(s *bState) {
419 var rows []io.Reader
339420 stat := newStatistics(tw, s)
340 defer func() {
341 // recovering if user defined decorator panics for example
342 if p := recover(); p != nil {
343 if b.recoveredPanic == nil {
344 if s.debugOut != nil {
345 fmt.Fprintln(s.debugOut, p)
346 _, _ = s.debugOut.Write(debug.Stack())
347 }
348 s.extender = makePanicExtender(p)
349 b.toShutdown = !b.toShutdown
350 b.recoveredPanic = p
351 }
352 reader, lines := s.extender(nil, s.reqWidth, stat)
353 b.frameCh <- &frame{reader, lines + 1}
421 r, err := s.draw(stat)
422 if err != nil {
423 b.frameCh <- &renderFrame{err: err}
424 return
425 }
426 rows = append(rows, r)
427 if s.extender != nil {
428 rows, err = s.extender(rows, stat)
429 if err != nil {
430 b.frameCh <- &renderFrame{err: err}
431 return
354432 }
355 s.completeFlushed = s.completed
356 }()
357 reader, lines := s.extender(s.draw(stat), s.reqWidth, stat)
358 b.toShutdown = s.completed && !s.completeFlushed
359 b.frameCh <- &frame{reader, lines + 1}
360 }:
361 case <-b.done:
362 s := b.cacheState
363 stat := newStatistics(tw, s)
364 var r io.Reader
365 if b.recoveredPanic == nil {
366 r = s.draw(stat)
367 }
368 reader, lines := s.extender(r, s.reqWidth, stat)
369 b.frameCh <- &frame{reader, lines + 1}
370 }
371 }
372
373 func (b *Bar) subscribeDecorators() {
374 var averageDecorators []decor.AverageDecorator
375 var ewmaDecorators []decor.EwmaDecorator
376 var shutdownListeners []decor.ShutdownListener
377 b.TraverseDecorators(func(d decor.Decorator) {
378 if d, ok := d.(decor.AverageDecorator); ok {
379 averageDecorators = append(averageDecorators, d)
380 }
381 if d, ok := d.(decor.EwmaDecorator); ok {
382 ewmaDecorators = append(ewmaDecorators, d)
383 }
384 if d, ok := d.(decor.ShutdownListener); ok {
385 shutdownListeners = append(shutdownListeners, d)
386 }
433 }
434 frame := &renderFrame{
435 rows: rows,
436 shutdown: s.shutdown,
437 rmOnComplete: s.rmOnComplete,
438 noPop: s.noPop,
439 done: done,
440 }
441 if s.completed || s.aborted {
442 // post increment makes sure OnComplete decorators are rendered
443 s.shutdown++
444 }
445 b.frameCh <- frame
446 }
447 select {
448 case b.operateState <- fn:
449 case <-b.done:
450 done = true
451 fn(b.bs)
452 }
453 }
454
455 func (b *Bar) triggerCompletion(s *bState) {
456 if s.autoRefresh {
457 // Technically this call isn't required, but if refresh rate is set to
458 // one hour for example and bar completes within a few minutes p.Wait()
459 // will wait for one hour. This call helps to avoid unnecessary waiting.
460 go b.tryEarlyRefresh(s.renderReq)
461 } else {
462 b.cancel()
463 }
464 }
465
466 func (b *Bar) tryEarlyRefresh(renderReq chan<- time.Time) {
467 var anyOtherRunning bool
468 b.container.traverseBars(func(bar *Bar) bool {
469 anyOtherRunning = b != bar && bar.IsRunning()
470 return anyOtherRunning
387471 })
388 b.hasEwmaDecorators = len(ewmaDecorators) != 0
389 select {
390 case b.operateState <- func(s *bState) {
391 s.averageDecorators = averageDecorators
392 s.ewmaDecorators = ewmaDecorators
393 s.shutdownListeners = shutdownListeners
394 }:
395 case <-b.done:
396 }
397 }
398
399 func (b *Bar) forceRefreshIfLastUncompleted() {
400 var uncompleted int
401 b.container.traverseBars(func(bar *Bar) bool {
402 if b != bar && !bar.Completed() {
403 uncompleted++
404 return false
405 }
406 return true
407 })
408 if uncompleted == 0 {
472 if !anyOtherRunning {
409473 for {
410474 select {
411 case b.container.refreshCh <- time.Now():
475 case renderReq <- time.Now():
412476 case <-b.done:
413477 return
414478 }
416480 }
417481 }
418482
419 func (b *Bar) wSyncTable() [][]chan int {
420 result := make(chan [][]chan int)
483 func (b *Bar) wSyncTable() syncTable {
484 result := make(chan syncTable)
421485 select {
422486 case b.operateState <- func(s *bState) { result <- s.wSyncTable() }:
423487 return <-result
424488 case <-b.done:
425 return b.cacheState.wSyncTable()
426 }
427 }
428
429 func (s *bState) draw(stat decor.Statistics) io.Reader {
489 return b.bs.wSyncTable()
490 }
491 }
492
493 func (s *bState) draw(stat decor.Statistics) (io.Reader, error) {
494 r, err := s.drawImpl(stat)
495 if err != nil {
496 for _, b := range s.buffers {
497 b.Reset()
498 }
499 return nil, err
500 }
501 return io.MultiReader(r, strings.NewReader("\n")), nil
502 }
503
504 func (s *bState) drawImpl(stat decor.Statistics) (io.Reader, error) {
505 decorFiller := func(buf *bytes.Buffer, decorators []decor.Decorator) (err error) {
506 for _, d := range decorators {
507 // need to call Decor in any case becase of width synchronization
508 str, width := d.Decor(stat)
509 if err != nil {
510 continue
511 }
512 if w := stat.AvailableWidth - width; w >= 0 {
513 _, err = buf.WriteString(str)
514 stat.AvailableWidth = w
515 } else if stat.AvailableWidth > 0 {
516 trunc := runewidth.Truncate(stripansi.Strip(str), stat.AvailableWidth, "…")
517 _, err = buf.WriteString(trunc)
518 stat.AvailableWidth = 0
519 }
520 }
521 return err
522 }
523
430524 bufP, bufB, bufA := s.buffers[0], s.buffers[1], s.buffers[2]
431 nlr := strings.NewReader("\n")
432 tw := stat.AvailableWidth
433 for _, d := range s.pDecorators {
434 str := d.Decor(stat)
435 stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
436 bufP.WriteString(str)
437 }
438 if stat.AvailableWidth < 1 {
439 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufP.String()), tw, "…"))
440 bufP.Reset()
441 return io.MultiReader(trunc, nlr)
442 }
443
444 if !s.trimSpace && stat.AvailableWidth > 1 {
525
526 err := eitherError(decorFiller(bufP, s.pDecorators), decorFiller(bufA, s.aDecorators))
527 if err != nil {
528 return nil, err
529 }
530
531 if !s.trimSpace && stat.AvailableWidth >= 2 {
445532 stat.AvailableWidth -= 2
446 bufB.WriteByte(' ')
447 defer bufB.WriteByte(' ')
448 }
449
450 tw = stat.AvailableWidth
451 for _, d := range s.aDecorators {
452 str := d.Decor(stat)
453 stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
454 bufA.WriteString(str)
455 }
456 if stat.AvailableWidth < 1 {
457 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufA.String()), tw, "…"))
458 bufA.Reset()
459 return io.MultiReader(bufP, bufB, trunc, nlr)
460 }
461
462 s.filler.Fill(bufB, s.reqWidth, stat)
463
464 return io.MultiReader(bufP, bufB, bufA, nlr)
465 }
466
467 func (s *bState) wSyncTable() [][]chan int {
468 columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
469 var pCount int
470 for _, d := range s.pDecorators {
471 if ch, ok := d.Sync(); ok {
472 columns = append(columns, ch)
473 pCount++
474 }
475 }
476 var aCount int
477 for _, d := range s.aDecorators {
478 if ch, ok := d.Sync(); ok {
479 columns = append(columns, ch)
480 aCount++
481 }
482 }
483 table := make([][]chan int, 2)
484 table[0] = columns[0:pCount]
485 table[1] = columns[pCount : pCount+aCount : pCount+aCount]
533 writeFiller := func(buf *bytes.Buffer) error {
534 return s.filler.Fill(buf, stat)
535 }
536 for _, fn := range []func(*bytes.Buffer) error{
537 writeSpace,
538 writeFiller,
539 writeSpace,
540 } {
541 if err := fn(bufB); err != nil {
542 return nil, err
543 }
544 }
545 } else {
546 err := s.filler.Fill(bufB, stat)
547 if err != nil {
548 return nil, err
549 }
550 }
551
552 return io.MultiReader(bufP, bufB, bufA), nil
553 }
554
555 func (s *bState) wSyncTable() (table syncTable) {
556 var count int
557 var row []chan int
558
559 for i, decorators := range [][]decor.Decorator{
560 s.pDecorators,
561 s.aDecorators,
562 } {
563 for _, d := range decorators {
564 if ch, ok := d.Sync(); ok {
565 row = append(row, ch)
566 count++
567 }
568 }
569 switch i {
570 case 0:
571 table[i] = row[0:count]
572 default:
573 table[i] = row[len(table[i-1]):count]
574 }
575 }
486576 return table
487577 }
488578
489 func (s bState) decoratorEwmaUpdate(dur time.Duration) {
490 wg := new(sync.WaitGroup)
579 func (s bState) decoratorEwmaUpdate(n int64, dur time.Duration) {
580 var wg sync.WaitGroup
491581 for i := 0; i < len(s.ewmaDecorators); i++ {
492582 switch d := s.ewmaDecorators[i]; i {
493583 case len(s.ewmaDecorators) - 1:
494 d.EwmaUpdate(s.lastIncrement, dur)
584 d.EwmaUpdate(n, dur)
495585 default:
496586 wg.Add(1)
497587 go func() {
498 d.EwmaUpdate(s.lastIncrement, dur)
588 d.EwmaUpdate(n, dur)
499589 wg.Done()
500590 }()
501591 }
504594 }
505595
506596 func (s bState) decoratorAverageAdjust(start time.Time) {
507 wg := new(sync.WaitGroup)
597 var wg sync.WaitGroup
508598 for i := 0; i < len(s.averageDecorators); i++ {
509599 switch d := s.averageDecorators[i]; i {
510600 case len(s.averageDecorators) - 1:
521611 }
522612
523613 func (s bState) decoratorShutdownNotify() {
524 wg := new(sync.WaitGroup)
614 var wg sync.WaitGroup
525615 for i := 0; i < len(s.shutdownListeners); i++ {
526616 switch d := s.shutdownListeners[i]; i {
527617 case len(s.shutdownListeners) - 1:
528 d.Shutdown()
618 d.OnShutdown()
529619 default:
530620 wg.Add(1)
531621 go func() {
532 d.Shutdown()
622 d.OnShutdown()
533623 wg.Done()
534624 }()
535625 }
539629
540630 func newStatistics(tw int, s *bState) decor.Statistics {
541631 return decor.Statistics{
632 AvailableWidth: tw,
633 RequestedWidth: s.reqWidth,
542634 ID: s.id,
543 AvailableWidth: tw,
544635 Total: s.total,
545636 Current: s.current,
546637 Refill: s.refill,
547 Completed: s.completeFlushed,
638 Completed: s.completed,
548639 Aborted: s.aborted,
549640 }
550641 }
551642
552 func extractBaseDecorator(d decor.Decorator) decor.Decorator {
643 func unwrap(d decor.Decorator) decor.Decorator {
553644 if d, ok := d.(decor.Wrapper); ok {
554 return extractBaseDecorator(d.Base())
645 return unwrap(d.Unwrap())
555646 }
556647 return d
557648 }
558649
559 func makePanicExtender(p interface{}) extenderFunc {
560 pstr := fmt.Sprint(p)
561 return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) {
562 mr := io.MultiReader(
563 strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")),
564 strings.NewReader("\n"),
565 )
566 return mr, 0
567 }
568 }
650 func writeSpace(buf *bytes.Buffer) error {
651 return buf.WriteByte(' ')
652 }
653
654 func eitherError(errors ...error) error {
655 for _, err := range errors {
656 if err != nil {
657 return err
658 }
659 }
660 return nil
661 }
22 import (
33 "io"
44
5 "github.com/vbauerster/mpb/v7/decor"
5 "github.com/vbauerster/mpb/v8/decor"
66 )
77
88 // BarFiller interface.
99 // Bar (without decorators) renders itself by calling BarFiller's Fill method.
10 //
11 // reqWidth is requested width set by `func WithWidth(int) ContainerOption`.
12 // If not set, it defaults to terminal width.
13 //
1410 type BarFiller interface {
15 Fill(w io.Writer, reqWidth int, stat decor.Statistics)
11 Fill(io.Writer, decor.Statistics) error
1612 }
1713
1814 // BarFillerBuilder interface.
2117 // BarStyle()
2218 // SpinnerStyle()
2319 // NopStyle()
24 //
2520 type BarFillerBuilder interface {
2621 Build() BarFiller
2722 }
2823
2924 // BarFillerFunc is function type adapter to convert compatible function
3025 // into BarFiller interface.
31 type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
26 type BarFillerFunc func(io.Writer, decor.Statistics) error
3227
33 func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
34 f(w, reqWidth, stat)
28 func (f BarFillerFunc) Fill(w io.Writer, stat decor.Statistics) error {
29 return f(w, stat)
3530 }
36
37 // BarFillerBuilderFunc is function type adapter to convert compatible
38 // function into BarFillerBuilder interface.
39 type BarFillerBuilderFunc func() BarFiller
40
41 func (f BarFillerBuilderFunc) Build() BarFiller {
42 return f()
43 }
44
45 // NewBarFiller constructs a BarFiller from provided BarFillerBuilder.
46 // Deprecated. Prefer using `*Progress.New(...)` directly.
47 func NewBarFiller(b BarFillerBuilder) BarFiller {
48 return b.Build()
49 }
22 import (
33 "io"
44
5 "github.com/acarl005/stripansi"
65 "github.com/mattn/go-runewidth"
7 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v7/internal"
6 "github.com/vbauerster/mpb/v8/decor"
7 "github.com/vbauerster/mpb/v8/internal"
98 )
109
1110 const (
1211 iLbound = iota
1312 iRbound
13 iRefiller
1414 iFiller
15 iRefiller
15 iTip
1616 iPadding
1717 components
1818 )
19
20 var defaultBarStyle = [components]string{"[", "]", "+", "=", ">", "-"}
1921
2022 // BarStyleComposer interface.
2123 type BarStyleComposer interface {
2224 BarFillerBuilder
2325 Lbound(string) BarStyleComposer
26 LboundMeta(func(string) string) BarStyleComposer
2427 Rbound(string) BarStyleComposer
28 RboundMeta(func(string) string) BarStyleComposer
2529 Filler(string) BarStyleComposer
30 FillerMeta(func(string) string) BarStyleComposer
2631 Refiller(string) BarStyleComposer
32 RefillerMeta(func(string) string) BarStyleComposer
2733 Padding(string) BarStyleComposer
28 TipOnComplete(string) BarStyleComposer
34 PaddingMeta(func(string) string) BarStyleComposer
2935 Tip(frames ...string) BarStyleComposer
36 TipMeta(func(string) string) BarStyleComposer
37 TipOnComplete() BarStyleComposer
3038 Reverse() BarStyleComposer
31 }
32
33 type bFiller struct {
34 rev bool
35 components [components]*component
36 tip struct {
37 count uint
38 onComplete *component
39 frames []*component
40 }
4139 }
4240
4341 type component struct {
4543 bytes []byte
4644 }
4745
46 type flushSection struct {
47 meta func(io.Writer, []byte) error
48 bytes []byte
49 }
50
51 type bFiller struct {
52 components [components]component
53 meta [components]func(io.Writer, []byte) error
54 flush func(io.Writer, ...flushSection) error
55 tipOnComplete bool
56 tip struct {
57 frames []component
58 count uint
59 }
60 }
61
4862 type barStyle struct {
49 lbound string
50 rbound string
51 filler string
52 refiller string
53 padding string
54 tipOnComplete string
63 style [components]string
64 metaFuncs [components]func(io.Writer, []byte) error
5565 tipFrames []string
66 tipOnComplete bool
5667 rev bool
5768 }
5869
5970 // BarStyle constructs default bar style which can be altered via
6071 // BarStyleComposer interface.
6172 func BarStyle() BarStyleComposer {
62 return &barStyle{
63 lbound: "[",
64 rbound: "]",
65 filler: "=",
66 refiller: "+",
67 padding: "-",
68 tipFrames: []string{">"},
69 }
70 }
71
72 func (s *barStyle) Lbound(bound string) BarStyleComposer {
73 s.lbound = bound
74 return s
75 }
76
77 func (s *barStyle) Rbound(bound string) BarStyleComposer {
78 s.rbound = bound
79 return s
80 }
81
82 func (s *barStyle) Filler(filler string) BarStyleComposer {
83 s.filler = filler
84 return s
85 }
86
87 func (s *barStyle) Refiller(refiller string) BarStyleComposer {
88 s.refiller = refiller
89 return s
90 }
91
92 func (s *barStyle) Padding(padding string) BarStyleComposer {
93 s.padding = padding
94 return s
95 }
96
97 func (s *barStyle) TipOnComplete(tip string) BarStyleComposer {
98 s.tipOnComplete = tip
99 return s
100 }
101
102 func (s *barStyle) Tip(frames ...string) BarStyleComposer {
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 {
103134 if len(frames) != 0 {
104 s.tipFrames = append(s.tipFrames[:0], frames...)
105 }
106 return s
107 }
108
109 func (s *barStyle) Reverse() BarStyleComposer {
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 {
110151 s.rev = true
111152 return s
112153 }
113154
114 func (s *barStyle) Build() BarFiller {
115 bf := &bFiller{rev: s.rev}
116 bf.components[iLbound] = &component{
117 width: runewidth.StringWidth(stripansi.Strip(s.lbound)),
118 bytes: []byte(s.lbound),
119 }
120 bf.components[iRbound] = &component{
121 width: runewidth.StringWidth(stripansi.Strip(s.rbound)),
122 bytes: []byte(s.rbound),
123 }
124 bf.components[iFiller] = &component{
125 width: runewidth.StringWidth(stripansi.Strip(s.filler)),
126 bytes: []byte(s.filler),
127 }
128 bf.components[iRefiller] = &component{
129 width: runewidth.StringWidth(stripansi.Strip(s.refiller)),
130 bytes: []byte(s.refiller),
131 }
132 bf.components[iPadding] = &component{
133 width: runewidth.StringWidth(stripansi.Strip(s.padding)),
134 bytes: []byte(s.padding),
135 }
136 bf.tip.onComplete = &component{
137 width: runewidth.StringWidth(stripansi.Strip(s.tipOnComplete)),
138 bytes: []byte(s.tipOnComplete),
139 }
140 bf.tip.frames = make([]*component, len(s.tipFrames))
155 func (s barStyle) Build() BarFiller {
156 bf := &bFiller{
157 meta: s.metaFuncs,
158 tipOnComplete: s.tipOnComplete,
159 }
160 bf.components[iLbound] = component{
161 width: runewidth.StringWidth(s.style[iLbound]),
162 bytes: []byte(s.style[iLbound]),
163 }
164 bf.components[iRbound] = component{
165 width: runewidth.StringWidth(s.style[iRbound]),
166 bytes: []byte(s.style[iRbound]),
167 }
168 bf.components[iFiller] = component{
169 width: runewidth.StringWidth(s.style[iFiller]),
170 bytes: []byte(s.style[iFiller]),
171 }
172 bf.components[iRefiller] = component{
173 width: runewidth.StringWidth(s.style[iRefiller]),
174 bytes: []byte(s.style[iRefiller]),
175 }
176 bf.components[iPadding] = component{
177 width: runewidth.StringWidth(s.style[iPadding]),
178 bytes: []byte(s.style[iPadding]),
179 }
180 bf.tip.frames = make([]component, len(s.tipFrames))
141181 for i, t := range s.tipFrames {
142 bf.tip.frames[i] = &component{
143 width: runewidth.StringWidth(stripansi.Strip(t)),
182 bf.tip.frames[i] = component{
183 width: runewidth.StringWidth(t),
144184 bytes: []byte(t),
145185 }
146186 }
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 }
211 }
147212 return bf
148213 }
149214
150 func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
151 width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
152 brackets := s.components[iLbound].width + s.components[iRbound].width
215 func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) error {
216 width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
153217 // don't count brackets as progress
154 width -= brackets
218 width -= (s.components[iLbound].width + s.components[iRbound].width)
155219 if width < 0 {
156 return
157 }
158
159 ow := optimisticWriter(w)
160 ow(s.components[iLbound].bytes)
161 defer ow(s.components[iRbound].bytes)
220 return nil
221 }
222
223 err := s.meta[iLbound](w, s.components[iLbound].bytes)
224 if err != nil {
225 return err
226 }
162227
163228 if width == 0 {
164 return
165 }
166
167 var filling [][]byte
168 var padding [][]byte
169 var tip *component
170 var filled int
171 var refWidth int
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
172235 curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width)))
173236
174 if stat.Current >= stat.Total {
175 tip = s.tip.onComplete
176 } else {
177 tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
178 }
179
180 if curWidth > 0 {
181 filling = append(filling, tip.bytes)
182 filled += tip.width
183 s.tip.count++
184 }
185
186 if stat.Refill > 0 {
187 refWidth = int(internal.PercentageRound(stat.Total, stat.Refill, uint(width)))
188 curWidth -= refWidth
189 refWidth += curWidth
190 }
191
192 for filled < curWidth {
193 if curWidth-filled >= s.components[iFiller].width {
194 filling = append(filling, s.components[iFiller].bytes)
195 if s.components[iFiller].width == 0 {
196 break
197 }
198 filled += s.components[iFiller].width
237 if curWidth != 0 {
238 if !stat.Completed || s.tipOnComplete {
239 tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
240 s.tip.count++
241 fillCount += tip.width
242 }
243 if stat.Refill != 0 {
244 refWidth := int(internal.PercentageRound(stat.Total, stat.Refill, uint(width)))
245 curWidth -= refWidth
246 refWidth += curWidth
247 for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w {
248 filling = append(filling, s.components[iFiller].bytes...)
249 }
250 for w := s.components[iRefiller].width; refWidth-fillCount >= w; fillCount += w {
251 refilling = append(refilling, s.components[iRefiller].bytes...)
252 }
199253 } else {
200 filling = append(filling, []byte("…"))
201 filled++
202 }
203 }
204
205 for filled < refWidth {
206 if refWidth-filled >= s.components[iRefiller].width {
207 filling = append(filling, s.components[iRefiller].bytes)
208 if s.components[iRefiller].width == 0 {
209 break
210 }
211 filled += s.components[iRefiller].width
212 } else {
213 filling = append(filling, []byte("…"))
214 filled++
215 }
216 }
217
218 padWidth := width - filled
219 for padWidth > 0 {
220 if padWidth >= s.components[iPadding].width {
221 padding = append(padding, s.components[iPadding].bytes)
222 if s.components[iPadding].width == 0 {
223 break
224 }
225 padWidth -= s.components[iPadding].width
226 } else {
227 padding = append(padding, []byte("…"))
228 padWidth--
229 }
230 }
231
232 if s.rev {
233 flush(ow, padding, filling)
234 } else {
235 flush(ow, filling, padding)
236 }
237 }
238
239 func flush(ow func([]byte), filling, padding [][]byte) {
240 for i := len(filling) - 1; i >= 0; i-- {
241 ow(filling[i])
242 }
243 for i := 0; i < len(padding); i++ {
244 ow(padding[i])
245 }
246 }
247
248 func optimisticWriter(w io.Writer) func([]byte) {
249 return func(p []byte) {
250 _, err := w.Write(p)
251 if err != nil {
252 panic(err)
253 }
254 }
255 }
254 for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w {
255 filling = append(filling, s.components[iFiller].bytes...)
256 }
257 }
258 }
259
260 for w := s.components[iPadding].width; width-fillCount >= w; fillCount += w {
261 padding = append(padding, s.components[iPadding].bytes...)
262 }
263
264 for w := 1; width-fillCount >= w; fillCount += w {
265 padding = append(padding, "…"...)
266 }
267
268 err = s.flush(w,
269 flushSection{s.meta[iRefiller], refilling},
270 flushSection{s.meta[iFiller], filling},
271 flushSection{s.meta[iTip], tip.bytes},
272 flushSection{s.meta[iPadding], padding},
273 )
274 if err != nil {
275 return err
276 }
277 return s.meta[iRbound](w, s.components[iRbound].bytes)
278 }
279
280 func makeMetaFunc(fn func(string) string) func(io.Writer, []byte) error {
281 return func(w io.Writer, p []byte) (err error) {
282 _, err = io.WriteString(w, fn(string(p)))
283 return err
284 }
285 }
286
287 func defaultMeta(w io.Writer, p []byte) (err error) {
288 _, err = w.Write(p)
289 return err
290 }
22 import (
33 "io"
44
5 "github.com/vbauerster/mpb/v7/decor"
5 "github.com/vbauerster/mpb/v8/decor"
66 )
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 }
715
816 // NopStyle provides BarFillerBuilder which builds NOP BarFiller.
917 func NopStyle() BarFillerBuilder {
10 return BarFillerBuilderFunc(func() BarFiller {
11 return BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
18 return barFillerBuilderFunc(func() BarFiller {
19 return BarFillerFunc(func(io.Writer, decor.Statistics) error {
20 return nil
21 })
1222 })
1323 }
33 "io"
44 "strings"
55
6 "github.com/acarl005/stripansi"
76 "github.com/mattn/go-runewidth"
8 "github.com/vbauerster/mpb/v7/decor"
9 "github.com/vbauerster/mpb/v7/internal"
7 "github.com/vbauerster/mpb/v8/decor"
8 "github.com/vbauerster/mpb/v8/internal"
109 )
1110
1211 const (
1413 positionRight
1514 )
1615
16 var defaultSpinnerStyle = [...]string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
17
1718 // SpinnerStyleComposer interface.
1819 type SpinnerStyleComposer interface {
1920 BarFillerBuilder
2021 PositionLeft() SpinnerStyleComposer
2122 PositionRight() SpinnerStyleComposer
23 Meta(func(string) string) SpinnerStyleComposer
2224 }
2325
2426 type sFiller struct {
27 frames []string
2528 count uint
26 position uint
27 frames []string
29 meta func(string) string
30 position func(string, int) string
2831 }
2932
3033 type spinnerStyle struct {
3134 position uint
3235 frames []string
36 meta func(string) string
3337 }
3438
3539 // SpinnerStyle constructs default spinner style which can be altered via
3640 // SpinnerStyleComposer interface.
3741 func SpinnerStyle(frames ...string) SpinnerStyleComposer {
38 ss := new(spinnerStyle)
42 ss := spinnerStyle{
43 meta: func(s string) string { return s },
44 }
3945 if len(frames) != 0 {
40 ss.frames = append(ss.frames, frames...)
46 ss.frames = frames
4147 } else {
42 ss.frames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
48 ss.frames = defaultSpinnerStyle[:]
4349 }
4450 return ss
4551 }
4652
47 func (s *spinnerStyle) PositionLeft() SpinnerStyleComposer {
53 func (s spinnerStyle) PositionLeft() SpinnerStyleComposer {
4854 s.position = positionLeft
4955 return s
5056 }
5157
52 func (s *spinnerStyle) PositionRight() SpinnerStyleComposer {
58 func (s spinnerStyle) PositionRight() SpinnerStyleComposer {
5359 s.position = positionRight
5460 return s
5561 }
5662
57 func (s *spinnerStyle) Build() BarFiller {
63 func (s spinnerStyle) Meta(fn func(string) string) SpinnerStyleComposer {
64 s.meta = fn
65 return s
66 }
67
68 func (s spinnerStyle) Build() BarFiller {
5869 sf := &sFiller{
59 position: s.position,
60 frames: s.frames,
70 frames: s.frames,
71 meta: s.meta,
72 }
73 switch s.position {
74 case positionLeft:
75 sf.position = func(frame string, padWidth int) string {
76 return frame + strings.Repeat(" ", padWidth)
77 }
78 case positionRight:
79 sf.position = func(frame string, padWidth int) string {
80 return strings.Repeat(" ", padWidth) + frame
81 }
82 default:
83 sf.position = func(frame string, padWidth int) string {
84 return strings.Repeat(" ", padWidth/2) + frame + strings.Repeat(" ", padWidth/2+padWidth%2)
85 }
6186 }
6287 return sf
6388 }
6489
65 func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
66 width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
67
90 func (s *sFiller) Fill(w io.Writer, stat decor.Statistics) error {
91 width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
6892 frame := s.frames[s.count%uint(len(s.frames))]
69 frameWidth := runewidth.StringWidth(stripansi.Strip(frame))
93 frameWidth := runewidth.StringWidth(frame)
94 s.count++
7095
7196 if width < frameWidth {
72 return
97 return nil
7398 }
7499
75 var err error
76 rest := width - frameWidth
77 switch s.position {
78 case positionLeft:
79 _, err = io.WriteString(w, frame+strings.Repeat(" ", rest))
80 case positionRight:
81 _, err = io.WriteString(w, strings.Repeat(" ", rest)+frame)
82 default:
83 str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
84 _, err = io.WriteString(w, str)
85 }
86 if err != nil {
87 panic(err)
88 }
89 s.count++
100 _, err := io.WriteString(w, s.position(s.meta(frame), width-frameWidth))
101 return err
90102 }
33 "bytes"
44 "io"
55
6 "github.com/vbauerster/mpb/v7/decor"
6 "github.com/vbauerster/mpb/v8/decor"
77 )
88
99 // BarOption is a func option to alter default behavior of a bar.
1010 type BarOption func(*bState)
1111
12 func skipNil(decorators []decor.Decorator) (filtered []decor.Decorator) {
13 for _, d := range decorators {
14 if d != nil {
15 filtered = append(filtered, d)
16 }
12 func inspect(decorators []decor.Decorator) (dest []decor.Decorator) {
13 for _, decorator := range decorators {
14 if decorator == nil {
15 continue
16 }
17 dest = append(dest, decorator)
1718 }
1819 return
19 }
20
21 func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
22 type mergeWrapper interface {
23 MergeUnwrap() []decor.Decorator
24 }
25 for _, decorator := range decorators {
26 if mw, ok := decorator.(mergeWrapper); ok {
27 *dest = append(*dest, mw.MergeUnwrap()...)
28 }
29 *dest = append(*dest, decorator)
30 }
3120 }
3221
3322 // AppendDecorators let you inject decorators to the bar's right side.
3423 func AppendDecorators(decorators ...decor.Decorator) BarOption {
35 return func(s *bState) {
36 s.addDecorators(&s.aDecorators, skipNil(decorators)...)
24 decorators = inspect(decorators)
25 return func(s *bState) {
26 s.aDecorators = decorators
3727 }
3828 }
3929
4030 // PrependDecorators let you inject decorators to the bar's left side.
4131 func PrependDecorators(decorators ...decor.Decorator) BarOption {
42 return func(s *bState) {
43 s.addDecorators(&s.pDecorators, skipNil(decorators)...)
32 decorators = inspect(decorators)
33 return func(s *bState) {
34 s.pDecorators = decorators
4435 }
4536 }
4637
5849 }
5950 }
6051
61 // BarQueueAfter queues this (being constructed) bar to relplace
62 // runningBar after it has been completed.
63 func BarQueueAfter(runningBar *Bar) BarOption {
64 if runningBar == nil {
65 return nil
66 }
67 return func(s *bState) {
68 s.runningBar = runningBar
52 // BarQueueAfter puts this (being constructed) bar into the queue.
53 // BarPriority will be inherited from the argument bar.
54 // When argument bar completes or aborts queued bar replaces its place.
55 func BarQueueAfter(bar *Bar) BarOption {
56 return func(s *bState) {
57 s.waitBar = bar
6958 }
7059 }
7160
7362 // on complete event.
7463 func BarRemoveOnComplete() BarOption {
7564 return func(s *bState) {
76 s.dropOnComplete = true
65 s.rmOnComplete = true
7766 }
7867 }
7968
8675 // BarFillerOnComplete replaces bar's filler with message, on complete event.
8776 func BarFillerOnComplete(message string) BarOption {
8877 return BarFillerMiddleware(func(base BarFiller) BarFiller {
89 return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
78 return BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
9079 if st.Completed {
9180 _, err := io.WriteString(w, message)
92 if err != nil {
93 panic(err)
94 }
95 } else {
96 base.Fill(w, reqWidth, st)
81 return err
9782 }
83 return base.Fill(w, st)
9884 })
9985 })
10086 }
10187
10288 // BarFillerMiddleware provides a way to augment the underlying BarFiller.
10389 func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption {
104 return func(s *bState) {
105 s.middleware = middle
90 if middle == nil {
91 return nil
92 }
93 return func(s *bState) {
94 s.filler = middle(s.filler)
10695 }
10796 }
10897
10998 // BarPriority sets bar's priority. Zero is highest priority, i.e. bar
110 // will be on top. If `BarReplaceOnComplete` option is supplied, this
111 // option is ignored.
99 // will be on top. This option isn't effective with `BarQueueAfter` option.
112100 func BarPriority(priority int) BarOption {
113101 return func(s *bState) {
114102 s.priority = priority
115103 }
116104 }
117105
118 // BarExtender provides a way to extend bar to the next new line.
119 func BarExtender(filler BarFiller) BarOption {
106 // BarExtender extends bar with arbitrary lines. Provided BarFiller will be
107 // called at each render/flush cycle. Any lines written to the underlying
108 // io.Writer will extend the bar either in above (rev = true) or below
109 // (rev = false) direction.
110 func BarExtender(filler BarFiller, rev bool) BarOption {
120111 if filler == nil {
121112 return nil
122113 }
123 return func(s *bState) {
124 s.extender = makeExtenderFunc(filler)
125 }
126 }
127
128 func makeExtenderFunc(filler BarFiller) extenderFunc {
114 fn := makeExtenderFunc(filler, rev)
115 return func(s *bState) {
116 s.extender = fn
117 }
118 }
119
120 func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc {
129121 buf := new(bytes.Buffer)
130 return func(r io.Reader, reqWidth int, st decor.Statistics) (io.Reader, int) {
131 filler.Fill(buf, reqWidth, st)
132 return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), []byte("\n"))
122 base := func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
123 err := filler.Fill(buf, stat)
124 if err != nil {
125 buf.Reset()
126 return rows, err
127 }
128 for {
129 b, err := buf.ReadBytes('\n')
130 if err != nil {
131 break
132 }
133 rows = append(rows, bytes.NewReader(b))
134 }
135 buf.Reset()
136 return rows, err
137 }
138
139 if !rev {
140 return base
141 }
142 return func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
143 rows, err := base(rows, stat)
144 if err != nil {
145 return rows, err
146 }
147 for left, right := 0, len(rows)-1; left < right; left, right = left+1, right-1 {
148 rows[left], rows[right] = rows[right], rows[left]
149 }
150 return rows, err
133151 }
134152 }
135153
148166 }
149167 }
150168
151 // BarOptional will invoke provided option only when cond is true.
169 // BarOptional will return provided option only when cond is true.
152170 func BarOptional(option BarOption, cond bool) BarOption {
153171 if cond {
154172 return option
156174 return nil
157175 }
158176
159 // BarOptOn will invoke provided option only when higher order predicate
160 // evaluates to true.
177 // BarOptOn will return provided option only when predicate evaluates to true.
161178 func BarOptOn(option BarOption, predicate func() bool) BarOption {
162179 if predicate() {
163180 return option
164181 }
165182 return nil
166183 }
184
185 // BarFuncOptional will call option and return its value only when cond is true.
186 func BarFuncOptional(option func() BarOption, cond bool) BarOption {
187 if cond {
188 return option()
189 }
190 return nil
191 }
192
193 // BarFuncOptOn will call option and return its value only when predicate evaluates to true.
194 func BarFuncOptOn(option func() BarOption, predicate func() bool) BarOption {
195 if predicate() {
196 return option()
197 }
198 return nil
199 }
11
22 import (
33 "bytes"
4 "context"
45 "fmt"
5 "io/ioutil"
6 "io"
67 "strings"
7 "sync/atomic"
88 "testing"
99 "time"
1010 "unicode/utf8"
1111
12 "github.com/vbauerster/mpb/v7"
13 "github.com/vbauerster/mpb/v7/decor"
12 "github.com/vbauerster/mpb/v8"
13 "github.com/vbauerster/mpb/v8/decor"
1414 )
1515
1616 func TestBarCompleted(t *testing.T) {
17 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(ioutil.Discard))
17 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
1818 total := 80
1919 bar := p.AddBar(int64(total))
2020
21 var count int
22 for !bar.Completed() {
23 time.Sleep(10 * time.Millisecond)
24 bar.Increment()
25 count++
26 }
27
28 p.Wait()
29 if count != total {
30 t.Errorf("got count: %d, expected %d\n", count, total)
31 }
21 if bar.Completed() {
22 t.Fail()
23 }
24
25 bar.IncrBy(total)
26
27 if !bar.Completed() {
28 t.Error("bar isn't completed after increment")
29 }
30
31 p.Wait()
32 }
33
34 func TestBarAborted(t *testing.T) {
35 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
36 total := 80
37 bar := p.AddBar(int64(total))
38
39 if bar.Aborted() {
40 t.Fail()
41 }
42
43 bar.Abort(false)
44
45 if !bar.Aborted() {
46 t.Error("bar isn't aborted after abort call")
47 }
48
49 p.Wait()
50 }
51
52 func TestBarSetTotal(t *testing.T) {
53 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
54 bar := p.AddBar(0)
55
56 bar.SetTotal(0, false)
57 if bar.Completed() {
58 t.Error("expected bar not to complete")
59 }
60
61 bar.SetTotal(0, true)
62 if !bar.Completed() {
63 t.Error("expected bar to complete")
64 }
65
66 p.Wait()
67 }
68
69 func TestBarEnableTriggerCompleteAndIncrementBefore(t *testing.T) {
70 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
71 bar := p.AddBar(0) // never complete bar
72
73 for _, f := range []func(){
74 func() { bar.SetTotal(40, false) },
75 func() { bar.IncrBy(60) },
76 func() { bar.SetTotal(80, false) },
77 func() { bar.IncrBy(20) },
78 } {
79 f()
80 if bar.Completed() {
81 t.Fail()
82 }
83 }
84
85 bar.EnableTriggerComplete()
86
87 if !bar.Completed() {
88 t.Fail()
89 }
90
91 p.Wait()
92 }
93
94 func TestBarEnableTriggerCompleteAndIncrementAfter(t *testing.T) {
95 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
96 bar := p.AddBar(0) // never complete bar
97
98 for _, f := range []func(){
99 func() { bar.SetTotal(40, false) },
100 func() { bar.IncrBy(60) },
101 func() { bar.SetTotal(80, false) },
102 func() { bar.EnableTriggerComplete() },
103 } {
104 f()
105 if bar.Completed() {
106 t.Fail()
107 }
108 }
109
110 bar.IncrBy(20)
111
112 if !bar.Completed() {
113 t.Fail()
114 }
115
116 p.Wait()
32117 }
33118
34119 func TestBarID(t *testing.T) {
35 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(ioutil.Discard))
120 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(io.Discard))
36121 total := 100
37122 wantID := 11
38123 bar := p.AddBar(int64(total), mpb.BarID(wantID))
39124
40 go func() {
41 for i := 0; i < total; i++ {
42 time.Sleep(50 * time.Millisecond)
43 bar.Increment()
44 }
45 }()
46
47125 gotID := bar.ID()
48126 if gotID != wantID {
49 t.Errorf("Expected bar id: %d, got %d\n", wantID, gotID)
50 }
51
52 bar.Abort(true)
127 t.Errorf("Expected bar id: %d, got %d", wantID, gotID)
128 }
129
130 bar.IncrBy(total)
131
53132 p.Wait()
54133 }
55134
56135 func TestBarSetRefill(t *testing.T) {
57136 var buf bytes.Buffer
58
59 p := mpb.New(mpb.WithOutput(&buf), mpb.WithWidth(100))
137 p := mpb.New(
138 mpb.WithWidth(100),
139 mpb.WithOutput(&buf),
140 mpb.WithAutoRefresh(),
141 )
60142
61143 total := 100
62144 till := 30
64146
65147 bar := p.New(int64(total), mpb.BarStyle().Refiller(refiller), mpb.BarFillerTrim())
66148
149 bar.IncrBy(till)
67150 bar.SetRefill(int64(till))
68 bar.IncrBy(till)
69
70 for i := 0; i < total-till; i++ {
71 bar.Increment()
72 time.Sleep(10 * time.Millisecond)
73 }
151 bar.IncrBy(total - till)
74152
75153 p.Wait()
76154
79157 strings.Repeat("=", total-till-1),
80158 )
81159
82 got := string(getLastLine(buf.Bytes()))
160 got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
83161
84162 if !strings.Contains(got, wantBar) {
85 t.Errorf("Want bar: %q, got bar: %q\n", wantBar, got)
86 }
87 }
88
89 func TestBarHas100PercentWithOnCompleteDecorator(t *testing.T) {
90 var buf bytes.Buffer
91
92 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(&buf))
93
94 total := 50
95
96 bar := p.AddBar(int64(total),
97 mpb.AppendDecorators(
98 decor.OnComplete(
99 decor.Percentage(), "done",
100 ),
101 ),
102 )
103
104 for i := 0; i < total; i++ {
105 bar.Increment()
106 time.Sleep(10 * time.Millisecond)
107 }
108
109 p.Wait()
110
111 hundred := "100 %"
112 if !bytes.Contains(buf.Bytes(), []byte(hundred)) {
113 t.Errorf("Bar's buffer does not contain: %q\n", hundred)
163 t.Errorf("Want bar: %q, got bar: %q", wantBar, got)
114164 }
115165 }
116166
117167 func TestBarHas100PercentWithBarRemoveOnComplete(t *testing.T) {
118168 var buf bytes.Buffer
119
120 p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(&buf))
169 p := mpb.New(
170 mpb.WithWidth(80),
171 mpb.WithOutput(&buf),
172 mpb.WithAutoRefresh(),
173 )
121174
122175 total := 50
123176
126179 mpb.AppendDecorators(decor.Percentage()),
127180 )
128181
129 for i := 0; i < total; i++ {
130 bar.Increment()
131 time.Sleep(10 * time.Millisecond)
132 }
182 bar.IncrBy(total)
133183
134184 p.Wait()
135185
136186 hundred := "100 %"
137187 if !bytes.Contains(buf.Bytes(), []byte(hundred)) {
138 t.Errorf("Bar's buffer does not contain: %q\n", hundred)
188 t.Errorf("Bar's buffer does not contain: %q", hundred)
139189 }
140190 }
141191
144194 customFormat := "╢▌▌░╟"
145195 runes := []rune(customFormat)
146196 total := 80
147 p := mpb.New(mpb.WithWidth(total), mpb.WithOutput(&buf))
197 p := mpb.New(
198 mpb.WithWidth(80),
199 mpb.WithOutput(&buf),
200 mpb.WithAutoRefresh(),
201 )
148202 bs := mpb.BarStyle()
149 bs.Lbound(string(runes[0]))
150 bs.Filler(string(runes[1]))
151 bs.Tip(string(runes[2]))
152 bs.Padding(string(runes[3]))
153 bs.Rbound(string(runes[4]))
203 bs = bs.Lbound(string(runes[0]))
204 bs = bs.Filler(string(runes[1]))
205 bs = bs.Tip(string(runes[2]))
206 bs = bs.Padding(string(runes[3]))
207 bs = bs.Rbound(string(runes[4]))
154208 bar := p.New(int64(total), bs, mpb.BarFillerTrim())
155209
156 for i := 0; i < total; i++ {
157 bar.Increment()
158 time.Sleep(10 * time.Millisecond)
159 }
210 bar.IncrBy(total)
160211
161212 p.Wait()
162213
166217 string(runes[2]),
167218 string(runes[4]),
168219 )
169 got := string(getLastLine(buf.Bytes()))
220 got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
170221
171222 if !strings.Contains(got, wantBar) {
172 t.Errorf("Want bar: %q:%d, got bar: %q:%d\n", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got))
173 }
174 }
175
176 func TestBarPanicBeforeComplete(t *testing.T) {
177 var buf bytes.Buffer
178 p := mpb.New(
179 mpb.WithWidth(80),
180 mpb.WithDebugOutput(&buf),
181 mpb.WithOutput(ioutil.Discard),
182 )
183
184 total := 100
185 panicMsg := "Upps!!!"
186 var pCount uint32
187 bar := p.AddBar(int64(total),
188 mpb.PrependDecorators(panicDecorator(panicMsg,
189 func(st decor.Statistics) bool {
190 if st.Current >= 42 {
191 atomic.AddUint32(&pCount, 1)
192 return true
193 }
194 return false
195 },
196 )),
197 )
198
199 for i := 0; i < total; i++ {
200 time.Sleep(10 * time.Millisecond)
201 bar.Increment()
202 }
203
204 p.Wait()
205
206 if pCount != 1 {
207 t.Errorf("Decor called after panic %d times\n", pCount-1)
208 }
209
210 barStr := buf.String()
211 if !strings.Contains(barStr, panicMsg) {
212 t.Errorf("%q doesn't contain %q\n", barStr, panicMsg)
213 }
214 }
215
216 func TestBarPanicAfterComplete(t *testing.T) {
217 var buf bytes.Buffer
218 p := mpb.New(
219 mpb.WithWidth(80),
220 mpb.WithDebugOutput(&buf),
221 mpb.WithOutput(ioutil.Discard),
222 )
223
224 total := 100
225 panicMsg := "Upps!!!"
226 var pCount uint32
227 bar := p.AddBar(int64(total),
228 mpb.PrependDecorators(panicDecorator(panicMsg,
229 func(st decor.Statistics) bool {
230 if st.Completed {
231 atomic.AddUint32(&pCount, 1)
232 return true
233 }
234 return false
235 },
236 )),
237 )
238
239 for i := 0; i < total; i++ {
240 time.Sleep(10 * time.Millisecond)
241 bar.Increment()
242 }
243
244 p.Wait()
245
246 if pCount > 2 {
247 t.Error("Decor called after panic more than 2 times\n")
248 }
249
250 barStr := buf.String()
251 if !strings.Contains(barStr, panicMsg) {
252 t.Errorf("%q doesn't contain %q\n", barStr, panicMsg)
223 t.Errorf("Want bar: %q:%d, got bar: %q:%d", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got))
253224 }
254225 }
255226
256227 func TestDecorStatisticsAvailableWidth(t *testing.T) {
257 total := 100
258 down := make(chan struct{})
259 checkDone := make(chan struct{})
228 ch := make(chan int, 2)
260229 td1 := func(s decor.Statistics) string {
261 if s.AvailableWidth != 80 {
262 t.Errorf("expected AvailableWidth %d got %d\n", 80, s.AvailableWidth)
263 }
264 return fmt.Sprintf("\x1b[31;1;4m%s\x1b[0m", strings.Repeat("0", 20))
230 ch <- s.AvailableWidth
231 return strings.Repeat("0", 20)
265232 }
266233 td2 := func(s decor.Statistics) string {
267 defer func() {
268 select {
269 case checkDone <- struct{}{}:
270 default:
271 }
272 }()
273 if s.AvailableWidth != 40 {
274 t.Errorf("expected AvailableWidth %d got %d\n", 40, s.AvailableWidth)
275 }
234 ch <- s.AvailableWidth
276235 return ""
277236 }
278 p := mpb.New(
237 ctx, cancel := context.WithCancel(context.Background())
238 refresh := make(chan interface{})
239 p := mpb.NewWithContext(ctx,
279240 mpb.WithWidth(100),
280 mpb.WithShutdownNotifier(down),
281 mpb.WithOutput(ioutil.Discard),
282 )
283 bar := p.AddBar(int64(total),
241 mpb.WithManualRefresh(refresh),
242 mpb.WithOutput(io.Discard),
243 )
244 _ = p.AddBar(0,
284245 mpb.BarFillerTrim(),
285246 mpb.PrependDecorators(
286247 decor.Name(strings.Repeat("0", 20)),
287 decor.Any(td1),
248 decor.Meta(
249 decor.Any(td1),
250 func(s string) string {
251 return "\x1b[31;1m" + s + "\x1b[0m"
252 },
253 ),
288254 ),
289255 mpb.AppendDecorators(
290256 decor.Name(strings.Repeat("0", 20)),
291257 decor.Any(td2),
292258 ),
293259 )
260 refresh <- time.Now()
294261 go func() {
295 for {
296 select {
297 case <-checkDone:
298 bar.Abort(true)
299 case <-down:
300 return
301 }
302 }
262 time.Sleep(10 * time.Millisecond)
263 cancel()
303264 }()
304 for !bar.Completed() {
305 bar.Increment()
306 }
307 p.Wait()
308 }
309
310 func panicDecorator(panicMsg string, cond func(decor.Statistics) bool) decor.Decorator {
311 return decor.Any(func(st decor.Statistics) string {
312 if cond(st) {
313 panic(panicMsg)
314 }
315 return ""
316 })
317 }
265 p.Wait()
266
267 if availableWidth := <-ch; availableWidth != 80 {
268 t.Errorf("expected AvailableWidth %d got %d", 80, availableWidth)
269 }
270
271 if availableWidth := <-ch; availableWidth != 40 {
272 t.Errorf("expected AvailableWidth %d got %d", 40, availableWidth)
273 }
274 }
275
276 func TestBarQueueAfterBar(t *testing.T) {
277 shutdown := make(chan interface{})
278 ctx, cancel := context.WithCancel(context.Background())
279 p := mpb.NewWithContext(ctx,
280 mpb.WithOutput(io.Discard),
281 mpb.WithAutoRefresh(),
282 mpb.WithShutdownNotifier(shutdown),
283 )
284 a := p.AddBar(100)
285 b := p.AddBar(100, mpb.BarQueueAfter(a))
286 identity := map[*mpb.Bar]string{
287 a: "a",
288 b: "b",
289 }
290
291 a.IncrBy(100)
292 a.Wait()
293 cancel()
294
295 bars := (<-shutdown).([]*mpb.Bar)
296 if l := len(bars); l != 1 {
297 t.Errorf("Expected len of bars: %d, got: %d", 1, l)
298 }
299
300 p.Wait()
301 if bars[0] != b {
302 t.Errorf("Expected bars[0] == b, got: %s", identity[bars[0]])
303 }
304 }
0 package mpb
0 package mpb_test
11
22 import (
3 "io"
34 "sync"
45 "testing"
6
7 "github.com/vbauerster/mpb/v8"
58 )
69
710 const total = 1000
811
9 func BenchmarkIncrementOneBar(b *testing.B) {
10 benchBody(1, b)
12 func BenchmarkNopStyle1Bar(b *testing.B) {
13 bench(b, mpb.NopStyle(), false, 1)
1114 }
1215
13 func BenchmarkIncrementTwoBars(b *testing.B) {
14 benchBody(2, b)
16 func BenchmarkNopStyle1BarWithAutoRefresh(b *testing.B) {
17 bench(b, mpb.NopStyle(), true, 1)
1518 }
1619
17 func BenchmarkIncrementThreeBars(b *testing.B) {
18 benchBody(3, b)
20 func BenchmarkNopStyle2Bars(b *testing.B) {
21 bench(b, mpb.NopStyle(), false, 2)
1922 }
2023
21 func BenchmarkIncrementFourBars(b *testing.B) {
22 benchBody(4, b)
24 func BenchmarkNopStyle2BarsWithAutoRefresh(b *testing.B) {
25 bench(b, mpb.NopStyle(), true, 2)
2326 }
2427
25 func benchBody(n int, b *testing.B) {
26 p := New(WithOutput(nil), WithWidth(80))
27 wg := new(sync.WaitGroup)
28 func BenchmarkNopStyle3Bars(b *testing.B) {
29 bench(b, mpb.NopStyle(), false, 3)
30 }
31
32 func BenchmarkNopStyle3BarsWithAutoRefresh(b *testing.B) {
33 bench(b, mpb.NopStyle(), true, 3)
34 }
35
36 func BenchmarkBarStyle1Bar(b *testing.B) {
37 bench(b, mpb.BarStyle(), false, 1)
38 }
39
40 func BenchmarkBarStyle1BarWithAutoRefresh(b *testing.B) {
41 bench(b, mpb.BarStyle(), true, 1)
42 }
43
44 func BenchmarkBarStyle2Bars(b *testing.B) {
45 bench(b, mpb.BarStyle(), false, 2)
46 }
47
48 func BenchmarkBarStyle2BarsWithAutoRefresh(b *testing.B) {
49 bench(b, mpb.BarStyle(), true, 2)
50 }
51
52 func BenchmarkBarStyle3Bars(b *testing.B) {
53 bench(b, mpb.BarStyle(), false, 3)
54 }
55
56 func BenchmarkBarStyle3BarsWithAutoRefresh(b *testing.B) {
57 bench(b, mpb.BarStyle(), true, 3)
58 }
59
60 func bench(b *testing.B, builder mpb.BarFillerBuilder, autoRefresh bool, n int) {
61 var wg sync.WaitGroup
62 p := mpb.New(
63 mpb.WithWidth(100),
64 mpb.WithOutput(io.Discard),
65 mpb.ContainerOptional(mpb.WithAutoRefresh(), autoRefresh),
66 )
2867 b.ResetTimer()
2968 for i := 0; i < b.N; i++ {
3069 for j := 0; j < n; j++ {
70 bar := p.New(total, builder)
3171 switch j {
3272 case n - 1:
33 bar := p.AddBar(total)
34 for c := 0; c < total; c++ {
35 bar.Increment()
36 }
37 if !bar.Completed() {
38 b.Fail()
39 }
73 complete(b, bar)
4074 default:
4175 wg.Add(1)
4276 go func() {
43 bar := p.AddBar(total)
44 for c := 0; c < total; c++ {
45 bar.Increment()
46 }
47 if !bar.Completed() {
48 b.Fail()
49 }
77 complete(b, bar)
5078 wg.Done()
5179 }()
5280 }
5583 }
5684 p.Wait()
5785 }
86
87 func complete(b *testing.B, bar *mpb.Bar) {
88 for i := 0; i < total; i++ {
89 bar.Increment()
90 }
91 bar.Wait()
92 }
11
22 import (
33 "io"
4 "io/ioutil"
54 "sync"
65 "time"
76 )
3029 }
3130 }
3231
33 // WithRefreshRate overrides default 120ms refresh rate.
32 // WithRefreshRate overrides default 150ms refresh rate.
3433 func WithRefreshRate(d time.Duration) ContainerOption {
3534 return func(s *pState) {
36 s.rr = d
35 s.refreshRate = d
3736 }
3837 }
3938
4140 // Refresh will occur upon receive value from provided ch.
4241 func WithManualRefresh(ch <-chan interface{}) ContainerOption {
4342 return func(s *pState) {
44 s.externalRefresh = ch
43 s.manualRC = ch
4544 }
4645 }
4746
5150 // rendering will start as soon as provided chan is closed.
5251 func WithRenderDelay(ch <-chan struct{}) ContainerOption {
5352 return func(s *pState) {
54 s.renderDelay = ch
53 s.delayRC = ch
5554 }
5655 }
5756
58 // WithShutdownNotifier provided chanel will be closed, after all bars
59 // have been rendered.
60 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 {
6160 return func(s *pState) {
62 select {
63 case <-ch:
64 default:
65 s.shutdownNotifier = ch
66 }
61 s.shutdownNotifier = ch
6762 }
6863 }
6964
70 // WithOutput overrides default os.Stdout output. Setting it to nil
71 // will effectively disable auto refresh rate and discard any output,
72 // 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.
7368 func WithOutput(w io.Writer) ContainerOption {
69 if w == nil {
70 w = io.Discard
71 }
7472 return func(s *pState) {
75 if w == nil {
76 s.output = ioutil.Discard
77 s.outputDiscarded = true
78 return
79 }
8073 s.output = w
8174 }
8275 }
8477 // WithDebugOutput sets debug output.
8578 func WithDebugOutput(w io.Writer) ContainerOption {
8679 if w == nil {
87 return nil
80 w = io.Discard
8881 }
8982 return func(s *pState) {
9083 s.debugOut = w
9184 }
9285 }
9386
94 // PopCompletedMode will pop and stop rendering completed bars.
87 // WithAutoRefresh force auto refresh regardless of what output is set to.
88 // Applicable only if not WithManualRefresh set.
89 func WithAutoRefresh() ContainerOption {
90 return func(s *pState) {
91 s.autoRefresh = true
92 }
93 }
94
95 // PopCompletedMode will pop completed bars to the top.
96 // To stop rendering bar after it has been popped, use
97 // mpb.BarRemoveOnComplete() option on that bar.
9598 func PopCompletedMode() ContainerOption {
9699 return func(s *pState) {
97100 s.popCompleted = true
98101 }
99102 }
100103
101 // ContainerOptional will invoke provided option only when cond is true.
104 // ContainerOptional will return provided option only when cond is true.
102105 func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
103106 if cond {
104107 return option
106109 return nil
107110 }
108111
109 // ContainerOptOn will invoke provided option only when higher order
110 // predicate evaluates to true.
112 // ContainerOptOn will return provided option only when predicate evaluates to true.
111113 func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption {
112114 if predicate() {
113115 return option
114116 }
115117 return nil
116118 }
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.lines = 4
38 func BenchmarkWithCurrentImpl(b *testing.B) {
39 w := New(out)
40 b.ResetTimer()
3541 for i := 0; i < b.N; i++ {
36 _ = w.ansiCuuAndEd()
42 _ = w.ew.ansiCuuAndEd(out, lines)
3743 }
3844 }
0 // +build darwin dragonfly freebsd netbsd openbsd
0 //go:build darwin || dragonfly || freebsd || netbsd || openbsd
11
22 package cwriter
33
0 // +build aix linux
0 //go:build aix || linux
11
22 package cwriter
33
0 // +build solaris
0 //go:build solaris
11
22 package cwriter
33
0 // +build zos
0 //go:build zos
11
22 package cwriter
33
77 "strconv"
88 )
99
10 // ErrNotTTY not a TeleTYpewriter error.
11 var ErrNotTTY = errors.New("not a terminal")
12
13 // http://ascii-table.com/ansi-escape-sequences.php
10 // https://github.com/dylanaraps/pure-sh-bible#cursor-movement
1411 const (
1512 escOpen = "\x1b["
1613 cuuAndEd = "A\x1b[J"
1714 )
1815
19 // Writer is a buffered the writer that updates the terminal. The
20 // contents of writer will be flushed when Flush is called.
21 type Writer struct {
22 out io.Writer
23 buf bytes.Buffer
24 lines 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(lines int) (err error) {
41 // some terminals interpret 'cursor up 0' as 'cursor up 1'
42 if w.lines > 0 {
43 err = w.clearLines()
44 if err != nil {
45 return
46 }
47 }
48 w.lines = lines
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)
61 }
52 type escWriter []byte
6253
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() error {
79 buf := make([]byte, 8)
80 buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lines), 10)
81 _, err := w.out.Write(append(buf, cuuAndEd...))
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)...))
8257 return err
8358 }
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.lines)
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.lines)
71 count := uint32(info.Size.X) * uint32(n)
4372 _, _, _ = procFillConsoleOutputCharacter.Call(
4473 uintptr(w.fd),
4574 uintptr(' '),
5180 }
5281
5382 // GetSize returns the visible dimensions of the given terminal.
54 //
5583 // These dimensions don't include any scrollback buffer height.
5684 func GetSize(fd int) (width, height int, err error) {
5785 var info windows.ConsoleScreenBufferInfo
00 package decor
11
2 // Any decorator displays text, that can be changed during decorator's
3 // lifetime via provided DecorFunc.
2 var _ Decorator = any{}
3
4 // Any decorator.
5 // Converts DecorFunc into Decorator.
46 //
57 // `fn` DecorFunc callback
6 //
78 // `wcc` optional WC config
8 //
99 func Any(fn DecorFunc, wcc ...WC) Decorator {
10 return &any{initWC(wcc...), fn}
10 return any{initWC(wcc...), fn}
1111 }
1212
1313 type any struct {
1515 fn DecorFunc
1616 }
1717
18 func (d *any) Decor(s Statistics) string {
19 return d.FormatMsg(d.fn(s))
18 func (d any) Decor(s Statistics) (string, int) {
19 return d.Format(d.fn(s))
2020 }
11
22 import (
33 "fmt"
4 "strings"
5 )
6
7 const (
8 _ = iota
9 UnitKiB
10 UnitKB
114 )
125
136 // CountersNoUnit is a wrapper around Counters with no unit param.
169 }
1710
1811 // CountersKibiByte is a wrapper around Counters with predefined unit
19 // UnitKiB (bytes/1024).
12 // as SizeB1024(0).
2013 func CountersKibiByte(pairFmt string, wcc ...WC) Decorator {
21 return Counters(UnitKiB, pairFmt, wcc...)
14 return Counters(SizeB1024(0), pairFmt, wcc...)
2215 }
2316
2417 // CountersKiloByte is a wrapper around Counters with predefined unit
25 // UnitKB (bytes/1000).
18 // as SizeB1000(0).
2619 func CountersKiloByte(pairFmt string, wcc ...WC) Decorator {
27 return Counters(UnitKB, pairFmt, wcc...)
20 return Counters(SizeB1000(0), pairFmt, wcc...)
2821 }
2922
3023 // Counters decorator with dynamic unit measure adjustment.
3124 //
32 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
33 //
34 // `pairFmt` printf compatible verbs for current and total pair
35 //
36 // `wcc` optional WC config
37 //
38 // pairFmt example if unit=UnitKB:
39 //
25 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
26 //
27 // `pairFmt` printf compatible verbs for current and total
28 //
29 // `wcc` optional WC config
30 //
31 // pairFmt example if unit=SizeB1000(0):
32 //
33 // pairFmt="%d / %d" output: "1MB / 12MB"
34 // pairFmt="% d / % d" output: "1 MB / 12 MB"
4035 // pairFmt="%.1f / %.1f" output: "1.0MB / 12.0MB"
4136 // pairFmt="% .1f / % .1f" output: "1.0 MB / 12.0 MB"
42 // pairFmt="%d / %d" output: "1MB / 12MB"
43 // pairFmt="% d / % d" output: "1 MB / 12 MB"
44 //
45 func Counters(unit int, pairFmt string, wcc ...WC) Decorator {
46 producer := func(unit int, pairFmt string) DecorFunc {
47 if pairFmt == "" {
48 pairFmt = "%d / %d"
49 } else if strings.Count(pairFmt, "%") != 2 {
50 panic("expected pairFmt with exactly 2 verbs")
51 }
52 switch unit {
53 case UnitKiB:
37 // pairFmt="%f / %f" output: "1.000000MB / 12.000000MB"
38 // pairFmt="% f / % f" output: "1.000000 MB / 12.000000 MB"
39 func Counters(unit interface{}, pairFmt string, wcc ...WC) Decorator {
40 producer := func() DecorFunc {
41 switch unit.(type) {
42 case SizeB1024:
43 if pairFmt == "" {
44 pairFmt = "% d / % d"
45 }
5446 return func(s Statistics) string {
5547 return fmt.Sprintf(pairFmt, SizeB1024(s.Current), SizeB1024(s.Total))
5648 }
57 case UnitKB:
49 case SizeB1000:
50 if pairFmt == "" {
51 pairFmt = "% d / % d"
52 }
5853 return func(s Statistics) string {
5954 return fmt.Sprintf(pairFmt, SizeB1000(s.Current), SizeB1000(s.Total))
6055 }
6156 default:
57 if pairFmt == "" {
58 pairFmt = "%d / %d"
59 }
6260 return func(s Statistics) string {
6361 return fmt.Sprintf(pairFmt, s.Current, s.Total)
6462 }
6563 }
6664 }
67 return Any(producer(unit, pairFmt), wcc...)
65 return Any(producer(), wcc...)
6866 }
6967
7068 // TotalNoUnit is a wrapper around Total with no unit param.
7371 }
7472
7573 // TotalKibiByte is a wrapper around Total with predefined unit
76 // UnitKiB (bytes/1024).
74 // as SizeB1024(0).
7775 func TotalKibiByte(format string, wcc ...WC) Decorator {
78 return Total(UnitKiB, format, wcc...)
76 return Total(SizeB1024(0), format, wcc...)
7977 }
8078
8179 // TotalKiloByte is a wrapper around Total with predefined unit
82 // UnitKB (bytes/1000).
80 // as SizeB1000(0).
8381 func TotalKiloByte(format string, wcc ...WC) Decorator {
84 return Total(UnitKB, format, wcc...)
82 return Total(SizeB1000(0), format, wcc...)
8583 }
8684
8785 // Total decorator with dynamic unit measure adjustment.
8886 //
89 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
87 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
9088 //
9189 // `format` printf compatible verb for Total
9290 //
9391 // `wcc` optional WC config
9492 //
95 // format example if unit=UnitKiB:
96 //
93 // format example if unit=SizeB1024(0):
94 //
95 // format="%d" output: "12MiB"
96 // format="% d" output: "12 MiB"
9797 // format="%.1f" output: "12.0MiB"
9898 // format="% .1f" output: "12.0 MiB"
99 // format="%d" output: "12MiB"
100 // format="% d" output: "12 MiB"
101 //
102 func Total(unit int, format string, wcc ...WC) Decorator {
103 producer := func(unit int, format string) DecorFunc {
104 if format == "" {
105 format = "%d"
106 } else if strings.Count(format, "%") != 1 {
107 panic("expected format with exactly 1 verb")
108 }
109
110 switch unit {
111 case UnitKiB:
99 // format="%f" output: "12.000000MiB"
100 // format="% f" output: "12.000000 MiB"
101 func Total(unit interface{}, format string, wcc ...WC) Decorator {
102 producer := func() DecorFunc {
103 switch unit.(type) {
104 case SizeB1024:
105 if format == "" {
106 format = "% d"
107 }
112108 return func(s Statistics) string {
113109 return fmt.Sprintf(format, SizeB1024(s.Total))
114110 }
115 case UnitKB:
111 case SizeB1000:
112 if format == "" {
113 format = "% d"
114 }
116115 return func(s Statistics) string {
117116 return fmt.Sprintf(format, SizeB1000(s.Total))
118117 }
119118 default:
119 if format == "" {
120 format = "%d"
121 }
120122 return func(s Statistics) string {
121123 return fmt.Sprintf(format, s.Total)
122124 }
123125 }
124126 }
125 return Any(producer(unit, format), wcc...)
127 return Any(producer(), wcc...)
126128 }
127129
128130 // CurrentNoUnit is a wrapper around Current with no unit param.
131133 }
132134
133135 // CurrentKibiByte is a wrapper around Current with predefined unit
134 // UnitKiB (bytes/1024).
136 // as SizeB1024(0).
135137 func CurrentKibiByte(format string, wcc ...WC) Decorator {
136 return Current(UnitKiB, format, wcc...)
138 return Current(SizeB1024(0), format, wcc...)
137139 }
138140
139141 // CurrentKiloByte is a wrapper around Current with predefined unit
140 // UnitKB (bytes/1000).
142 // as SizeB1000(0).
141143 func CurrentKiloByte(format string, wcc ...WC) Decorator {
142 return Current(UnitKB, format, wcc...)
144 return Current(SizeB1000(0), format, wcc...)
143145 }
144146
145147 // Current decorator with dynamic unit measure adjustment.
146148 //
147 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
149 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
148150 //
149151 // `format` printf compatible verb for Current
150152 //
151153 // `wcc` optional WC config
152154 //
153 // format example if unit=UnitKiB:
154 //
155 // format example if unit=SizeB1024(0):
156 //
157 // format="%d" output: "12MiB"
158 // format="% d" output: "12 MiB"
155159 // format="%.1f" output: "12.0MiB"
156160 // format="% .1f" output: "12.0 MiB"
157 // format="%d" output: "12MiB"
158 // format="% d" output: "12 MiB"
159 //
160 func Current(unit int, format string, wcc ...WC) Decorator {
161 producer := func(unit int, format string) DecorFunc {
162 if format == "" {
163 format = "%d"
164 } else if strings.Count(format, "%") != 1 {
165 panic("expected format with exactly 1 verb")
166 }
167
168 switch unit {
169 case UnitKiB:
161 // format="%f" output: "12.000000MiB"
162 // format="% f" output: "12.000000 MiB"
163 func Current(unit interface{}, format string, wcc ...WC) Decorator {
164 producer := func() DecorFunc {
165 switch unit.(type) {
166 case SizeB1024:
167 if format == "" {
168 format = "% d"
169 }
170170 return func(s Statistics) string {
171171 return fmt.Sprintf(format, SizeB1024(s.Current))
172172 }
173 case UnitKB:
173 case SizeB1000:
174 if format == "" {
175 format = "% d"
176 }
174177 return func(s Statistics) string {
175178 return fmt.Sprintf(format, SizeB1000(s.Current))
176179 }
177180 default:
181 if format == "" {
182 format = "%d"
183 }
178184 return func(s Statistics) string {
179185 return fmt.Sprintf(format, s.Current)
180186 }
181187 }
182188 }
183 return Any(producer(unit, format), wcc...)
189 return Any(producer(), wcc...)
184190 }
185191
186192 // InvertedCurrentNoUnit is a wrapper around InvertedCurrent with no unit param.
189195 }
190196
191197 // InvertedCurrentKibiByte is a wrapper around InvertedCurrent with predefined unit
192 // UnitKiB (bytes/1024).
198 // as SizeB1024(0).
193199 func InvertedCurrentKibiByte(format string, wcc ...WC) Decorator {
194 return InvertedCurrent(UnitKiB, format, wcc...)
200 return InvertedCurrent(SizeB1024(0), format, wcc...)
195201 }
196202
197203 // InvertedCurrentKiloByte is a wrapper around InvertedCurrent with predefined unit
198 // UnitKB (bytes/1000).
204 // as SizeB1000(0).
199205 func InvertedCurrentKiloByte(format string, wcc ...WC) Decorator {
200 return InvertedCurrent(UnitKB, format, wcc...)
206 return InvertedCurrent(SizeB1000(0), format, wcc...)
201207 }
202208
203209 // InvertedCurrent decorator with dynamic unit measure adjustment.
204210 //
205 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
211 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
206212 //
207213 // `format` printf compatible verb for InvertedCurrent
208214 //
209215 // `wcc` optional WC config
210216 //
211 // format example if unit=UnitKiB:
212 //
217 // format example if unit=SizeB1024(0):
218 //
219 // format="%d" output: "12MiB"
220 // format="% d" output: "12 MiB"
213221 // format="%.1f" output: "12.0MiB"
214222 // format="% .1f" output: "12.0 MiB"
215 // format="%d" output: "12MiB"
216 // format="% d" output: "12 MiB"
217 //
218 func InvertedCurrent(unit int, format string, wcc ...WC) Decorator {
219 producer := func(unit int, format string) DecorFunc {
220 if format == "" {
221 format = "%d"
222 } else if strings.Count(format, "%") != 1 {
223 panic("expected format with exactly 1 verb")
224 }
225
226 switch unit {
227 case UnitKiB:
223 // format="%f" output: "12.000000MiB"
224 // format="% f" output: "12.000000 MiB"
225 func InvertedCurrent(unit interface{}, format string, wcc ...WC) Decorator {
226 producer := func() DecorFunc {
227 switch unit.(type) {
228 case SizeB1024:
229 if format == "" {
230 format = "% d"
231 }
228232 return func(s Statistics) string {
229233 return fmt.Sprintf(format, SizeB1024(s.Total-s.Current))
230234 }
231 case UnitKB:
235 case SizeB1000:
236 if format == "" {
237 format = "% d"
238 }
232239 return func(s Statistics) string {
233240 return fmt.Sprintf(format, SizeB1000(s.Total-s.Current))
234241 }
235242 default:
243 if format == "" {
244 format = "%d"
245 }
236246 return func(s Statistics) string {
237247 return fmt.Sprintf(format, s.Total-s.Current)
238248 }
239249 }
240250 }
241 return Any(producer(unit, format), wcc...)
242 }
251 return Any(producer(), wcc...)
252 }
33 "fmt"
44 "time"
55
6 "github.com/acarl005/stripansi"
76 "github.com/mattn/go-runewidth"
87 )
98
109 const (
1110 // DidentRight bit specifies identation direction.
12 // |foo |b | With DidentRight
13 // | foo| b| Without DidentRight
11 //
12 // |foo |b | With DidentRight
13 // | foo| b| Without DidentRight
1414 DidentRight = 1 << iota
1515
1616 // DextraSpace bit adds extra space, makes sense with DSyncWidth only.
4343 ET_STYLE_MMSS
4444 )
4545
46 // Statistics consists of progress related statistics, that Decorator
47 // may need.
46 // Statistics contains fields which are necessary for implementing
47 // `decor.Decorator` and `mpb.BarFiller` interfaces.
4848 type Statistics struct {
49 AvailableWidth int // calculated width initially equal to terminal width
50 RequestedWidth int // width set by `mpb.WithWidth`
4951 ID int
50 AvailableWidth int
5152 Total int64
5253 Current int64
5354 Refill int64
6465 // `DecorFunc` into a `Decorator` interface by using provided
6566 // `func Any(DecorFunc, ...WC) Decorator`.
6667 type Decorator interface {
67 Configurator
6868 Synchronizer
69 Decor(Statistics) string
69 Formatter
70 Decor(Statistics) (str string, viewWidth int)
7071 }
7172
7273 // DecorFunc func type.
73 // To be used with `func Any`(DecorFunc, ...WC) Decorator`.
74 // To be used with `func Any(DecorFunc, ...WC) Decorator`.
7475 type DecorFunc func(Statistics) string
7576
7677 // Synchronizer interface.
8081 Sync() (chan int, bool)
8182 }
8283
83 // Configurator interface.
84 type Configurator interface {
85 GetConf() WC
86 SetConf(WC)
84 // Formatter interface.
85 // Format method needs to be called from within Decorator.Decor method
86 // in order to format string according to decor.WC settings.
87 // No need to implement manually as long as decor.WC is embedded.
88 type Formatter interface {
89 Format(string) (str string, viewWidth int)
8790 }
8891
8992 // Wrapper interface.
9194 // it is necessary to implement this interface to retain functionality
9295 // of built-in Decorator.
9396 type Wrapper interface {
94 Base() Decorator
97 Unwrap() Decorator
9598 }
9699
97100 // EwmaDecorator interface.
111114 // If decorator needs to be notified once upon bar shutdown event, so
112115 // this is the right interface to implement.
113116 type ShutdownListener interface {
114 Shutdown()
117 OnShutdown()
115118 }
116119
117120 // Global convenience instances of WC with sync width bit set.
133136 wsync chan int
134137 }
135138
136 // FormatMsg formats final message according to WC.W and WC.C.
137 // Should be called by any Decorator implementation.
138 func (wc *WC) FormatMsg(msg string) string {
139 pureWidth := runewidth.StringWidth(msg)
140 stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
141 maxCell := wc.W
139 // Format should be called by any Decorator implementation.
140 // Returns formatted string and its view (visual) width.
141 func (wc WC) Format(str string) (string, int) {
142 viewWidth := runewidth.StringWidth(str)
143 if wc.W > viewWidth {
144 viewWidth = wc.W
145 }
142146 if (wc.C & DSyncWidth) != 0 {
143 cellCount := stripWidth
144147 if (wc.C & DextraSpace) != 0 {
145 cellCount++
148 viewWidth++
146149 }
147 wc.wsync <- cellCount
148 maxCell = <-wc.wsync
150 wc.wsync <- viewWidth
151 viewWidth = <-wc.wsync
149152 }
150 return wc.fill(msg, maxCell+(pureWidth-stripWidth))
153 return wc.fill(str, viewWidth), viewWidth
151154 }
152155
153156 // Init initializes width related config.
154157 func (wc *WC) Init() WC {
155 wc.fill = runewidth.FillLeft
156158 if (wc.C & DidentRight) != 0 {
157159 wc.fill = runewidth.FillRight
160 } else {
161 wc.fill = runewidth.FillLeft
158162 }
159163 if (wc.C & DSyncWidth) != 0 {
160164 // it's deliberate choice to override wsync on each Init() call,
165169 }
166170
167171 // Sync is implementation of Synchronizer interface.
168 func (wc *WC) Sync() (chan int, bool) {
172 func (wc WC) Sync() (chan int, bool) {
169173 if (wc.C&DSyncWidth) != 0 && wc.wsync == nil {
170174 panic(fmt.Sprintf("%T is not initialized", wc))
171175 }
172176 return wc.wsync, (wc.C & DSyncWidth) != 0
173 }
174
175 // GetConf is implementation of Configurator interface.
176 func (wc *WC) GetConf() WC {
177 return *wc
178 }
179
180 // SetConf is implementation of Configurator interface.
181 func (wc *WC) SetConf(conf WC) {
182 *wc = conf.Init()
183177 }
184178
185179 func initWC(wcc ...WC) WC {
0 // Package decor provides common decorators for "github.com/vbauerster/mpb/v7" module.
0 // Package decor provides common decorators for "github.com/vbauerster/mpb/v8" module.
11 //
22 // Some decorators returned by this package might have a closure state. It is ok to use
33 // decorators concurrently, unless you share the same decorator among multiple
55 //
66 // Don't:
77 //
8 // p := mpb.New()
9 // name := decor.Name("bar")
10 // p.AddBar(100, mpb.AppendDecorators(name))
11 // p.AddBar(100, mpb.AppendDecorators(name))
8 // p := mpb.New()
9 // name := decor.Name("bar")
10 // p.AddBar(100, mpb.AppendDecorators(name))
11 // p.AddBar(100, mpb.AppendDecorators(name))
1212 //
1313 // Do:
1414 //
88 // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
99 //
1010 // `wcc` optional WC config
11 //
1211 func Elapsed(style TimeStyle, wcc ...WC) Decorator {
1312 return NewElapsed(style, time.Now(), wcc...)
1413 }
2019 // `startTime` start time
2120 //
2221 // `wcc` optional WC config
23 //
2422 func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
2523 var msg string
2624 producer := chooseTimeProducer(style)
77 "github.com/VividCortex/ewma"
88 )
99
10 var (
11 _ Decorator = (*movingAverageETA)(nil)
12 _ EwmaDecorator = (*movingAverageETA)(nil)
13 _ Decorator = (*averageETA)(nil)
14 _ AverageDecorator = (*averageETA)(nil)
15 )
16
1017 // TimeNormalizer interface. Implementors could be passed into
1118 // MovingAverageETA, in order to affect i.e. normalize its output.
1219 type TimeNormalizer interface {
2128 return f(src)
2229 }
2330
24 // EwmaETA exponential-weighted-moving-average based ETA decorator.
25 // For this decorator to work correctly you have to measure each
26 // iteration's duration and pass it to the
27 // *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
31 // EwmaETA exponential-weighted-moving-average based ETA decorator. For this
32 // decorator to work correctly you have to measure each iteration's duration
33 // and pass it to one of the (*Bar).EwmaIncr... family methods.
2834 func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
2935 var average ewma.MovingAverage
3036 if age == 0 {
4450 // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
4551 //
4652 // `wcc` optional WC config
47 //
4853 func MovingAverageETA(style TimeStyle, average ewma.MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
4954 d := &movingAverageETA{
5055 WC: initWC(wcc...),
6267 producer func(time.Duration) string
6368 }
6469
65 func (d *movingAverageETA) Decor(s Statistics) string {
70 func (d *movingAverageETA) Decor(s Statistics) (string, int) {
6671 v := math.Round(d.average.Value())
6772 remaining := time.Duration((s.Total - s.Current) * int64(v))
6873 if d.normalizer != nil {
6974 remaining = d.normalizer.Normalize(remaining)
7075 }
71 return d.FormatMsg(d.producer(remaining))
76 return d.Format(d.producer(remaining))
7277 }
7378
7479 func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) {
8489 // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
8590 //
8691 // `wcc` optional WC config
87 //
8892 func AverageETA(style TimeStyle, wcc ...WC) Decorator {
8993 return NewAverageETA(style, time.Now(), nil, wcc...)
9094 }
98102 // `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
99103 //
100104 // `wcc` optional WC config
101 //
102105 func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator {
103106 d := &averageETA{
104107 WC: initWC(wcc...),
116119 producer func(time.Duration) string
117120 }
118121
119 func (d *averageETA) Decor(s Statistics) string {
122 func (d *averageETA) Decor(s Statistics) (string, int) {
120123 var remaining time.Duration
121124 if s.Current != 0 {
122125 durPerItem := float64(time.Since(d.startTime)) / float64(s.Current)
126129 remaining = d.normalizer.Normalize(remaining)
127130 }
128131 }
129 return d.FormatMsg(d.producer(remaining))
132 return d.Format(d.producer(remaining))
130133 }
131134
132135 func (d *averageETA) AverageAdjust(startTime time.Time) {
195198 }
196199 default:
197200 return func(remaining time.Duration) string {
198 // strip off nanoseconds
199 return ((remaining / time.Second) * time.Second).String()
200 }
201 }
202 }
201 return remaining.Truncate(time.Second).String()
202 }
203 }
204 }
+0
-110
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 decorator == nil {
20 return nil
21 }
22 if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 {
23 return decorator
24 }
25 md := &mergeDecorator{
26 Decorator: decorator,
27 wc: decorator.GetConf(),
28 placeHolders: make([]*placeHolderDecorator, len(placeholders)),
29 }
30 decorator.SetConf(WC{})
31 for i, wc := range placeholders {
32 if (wc.C & DSyncWidth) == 0 {
33 return decorator
34 }
35 md.placeHolders[i] = &placeHolderDecorator{wc.Init()}
36 }
37 return md
38 }
39
40 type mergeDecorator struct {
41 Decorator
42 wc WC
43 placeHolders []*placeHolderDecorator
44 }
45
46 func (d *mergeDecorator) GetConf() WC {
47 return d.wc
48 }
49
50 func (d *mergeDecorator) SetConf(conf WC) {
51 d.wc = conf.Init()
52 }
53
54 func (d *mergeDecorator) MergeUnwrap() []Decorator {
55 decorators := make([]Decorator, len(d.placeHolders))
56 for i, ph := range d.placeHolders {
57 decorators[i] = ph
58 }
59 return decorators
60 }
61
62 func (d *mergeDecorator) Sync() (chan int, bool) {
63 return d.wc.Sync()
64 }
65
66 func (d *mergeDecorator) Base() Decorator {
67 return d.Decorator
68 }
69
70 func (d *mergeDecorator) Decor(s Statistics) string {
71 msg := d.Decorator.Decor(s)
72 pureWidth := runewidth.StringWidth(msg)
73 stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
74 cellCount := stripWidth
75 if (d.wc.C & DextraSpace) != 0 {
76 cellCount++
77 }
78
79 total := runewidth.StringWidth(d.placeHolders[0].FormatMsg(""))
80 pw := (cellCount - total) / len(d.placeHolders)
81 rem := (cellCount - total) % len(d.placeHolders)
82
83 var diff int
84 for i := 1; i < len(d.placeHolders); i++ {
85 ph := d.placeHolders[i]
86 width := pw - diff
87 if (ph.WC.C & DextraSpace) != 0 {
88 width--
89 if width < 0 {
90 width = 0
91 }
92 }
93 max := runewidth.StringWidth(ph.FormatMsg(strings.Repeat(" ", width)))
94 total += max
95 diff = max - pw
96 }
97
98 d.wc.wsync <- pw + rem
99 max := <-d.wc.wsync
100 return d.wc.fill(msg, max+total+(pureWidth-stripWidth))
101 }
102
103 type placeHolderDecorator struct {
104 WC
105 }
106
107 func (d *placeHolderDecorator) Decor(Statistics) string {
108 return ""
109 }
0 package decor
1
2 var (
3 _ Decorator = metaWrapper{}
4 _ Wrapper = metaWrapper{}
5 )
6
7 // Meta wrap decorator.
8 // Provided fn is supposed to wrap output of given decorator
9 // with meta information like ANSI escape codes for example.
10 // Primary usage intention is to set SGR display attributes.
11 //
12 // `decorator` Decorator to wrap
13 // `fn` func to apply meta information
14 func Meta(decorator Decorator, fn func(string) string) Decorator {
15 if decorator == nil {
16 return nil
17 }
18 return metaWrapper{decorator, fn}
19 }
20
21 type metaWrapper struct {
22 Decorator
23 fn func(string) string
24 }
25
26 func (d metaWrapper) Decor(s Statistics) (string, int) {
27 str, width := d.Decorator.Decor(s)
28 return d.fn(str), width
29 }
30
31 func (d metaWrapper) Unwrap() Decorator {
32 return d.Decorator
33 }
44 "sync"
55
66 "github.com/VividCortex/ewma"
7 )
8
9 var (
10 _ ewma.MovingAverage = (*threadSafeMovingAverage)(nil)
11 _ ewma.MovingAverage = (*medianWindow)(nil)
12 _ sort.Interface = (*medianWindow)(nil)
713 )
814
915 type threadSafeMovingAverage struct {
55 // `str` string to display
66 //
77 // `wcc` optional WC config
8 //
98 func Name(str string, wcc ...WC) Decorator {
109 return Any(func(Statistics) string { return str }, wcc...)
1110 }
00 package decor
11
2 // OnAbort returns decorator, which wraps provided decorator with sole
3 // purpose to display provided message on abort event. It has no effect
4 // if bar.Abort(drop bool) is called with true argument.
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.
512 //
613 // `decorator` Decorator to wrap
7 //
8 // `message` message to display on abort event
9 //
14 // `message` message to display
1015 func OnAbort(decorator Decorator, message string) Decorator {
1116 if decorator == nil {
1217 return nil
1318 }
14 d := &onAbortWrapper{
15 Decorator: decorator,
16 msg: message,
17 }
18 if md, ok := decorator.(*mergeDecorator); ok {
19 d.Decorator, md.Decorator = md.Decorator, d
20 return md
21 }
22 return d
19 return onAbortWrapper{decorator, message}
2320 }
2421
2522 type onAbortWrapper struct {
2724 msg string
2825 }
2926
30 func (d *onAbortWrapper) Decor(s Statistics) string {
27 func (d onAbortWrapper) Decor(s Statistics) (string, int) {
3128 if s.Aborted {
32 wc := d.GetConf()
33 return wc.FormatMsg(d.msg)
29 return d.Format(d.msg)
3430 }
3531 return d.Decorator.Decor(s)
3632 }
3733
38 func (d *onAbortWrapper) Base() Decorator {
34 func (d onAbortWrapper) Unwrap() Decorator {
3935 return d.Decorator
4036 }
37
38 // OnAbortMeta wrap decorator.
39 // Provided fn is supposed to wrap output of given decorator
40 // with meta information like ANSI escape codes for example.
41 // Primary usage intention is to set SGR display attributes.
42 //
43 // `decorator` Decorator to wrap
44 // `fn` func to apply meta information
45 func OnAbortMeta(decorator Decorator, fn func(string) string) Decorator {
46 if decorator == nil {
47 return nil
48 }
49 return onAbortMetaWrapper{decorator, fn}
50 }
51
52 type onAbortMetaWrapper struct {
53 Decorator
54 fn func(string) string
55 }
56
57 func (d onAbortMetaWrapper) Decor(s Statistics) (string, int) {
58 if s.Completed {
59 str, width := d.Decorator.Decor(s)
60 return d.fn(str), width
61 }
62 return d.Decorator.Decor(s)
63 }
64
65 func (d onAbortMetaWrapper) Unwrap() Decorator {
66 return d.Decorator
67 }
0 package decor
1
2 // OnCompleteOrOnAbort wrap decorator.
3 // Displays provided message on complete or on abort event.
4 //
5 // `decorator` Decorator to wrap
6 // `message` message to display
7 func OnCompleteOrOnAbort(decorator Decorator, message string) Decorator {
8 return OnComplete(OnAbort(decorator, message), message)
9 }
10
11 // OnCompleteMetaOrOnAbortMeta wrap decorator.
12 // Provided fn is supposed to wrap output of given decorator
13 // with meta information like ANSI escape codes for example.
14 // Primary usage intention is to set SGR display attributes.
15 //
16 // `decorator` Decorator to wrap
17 // `fn` func to apply meta information
18 func OnCompleteMetaOrOnAbortMeta(decorator Decorator, fn func(string) string) Decorator {
19 return OnCompleteMeta(OnAbortMeta(decorator, fn), fn)
20 }
00 package decor
11
2 // OnComplete returns decorator, which wraps provided decorator with
3 // sole purpose to display provided message on complete event.
2 var (
3 _ Decorator = onCompleteWrapper{}
4 _ Wrapper = onCompleteWrapper{}
5 _ Decorator = onCompleteMetaWrapper{}
6 _ Wrapper = onCompleteMetaWrapper{}
7 )
8
9 // OnComplete wrap decorator.
10 // Displays provided message on complete event.
411 //
512 // `decorator` Decorator to wrap
6 //
7 // `message` message to display on complete event
8 //
13 // `message` message to display
914 func OnComplete(decorator Decorator, message string) Decorator {
1015 if decorator == nil {
1116 return nil
1217 }
13 d := &onCompleteWrapper{
14 Decorator: decorator,
15 msg: message,
16 }
17 if md, ok := decorator.(*mergeDecorator); ok {
18 d.Decorator, md.Decorator = md.Decorator, d
19 return md
20 }
21 return d
18 return onCompleteWrapper{decorator, message}
2219 }
2320
2421 type onCompleteWrapper struct {
2623 msg string
2724 }
2825
29 func (d *onCompleteWrapper) Decor(s Statistics) string {
26 func (d onCompleteWrapper) Decor(s Statistics) (string, int) {
3027 if s.Completed {
31 wc := d.GetConf()
32 return wc.FormatMsg(d.msg)
28 return d.Format(d.msg)
3329 }
3430 return d.Decorator.Decor(s)
3531 }
3632
37 func (d *onCompleteWrapper) Base() Decorator {
33 func (d onCompleteWrapper) Unwrap() Decorator {
3834 return d.Decorator
3935 }
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 }
00 package decor
11
2 // OnPredicate returns decorator if predicate evaluates to true.
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.
312 //
413 // `decorator` Decorator
514 //
615 // `predicate` func() bool
7 //
816 func OnPredicate(decorator Decorator, predicate func() bool) Decorator {
9 if predicate() {
10 return decorator
11 }
12 return nil
17 return Predicative(predicate, decorator, nil)
1318 }
1419
15 // OnCondition returns decorator if condition is true.
16 //
17 // `decorator` Decorator
20 // Conditional returns decorator `a` if condition is true, otherwise
21 // decorator `b`.
1822 //
1923 // `cond` bool
2024 //
21 func OnCondition(decorator Decorator, cond bool) Decorator {
25 // `a` Decorator
26 //
27 // `b` Decorator
28 func Conditional(cond bool, a, b Decorator) Decorator {
2229 if cond {
23 return decorator
30 return a
31 } else {
32 return b
2433 }
25 return nil
2634 }
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 }
+0
-12
decor/optimistic_string_writer.go less more
0 package decor
1
2 import "io"
3
4 func optimisticStringWriter(w io.Writer) func(string) {
5 return func(s string) {
6 _, err := io.WriteString(w, s)
7 if err != nil {
8 panic(err)
9 }
10 }
11 }
33 "fmt"
44 "strconv"
55
6 "github.com/vbauerster/mpb/v7/internal"
6 "github.com/vbauerster/mpb/v8/internal"
77 )
8
9 var _ fmt.Formatter = percentageType(0)
810
911 type percentageType float64
1012
1113 func (s percentageType) Format(st fmt.State, verb rune) {
12 var prec int
14 prec := -1
1315 switch verb {
14 case 'd':
15 case 's':
16 prec = -1
17 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':
1820 if p, ok := st.Precision(); ok {
1921 prec = p
20 } else {
21 prec = 6
2222 }
23 default:
24 verb, prec = 'f', 0
2325 }
2426
25 osw := optimisticStringWriter(st)
26 osw(strconv.FormatFloat(float64(s), 'f', prec, 64))
27 b := strconv.AppendFloat(make([]byte, 0, 16), float64(s), byte(verb), prec, 64)
2728 if st.Flag(' ') {
28 osw(" ")
29 b = append(b, ' ', '%')
30 } else {
31 b = append(b, '%')
2932 }
30 osw("%")
33 _, err := st.Write(b)
34 if err != nil {
35 panic(err)
36 }
3137 }
3238
3339 // Percentage returns percentage decorator. It's a wrapper of NewPercentage.
3743
3844 // NewPercentage percentage decorator with custom format string.
3945 //
46 // `format` printf compatible verb
47 //
48 // `wcc` optional WC config
49 //
4050 // format examples:
4151 //
52 // format="%d" output: "1%"
53 // format="% d" output: "1 %"
4254 // format="%.1f" output: "1.0%"
4355 // format="% .1f" output: "1.0 %"
44 // format="%d" output: "1%"
45 // format="% d" output: "1 %"
46 //
56 // format="%f" output: "1.000000%"
57 // format="% f" output: "1.000000 %"
4758 func NewPercentage(format string, wcc ...WC) Decorator {
4859 if format == "" {
4960 format = "% d"
0 package decor
1
2 import (
3 "fmt"
4 "testing"
5 )
6
7 func TestPercentageType(t *testing.T) {
8 cases := map[string]struct {
9 value float64
10 verb string
11 expected string
12 }{
13 "10 %d": {10, "%d", "10%"},
14 "10 %s": {10, "%s", "10%"},
15 "10 %f": {10, "%f", "10.000000%"},
16 "10 %.6f": {10, "%.6f", "10.000000%"},
17 "10 %.0f": {10, "%.0f", "10%"},
18 "10 %.1f": {10, "%.1f", "10.0%"},
19 "10 %.2f": {10, "%.2f", "10.00%"},
20 "10 %.3f": {10, "%.3f", "10.000%"},
21
22 "10 % d": {10, "% d", "10 %"},
23 "10 % s": {10, "% s", "10 %"},
24 "10 % f": {10, "% f", "10.000000 %"},
25 "10 % .6f": {10, "% .6f", "10.000000 %"},
26 "10 % .0f": {10, "% .0f", "10 %"},
27 "10 % .1f": {10, "% .1f", "10.0 %"},
28 "10 % .2f": {10, "% .2f", "10.00 %"},
29 "10 % .3f": {10, "% .3f", "10.000 %"},
30
31 "10.5 %d": {10.5, "%d", "10%"},
32 "10.5 %s": {10.5, "%s", "10%"},
33 "10.5 %f": {10.5, "%f", "10.500000%"},
34 "10.5 %.6f": {10.5, "%.6f", "10.500000%"},
35 "10.5 %.0f": {10.5, "%.0f", "10%"},
36 "10.5 %.1f": {10.5, "%.1f", "10.5%"},
37 "10.5 %.2f": {10.5, "%.2f", "10.50%"},
38 "10.5 %.3f": {10.5, "%.3f", "10.500%"},
39
40 "10.5 % d": {10.5, "% d", "10 %"},
41 "10.5 % s": {10.5, "% s", "10 %"},
42 "10.5 % f": {10.5, "% f", "10.500000 %"},
43 "10.5 % .6f": {10.5, "% .6f", "10.500000 %"},
44 "10.5 % .0f": {10.5, "% .0f", "10 %"},
45 "10.5 % .1f": {10.5, "% .1f", "10.5 %"},
46 "10.5 % .2f": {10.5, "% .2f", "10.50 %"},
47 "10.5 % .3f": {10.5, "% .3f", "10.500 %"},
48 }
49 for name, tc := range cases {
50 t.Run(name, func(t *testing.T) {
51 got := fmt.Sprintf(tc.verb, percentageType(tc.value))
52 if got != tc.expected {
53 t.Fatalf("expected: %q, got: %q\n", tc.expected, got)
54 }
55 })
56 }
57 }
66
77 //go:generate stringer -type=SizeB1024 -trimprefix=_i
88 //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 )
916
1017 const (
1118 _ib SizeB1024 = iota + 1
2128 type SizeB1024 int64
2229
2330 func (self SizeB1024) Format(st fmt.State, verb rune) {
24 var prec int
31 prec := -1
2532 switch verb {
26 case 'd':
27 case 's':
28 prec = -1
29 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':
3037 if p, ok := st.Precision(); ok {
3138 prec = p
32 } else {
33 prec = 6
3439 }
40 default:
41 verb, prec = 'f', 0
3542 }
3643
3744 var unit SizeB1024
4855 unit = _iTiB
4956 }
5057
51 osw := optimisticStringWriter(st)
52 osw(strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
58 b := strconv.AppendFloat(make([]byte, 0, 24), float64(self)/float64(unit), byte(verb), prec, 64)
5359 if st.Flag(' ') {
54 osw(" ")
60 b = append(b, ' ')
5561 }
56 osw(unit.String())
62 b = append(b, []byte(unit.String())...)
63 _, err := st.Write(b)
64 if err != nil {
65 panic(err)
66 }
5767 }
5868
5969 const (
7080 type SizeB1000 int64
7181
7282 func (self SizeB1000) Format(st fmt.State, verb rune) {
73 var prec int
83 prec := -1
7484 switch verb {
75 case 'd':
76 case 's':
77 prec = -1
78 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':
7989 if p, ok := st.Precision(); ok {
8090 prec = p
81 } else {
82 prec = 6
8391 }
92 default:
93 verb, prec = 'f', 0
8494 }
8595
8696 var unit SizeB1000
97107 unit = _TB
98108 }
99109
100 osw := optimisticStringWriter(st)
101 osw(strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
110 b := strconv.AppendFloat(make([]byte, 0, 24), float64(self)/float64(unit), byte(verb), prec, 64)
102111 if st.Flag(' ') {
103 osw(" ")
112 b = append(b, ' ')
104113 }
105 osw(unit.String())
114 b = append(b, []byte(unit.String())...)
115 _, err := st.Write(b)
116 if err != nil {
117 panic(err)
118 }
106119 }
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) {
11
22 import (
33 "fmt"
4 "io"
45 "math"
56 "time"
67
78 "github.com/VividCortex/ewma"
89 )
910
11 var (
12 _ Decorator = (*movingAverageSpeed)(nil)
13 _ EwmaDecorator = (*movingAverageSpeed)(nil)
14 _ Decorator = (*averageSpeed)(nil)
15 _ AverageDecorator = (*averageSpeed)(nil)
16 )
17
1018 // FmtAsSpeed adds "/s" to the end of the input formatter. To be
1119 // used with SizeB1000 or SizeB1024 types, for example:
1220 //
1321 // fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048)))
14 //
1522 func FmtAsSpeed(input fmt.Formatter) fmt.Formatter {
16 return &speedFormatter{input}
23 return speedFormatter{input}
1724 }
1825
1926 type speedFormatter struct {
2027 fmt.Formatter
2128 }
2229
23 func (self *speedFormatter) Format(st fmt.State, verb rune) {
30 func (self speedFormatter) Format(st fmt.State, verb rune) {
2431 self.Formatter.Format(st, verb)
25 optimisticStringWriter(st)("/s")
32 _, err := io.WriteString(st, "/s")
33 if err != nil {
34 panic(err)
35 }
2636 }
2737
2838 // EwmaSpeed exponential-weighted-moving-average based speed decorator.
29 // For this decorator to work correctly you have to measure each
30 // iteration's duration and pass it to the
31 // *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
32 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 {
3342 var average ewma.MovingAverage
3443 if age == 0 {
3544 average = ewma.NewMovingAverage()
4251 // MovingAverageSpeed decorator relies on MovingAverage implementation
4352 // to calculate its average.
4453 //
45 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
54 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
4655 //
4756 // `format` printf compatible verb for value, like "%f" or "%d"
4857 //
5261 //
5362 // format examples:
5463 //
55 // unit=UnitKiB, format="%.1f" output: "1.0MiB/s"
56 // unit=UnitKiB, format="% .1f" output: "1.0 MiB/s"
57 // unit=UnitKB, format="%.1f" output: "1.0MB/s"
58 // unit=UnitKB, format="% .1f" output: "1.0 MB/s"
59 //
60 func MovingAverageSpeed(unit int, format string, average ewma.MovingAverage, wcc ...WC) Decorator {
61 if format == "" {
62 format = "%.0f"
63 }
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 {
6469 d := &movingAverageSpeed{
6570 WC: initWC(wcc...),
6671 average: average,
7681 msg string
7782 }
7883
79 func (d *movingAverageSpeed) Decor(s Statistics) string {
84 func (d *movingAverageSpeed) Decor(s Statistics) (string, int) {
8085 if !s.Completed {
8186 var speed float64
8287 if v := d.average.Value(); v > 0 {
8489 }
8590 d.msg = d.producer(speed * 1e9)
8691 }
87 return d.FormatMsg(d.msg)
92 return d.Format(d.msg)
8893 }
8994
9095 func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) {
97102
98103 // AverageSpeed decorator with dynamic unit measure adjustment. It's
99104 // a wrapper of NewAverageSpeed.
100 func AverageSpeed(unit int, format string, wcc ...WC) Decorator {
105 func AverageSpeed(unit interface{}, format string, wcc ...WC) Decorator {
101106 return NewAverageSpeed(unit, format, time.Now(), wcc...)
102107 }
103108
104109 // NewAverageSpeed decorator with dynamic unit measure adjustment and
105110 // user provided start time.
106111 //
107 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
112 // `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
108113 //
109114 // `format` printf compatible verb for value, like "%f" or "%d"
110115 //
114119 //
115120 // format examples:
116121 //
117 // unit=UnitKiB, format="%.1f" output: "1.0MiB/s"
118 // unit=UnitKiB, format="% .1f" output: "1.0 MiB/s"
119 // unit=UnitKB, format="%.1f" output: "1.0MB/s"
120 // unit=UnitKB, format="% .1f" output: "1.0 MB/s"
121 //
122 func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator {
123 if format == "" {
124 format = "%.0f"
125 }
122 // unit=SizeB1024(0), format="%.1f" output: "1.0MiB/s"
123 // unit=SizeB1024(0), format="% .1f" output: "1.0 MiB/s"
124 // unit=SizeB1000(0), format="%.1f" output: "1.0MB/s"
125 // unit=SizeB1000(0), format="% .1f" output: "1.0 MB/s"
126 func NewAverageSpeed(unit interface{}, format string, startTime time.Time, wcc ...WC) Decorator {
126127 d := &averageSpeed{
127128 WC: initWC(wcc...),
128129 startTime: startTime,
138139 msg string
139140 }
140141
141 func (d *averageSpeed) Decor(s Statistics) string {
142 func (d *averageSpeed) Decor(s Statistics) (string, int) {
142143 if !s.Completed {
143144 speed := float64(s.Current) / float64(time.Since(d.startTime))
144145 d.msg = d.producer(speed * 1e9)
145146 }
146
147 return d.FormatMsg(d.msg)
147 return d.Format(d.msg)
148148 }
149149
150150 func (d *averageSpeed) AverageAdjust(startTime time.Time) {
151151 d.startTime = startTime
152152 }
153153
154 func chooseSpeedProducer(unit int, format string) func(float64) string {
155 switch unit {
156 case UnitKiB:
154 func chooseSpeedProducer(unit interface{}, format string) func(float64) string {
155 switch unit.(type) {
156 case SizeB1024:
157 if format == "" {
158 format = "% d"
159 }
157160 return func(speed float64) string {
158161 return fmt.Sprintf(format, FmtAsSpeed(SizeB1024(math.Round(speed))))
159162 }
160 case UnitKB:
163 case SizeB1000:
164 if format == "" {
165 format = "% d"
166 }
161167 return func(speed float64) string {
162168 return fmt.Sprintf(format, FmtAsSpeed(SizeB1000(math.Round(speed))))
163169 }
164170 default:
171 if format == "" {
172 format = "%f"
173 }
165174 return func(speed float64) string {
166175 return fmt.Sprintf(format, speed)
167176 }
44 "time"
55 )
66
7 func TestSpeedKiBDecor(t *testing.T) {
7 func TestAverageSpeedSizeB1024(t *testing.T) {
88 cases := []struct {
99 name string
1010 fmt string
11 unit int
11 unit interface{}
1212 current int64
1313 elapsed time.Duration
1414 expected string
1515 }{
1616 {
1717 name: "empty fmt",
18 unit: UnitKiB,
18 unit: SizeB1024(0),
1919 fmt: "",
2020 current: 0,
2121 elapsed: time.Second,
22 expected: "0 b/s",
23 },
24 {
25 name: "SizeB1024(0):%d:0b",
26 unit: SizeB1024(0),
27 fmt: "%d",
28 current: 0,
29 elapsed: time.Second,
2230 expected: "0b/s",
2331 },
2432 {
25 name: "UnitKiB:%d:0b",
26 unit: UnitKiB,
27 fmt: "%d",
28 current: 0,
29 elapsed: time.Second,
30 expected: "0b/s",
31 },
32 {
33 name: "UnitKiB:% .2f:0b",
34 unit: UnitKiB,
33 name: "SizeB1024(0):%f:0b",
34 unit: SizeB1024(0),
35 fmt: "%f",
36 current: 0,
37 elapsed: time.Second,
38 expected: "0.000000b/s",
39 },
40 {
41 name: "SizeB1024(0):% .2f:0b",
42 unit: SizeB1024(0),
3543 fmt: "% .2f",
3644 current: 0,
3745 elapsed: time.Second,
3846 expected: "0.00 b/s",
3947 },
4048 {
41 name: "UnitKiB:%d:1b",
42 unit: UnitKiB,
49 name: "SizeB1024(0):%d:1b",
50 unit: SizeB1024(0),
4351 fmt: "%d",
4452 current: 1,
4553 elapsed: time.Second,
4654 expected: "1b/s",
4755 },
4856 {
49 name: "UnitKiB:% .2f:1b",
50 unit: UnitKiB,
57 name: "SizeB1024(0):% .2f:1b",
58 unit: SizeB1024(0),
5159 fmt: "% .2f",
5260 current: 1,
5361 elapsed: time.Second,
5462 expected: "1.00 b/s",
5563 },
5664 {
57 name: "UnitKiB:%d:KiB",
58 unit: UnitKiB,
65 name: "SizeB1024(0):%d:KiB",
66 unit: SizeB1024(0),
5967 fmt: "%d",
6068 current: 2 * int64(_iKiB),
6169 elapsed: 1 * time.Second,
6270 expected: "2KiB/s",
6371 },
6472 {
65 name: "UnitKiB:% .f:KiB",
66 unit: UnitKiB,
73 name: "SizeB1024(0):% .f:KiB",
74 unit: SizeB1024(0),
6775 fmt: "% .2f",
6876 current: 2 * int64(_iKiB),
6977 elapsed: 1 * time.Second,
7078 expected: "2.00 KiB/s",
7179 },
7280 {
73 name: "UnitKiB:%d:MiB",
74 unit: UnitKiB,
81 name: "SizeB1024(0):%d:MiB",
82 unit: SizeB1024(0),
7583 fmt: "%d",
7684 current: 2 * int64(_iMiB),
7785 elapsed: 1 * time.Second,
7886 expected: "2MiB/s",
7987 },
8088 {
81 name: "UnitKiB:% .2f:MiB",
82 unit: UnitKiB,
89 name: "SizeB1024(0):% .2f:MiB",
90 unit: SizeB1024(0),
8391 fmt: "% .2f",
8492 current: 2 * int64(_iMiB),
8593 elapsed: 1 * time.Second,
8694 expected: "2.00 MiB/s",
8795 },
8896 {
89 name: "UnitKiB:%d:GiB",
90 unit: UnitKiB,
97 name: "SizeB1024(0):%d:GiB",
98 unit: SizeB1024(0),
9199 fmt: "%d",
92100 current: 2 * int64(_iGiB),
93101 elapsed: 1 * time.Second,
94102 expected: "2GiB/s",
95103 },
96104 {
97 name: "UnitKiB:% .2f:GiB",
98 unit: UnitKiB,
105 name: "SizeB1024(0):% .2f:GiB",
106 unit: SizeB1024(0),
99107 fmt: "% .2f",
100108 current: 2 * int64(_iGiB),
101109 elapsed: 1 * time.Second,
102110 expected: "2.00 GiB/s",
103111 },
104112 {
105 name: "UnitKiB:%d:TiB",
106 unit: UnitKiB,
113 name: "SizeB1024(0):%d:TiB",
114 unit: SizeB1024(0),
107115 fmt: "%d",
108116 current: 2 * int64(_iTiB),
109117 elapsed: 1 * time.Second,
110118 expected: "2TiB/s",
111119 },
112120 {
113 name: "UnitKiB:% .2f:TiB",
114 unit: UnitKiB,
121 name: "SizeB1024(0):% .2f:TiB",
122 unit: SizeB1024(0),
115123 fmt: "% .2f",
116124 current: 2 * int64(_iTiB),
117125 elapsed: 1 * time.Second,
124132 stat := Statistics{
125133 Current: tc.current,
126134 }
127 res := decor.Decor(stat)
135 res, _ := decor.Decor(stat)
128136 if res != tc.expected {
129137 t.Fatalf("expected: %q, got: %q\n", tc.expected, res)
130138 }
132140 }
133141 }
134142
135 func TestSpeedKBDecor(t *testing.T) {
143 func TestAverageSpeedSizeB1000(t *testing.T) {
136144 cases := []struct {
137145 name string
138146 fmt string
139 unit int
147 unit interface{}
140148 current int64
141149 elapsed time.Duration
142150 expected string
143151 }{
144152 {
145153 name: "empty fmt",
146 unit: UnitKB,
154 unit: SizeB1000(0),
147155 fmt: "",
148156 current: 0,
149157 elapsed: time.Second,
158 expected: "0 b/s",
159 },
160 {
161 name: "SizeB1000(0):%d:0b",
162 unit: SizeB1000(0),
163 fmt: "%d",
164 current: 0,
165 elapsed: time.Second,
150166 expected: "0b/s",
151167 },
152168 {
153 name: "UnitKB:%d:0b",
154 unit: UnitKB,
155 fmt: "%d",
156 current: 0,
157 elapsed: time.Second,
158 expected: "0b/s",
159 },
160 {
161 name: "UnitKB:% .2f:0b",
162 unit: UnitKB,
169 name: "SizeB1000(0):%f:0b",
170 unit: SizeB1000(0),
171 fmt: "%f",
172 current: 0,
173 elapsed: time.Second,
174 expected: "0.000000b/s",
175 },
176 {
177 name: "SizeB1000(0):% .2f:0b",
178 unit: SizeB1000(0),
163179 fmt: "% .2f",
164180 current: 0,
165181 elapsed: time.Second,
166182 expected: "0.00 b/s",
167183 },
168184 {
169 name: "UnitKB:%d:1b",
170 unit: UnitKB,
185 name: "SizeB1000(0):%d:1b",
186 unit: SizeB1000(0),
171187 fmt: "%d",
172188 current: 1,
173189 elapsed: time.Second,
174190 expected: "1b/s",
175191 },
176192 {
177 name: "UnitKB:% .2f:1b",
178 unit: UnitKB,
193 name: "SizeB1000(0):% .2f:1b",
194 unit: SizeB1000(0),
179195 fmt: "% .2f",
180196 current: 1,
181197 elapsed: time.Second,
182198 expected: "1.00 b/s",
183199 },
184200 {
185 name: "UnitKB:%d:KB",
186 unit: UnitKB,
201 name: "SizeB1000(0):%d:KB",
202 unit: SizeB1000(0),
187203 fmt: "%d",
188204 current: 2 * int64(_KB),
189205 elapsed: 1 * time.Second,
190206 expected: "2KB/s",
191207 },
192208 {
193 name: "UnitKB:% .f:KB",
194 unit: UnitKB,
209 name: "SizeB1000(0):% .f:KB",
210 unit: SizeB1000(0),
195211 fmt: "% .2f",
196212 current: 2 * int64(_KB),
197213 elapsed: 1 * time.Second,
198214 expected: "2.00 KB/s",
199215 },
200216 {
201 name: "UnitKB:%d:MB",
202 unit: UnitKB,
217 name: "SizeB1000(0):%d:MB",
218 unit: SizeB1000(0),
203219 fmt: "%d",
204220 current: 2 * int64(_MB),
205221 elapsed: 1 * time.Second,
206222 expected: "2MB/s",
207223 },
208224 {
209 name: "UnitKB:% .2f:MB",
210 unit: UnitKB,
225 name: "SizeB1000(0):% .2f:MB",
226 unit: SizeB1000(0),
211227 fmt: "% .2f",
212228 current: 2 * int64(_MB),
213229 elapsed: 1 * time.Second,
214230 expected: "2.00 MB/s",
215231 },
216232 {
217 name: "UnitKB:%d:GB",
218 unit: UnitKB,
233 name: "SizeB1000(0):%d:GB",
234 unit: SizeB1000(0),
219235 fmt: "%d",
220236 current: 2 * int64(_GB),
221237 elapsed: 1 * time.Second,
222238 expected: "2GB/s",
223239 },
224240 {
225 name: "UnitKB:% .2f:GB",
226 unit: UnitKB,
241 name: "SizeB1000(0):% .2f:GB",
242 unit: SizeB1000(0),
227243 fmt: "% .2f",
228244 current: 2 * int64(_GB),
229245 elapsed: 1 * time.Second,
230246 expected: "2.00 GB/s",
231247 },
232248 {
233 name: "UnitKB:%d:TB",
234 unit: UnitKB,
249 name: "SizeB1000(0):%d:TB",
250 unit: SizeB1000(0),
235251 fmt: "%d",
236252 current: 2 * int64(_TB),
237253 elapsed: 1 * time.Second,
238254 expected: "2TB/s",
239255 },
240256 {
241 name: "UnitKB:% .2f:TB",
242 unit: UnitKB,
257 name: "SizeB1000(0):% .2f:TB",
258 unit: SizeB1000(0),
243259 fmt: "% .2f",
244260 current: 2 * int64(_TB),
245261 elapsed: 1 * time.Second,
252268 stat := Statistics{
253269 Current: tc.current,
254270 }
255 res := decor.Decor(stat)
271 res, _ := decor.Decor(stat)
256272 if res != tc.expected {
257273 t.Fatalf("expected: %q, got: %q\n", tc.expected, res)
258274 }
00 package decor
11
2 var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
2 var defaultSpinnerStyle = [...]string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
33
44 // Spinner returns spinner decorator.
55 //
88 // `wcc` optional WC config
99 func Spinner(frames []string, wcc ...WC) Decorator {
1010 if len(frames) == 0 {
11 frames = defaultSpinnerStyle
11 frames = defaultSpinnerStyle[:]
1212 }
1313 var count uint
1414 f := func(s Statistics) string {
00 package mpb_test
11
22 import (
3 "sync"
43 "testing"
54
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
5 "github.com/vbauerster/mpb/v8"
6 "github.com/vbauerster/mpb/v8/decor"
87 )
98
109 func TestNameDecorator(t *testing.T) {
3130 }
3231
3332 for _, test := range tests {
34 got := test.decorator.Decor(decor.Statistics{})
33 got, _ := test.decorator.Decor(decor.Statistics{})
3534 if got != test.want {
3635 t.Errorf("Want: %q, Got: %q\n", test.want, got)
3736 }
182181 }
183182
184183 for _, columnCase := range testCases {
185 mpb.SyncWidth(toSyncMatrix(columnCase))
186 numBars := len(columnCase)
187 gott := make([]chan string, numBars)
188 wg := new(sync.WaitGroup)
189 wg.Add(numBars)
190 for i, step := range columnCase {
184 mpb.SyncWidth(toSyncMatrix(columnCase), nil)
185 var results []chan string
186 for _, step := range columnCase {
191187 step := step
192 ch := make(chan string, 1)
188 ch := make(chan string)
193189 go func() {
194 defer wg.Done()
195 ch <- step.decorator.Decor(step.stat)
190 str, _ := step.decorator.Decor(step.stat)
191 ch <- str
196192 }()
197 gott[i] = ch
198 }
199 wg.Wait()
200
201 for i, ch := range gott {
202 got := <-ch
193 results = append(results, ch)
194 }
195
196 for i, ch := range results {
197 res := <-ch
203198 want := columnCase[i].want
204 if got != want {
205 t.Errorf("Want: %q, Got: %q\n", want, got)
199 if res != want {
200 t.Errorf("Want: %q, Got: %q\n", want, res)
206201 }
207202 }
208
209203 }
210204 }
211205
772772 s.current = tc.current
773773 s.trimSpace = tc.trim
774774 s.refill = tc.refill
775 s.completed = tc.total > 0 && tc.current >= tc.total
775776 tmpBuf.Reset()
776 _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
777 r, err := s.draw(newStatistics(tw, s))
778 if err != nil {
779 t.FailNow()
780 }
781 _, err = tmpBuf.ReadFrom(r)
777782 if err != nil {
778783 t.FailNow()
779784 }
804809 }{
805810 3: {
806811 {
807 style: BarStyle().TipOnComplete(">"),
812 style: BarStyle().TipOnComplete(),
808813 name: "t,c{60,60}",
809814 total: 60,
810815 current: 60,
811816 want: " ",
812817 },
813818 {
814 style: BarStyle().TipOnComplete(">"),
819 style: BarStyle().TipOnComplete(),
815820 name: "t,c{60,60}trim",
816821 total: 60,
817822 current: 60,
821826 },
822827 4: {
823828 {
824 style: BarStyle().TipOnComplete(">"),
829 style: BarStyle().TipOnComplete(),
825830 name: "t,c{60,59}",
826831 total: 60,
827832 current: 59,
828833 want: " [] ",
829834 },
830835 {
831 style: BarStyle().TipOnComplete(">"),
836 style: BarStyle().TipOnComplete(),
832837 name: "t,c{60,59}trim",
833838 total: 60,
834839 current: 59,
836841 want: "[=>]",
837842 },
838843 {
839 style: BarStyle().TipOnComplete(">"),
844 style: BarStyle().TipOnComplete(),
840845 name: "t,c{60,60}",
841846 total: 60,
842847 current: 60,
843848 want: " [] ",
844849 },
845850 {
846 style: BarStyle().TipOnComplete(">"),
851 style: BarStyle().TipOnComplete(),
847852 name: "t,c{60,60}trim",
848853 total: 60,
849854 current: 60,
853858 },
854859 5: {
855860 {
856 style: BarStyle().TipOnComplete(">"),
861 style: BarStyle().TipOnComplete(),
857862 name: "t,c{60,59}",
858863 total: 60,
859864 current: 59,
860865 want: " [>] ",
861866 },
862867 {
863 style: BarStyle().TipOnComplete(">"),
868 style: BarStyle().TipOnComplete(),
864869 name: "t,c{60,59}trim",
865870 total: 60,
866871 current: 59,
868873 want: "[==>]",
869874 },
870875 {
871 style: BarStyle().TipOnComplete(">"),
876 style: BarStyle().TipOnComplete(),
872877 name: "t,c{60,60}",
873878 total: 60,
874879 current: 60,
875880 want: " [>] ",
876881 },
877882 {
878 style: BarStyle().TipOnComplete(">"),
883 style: BarStyle().TipOnComplete(),
879884 name: "t,c{60,60}trim",
880885 total: 60,
881886 current: 60,
885890 },
886891 6: {
887892 {
888 style: BarStyle().TipOnComplete(">"),
893 style: BarStyle().TipOnComplete(),
889894 name: "t,c{60,59}",
890895 total: 60,
891896 current: 59,
892897 want: " [=>] ",
893898 },
894899 {
895 style: BarStyle().TipOnComplete(">"),
900 style: BarStyle().TipOnComplete(),
896901 name: "t,c{60,59}trim",
897902 total: 60,
898903 current: 59,
900905 want: "[===>]",
901906 },
902907 {
903 style: BarStyle().TipOnComplete(">"),
908 style: BarStyle().TipOnComplete(),
904909 name: "t,c{60,60}",
905910 total: 60,
906911 current: 60,
907912 want: " [=>] ",
908913 },
909914 {
910 style: BarStyle().TipOnComplete(">"),
915 style: BarStyle().TipOnComplete(),
911916 name: "t,c{60,60}trim",
912917 total: 60,
913918 current: 60,
917922 },
918923 7: {
919924 {
920 style: BarStyle().TipOnComplete(">"),
925 style: BarStyle().TipOnComplete(),
921926 name: "t,c{60,59}",
922927 total: 60,
923928 current: 59,
924929 want: " [==>] ",
925930 },
926931 {
927 style: BarStyle().TipOnComplete(">"),
932 style: BarStyle().TipOnComplete(),
928933 name: "t,c{60,59}trim",
929934 total: 60,
930935 current: 59,
932937 want: "[====>]",
933938 },
934939 {
935 style: BarStyle().TipOnComplete(">"),
940 style: BarStyle().TipOnComplete(),
936941 name: "t,c{60,60}",
937942 total: 60,
938943 current: 60,
939944 want: " [==>] ",
940945 },
941946 {
942 style: BarStyle().TipOnComplete(">"),
947 style: BarStyle().TipOnComplete(),
943948 name: "t,c{60,60}trim",
944949 total: 60,
945950 current: 60,
949954 },
950955 8: {
951956 {
952 style: BarStyle().TipOnComplete(">"),
957 style: BarStyle().TipOnComplete(),
953958 name: "t,c{60,59}",
954959 total: 60,
955960 current: 59,
956961 want: " [===>] ",
957962 },
958963 {
959 style: BarStyle().TipOnComplete(">"),
964 style: BarStyle().TipOnComplete(),
960965 name: "t,c{60,59}trim",
961966 total: 60,
962967 current: 59,
964969 want: "[=====>]",
965970 },
966971 {
967 style: BarStyle().TipOnComplete(">"),
972 style: BarStyle().TipOnComplete(),
968973 name: "t,c{60,60}",
969974 total: 60,
970975 current: 60,
971976 want: " [===>] ",
972977 },
973978 {
974 style: BarStyle().TipOnComplete(">"),
979 style: BarStyle().TipOnComplete(),
975980 name: "t,c{60,60}trim",
976981 total: 60,
977982 current: 60,
981986 },
982987 80: {
983988 {
984 style: BarStyle().TipOnComplete(">"),
989 style: BarStyle().TipOnComplete(),
985990 name: "t,c{60,59}",
986991 total: 60,
987992 current: 59,
988993 want: " [==========================================================================>-] ",
989994 },
990995 {
991 style: BarStyle().TipOnComplete(">"),
996 style: BarStyle().TipOnComplete(),
992997 name: "t,c{60,59}trim",
993998 total: 60,
994999 current: 59,
9961001 want: "[============================================================================>-]",
9971002 },
9981003 {
999 style: BarStyle().TipOnComplete(">"),
1004 style: BarStyle().TipOnComplete(),
10001005 name: "t,c,bw{60,59,60}",
10011006 total: 60,
10021007 current: 59,
10041009 want: " [========================================================>-] ",
10051010 },
10061011 {
1007 style: BarStyle().TipOnComplete(">"),
1012 style: BarStyle().TipOnComplete(),
10081013 name: "t,c,bw{60,59,60}trim",
10091014 total: 60,
10101015 current: 59,
10131018 want: "[========================================================>-]",
10141019 },
10151020 {
1016 style: BarStyle().TipOnComplete(">"),
1021 style: BarStyle().TipOnComplete(),
10171022 name: "t,c{60,60}",
10181023 total: 60,
10191024 current: 60,
10201025 want: " [===========================================================================>] ",
10211026 },
10221027 {
1023 style: BarStyle().TipOnComplete(">"),
1028 style: BarStyle().TipOnComplete(),
10241029 name: "t,c{60,60}trim",
10251030 total: 60,
10261031 current: 60,
10281033 want: "[=============================================================================>]",
10291034 },
10301035 {
1031 style: BarStyle().TipOnComplete(">"),
1036 style: BarStyle().TipOnComplete(),
10321037 name: "t,c,bw{60,60,60}",
10331038 total: 60,
10341039 current: 60,
10361041 want: " [=========================================================>] ",
10371042 },
10381043 {
1039 style: BarStyle().TipOnComplete(">"),
1044 style: BarStyle().TipOnComplete(),
10401045 name: "t,c,bw{60,60,60}trim",
10411046 total: 60,
10421047 current: 60,
10471052 },
10481053 99: {
10491054 {
1050 style: BarStyle().TipOnComplete(">"),
1055 style: BarStyle().TipOnComplete(),
10511056 name: "t,c{100,99}",
10521057 total: 100,
10531058 current: 99,
10541059 want: " [=============================================================================================>-] ",
10551060 },
10561061 {
1057 style: BarStyle().TipOnComplete(">"),
1062 style: BarStyle().TipOnComplete(),
10581063 name: "t,c{100,99}trim",
10591064 total: 100,
10601065 current: 99,
10621067 want: "[===============================================================================================>-]",
10631068 },
10641069 {
1065 style: BarStyle().TipOnComplete(">"),
1070 style: BarStyle().TipOnComplete(),
10661071 name: "t,c{100,100}",
10671072 total: 100,
10681073 current: 100,
10691074 want: " [==============================================================================================>] ",
10701075 },
10711076 {
1072 style: BarStyle().TipOnComplete(">"),
1077 style: BarStyle().TipOnComplete(),
10731078 name: "t,c{100,100}trim",
10741079 total: 100,
10751080 current: 100,
10771082 want: "[================================================================================================>]",
10781083 },
10791084 {
1080 style: BarStyle().TipOnComplete(">"),
1085 style: BarStyle().TipOnComplete(),
10811086 name: "t,c,r{100,100,99}",
10821087 total: 100,
10831088 current: 100,
10851090 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
10861091 },
10871092 {
1088 style: BarStyle().TipOnComplete(">"),
1093 style: BarStyle().TipOnComplete(),
10891094 name: "t,c,r{100,100,99}trim",
10901095 total: 100,
10911096 current: 100,
10941099 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
10951100 },
10961101 {
1097 style: BarStyle().TipOnComplete("<").Reverse(),
1098 name: `t,c,r{100,100,99}TipOnComplete("<").Reverse()`,
1102 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1103 name: `t,c,r{100,100,99}.Tip("<").TipOnComplete().Reverse()`,
10991104 total: 100,
11001105 current: 100,
11011106 refill: 99,
11021107 want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
11031108 },
11041109 {
1105 style: BarStyle().TipOnComplete("<").Reverse(),
1106 name: `t,c,r{100,100,99}TipOnComplete("<").Reverse()trim`,
1110 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1111 name: `t,c,r{100,100,99}.Tip("<").TipOnComplete().Reverse()trim`,
11071112 total: 100,
11081113 current: 100,
11091114 refill: 99,
11111116 want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
11121117 },
11131118 {
1114 style: BarStyle().TipOnComplete(">"),
1119 style: BarStyle().TipOnComplete(),
11151120 name: "t,c,r{100,100,100}",
11161121 total: 100,
11171122 current: 100,
11191124 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
11201125 },
11211126 {
1122 style: BarStyle().TipOnComplete(">"),
1127 style: BarStyle().TipOnComplete(),
11231128 name: "t,c,r{100,100,100}trim",
11241129 total: 100,
11251130 current: 100,
11281133 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
11291134 },
11301135 {
1131 style: BarStyle().TipOnComplete("<").Reverse(),
1132 name: `t,c,r{100,100,100}TipOnComplete("<").Reverse()`,
1136 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1137 name: `t,c,r{100,100,100}.Tip("<").TipOnComplete().Reverse()`,
11331138 total: 100,
11341139 current: 100,
11351140 refill: 100,
11361141 want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
11371142 },
11381143 {
1139 style: BarStyle().TipOnComplete("<").Reverse(),
1140 name: `t,c,r{100,100,100}TipOnComplete("<").Reverse()trim`,
1144 style: BarStyle().Tip("<").TipOnComplete().Reverse(),
1145 name: `t,c,r{100,100,100}.Tip("<").TipOnComplete().Reverse()trim`,
11411146 total: 100,
11421147 current: 100,
11431148 refill: 100,
11471152 },
11481153 100: {
11491154 {
1150 style: BarStyle().TipOnComplete(">"),
1155 style: BarStyle().TipOnComplete(),
11511156 name: "t,c{100,99}",
11521157 total: 100,
11531158 current: 99,
11541159 want: " [==============================================================================================>-] ",
11551160 },
11561161 {
1157 style: BarStyle().TipOnComplete(">"),
1162 style: BarStyle().TipOnComplete(),
11581163 name: "t,c{100,99}trim",
11591164 total: 100,
11601165 current: 99,
11621167 want: "[================================================================================================>-]",
11631168 },
11641169 {
1165 style: BarStyle().TipOnComplete(">"),
1170 style: BarStyle().TipOnComplete(),
11661171 name: "t,c{100,100}",
11671172 total: 100,
11681173 current: 100,
11691174 want: " [===============================================================================================>] ",
11701175 },
11711176 {
1172 style: BarStyle().TipOnComplete(">"),
1177 style: BarStyle().TipOnComplete(),
11731178 name: "t,c{100,100}trim",
11741179 total: 100,
11751180 current: 100,
11771182 want: "[=================================================================================================>]",
11781183 },
11791184 {
1180 style: BarStyle().TipOnComplete(">"),
1185 style: BarStyle().TipOnComplete(),
11811186 name: "t,c,r{100,100,99}",
11821187 total: 100,
11831188 current: 100,
11851190 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
11861191 },
11871192 {
1188 style: BarStyle().TipOnComplete(">"),
1193 style: BarStyle().TipOnComplete(),
11891194 name: "t,c,r{100,100,99}trim",
11901195 total: 100,
11911196 current: 100,
11941199 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
11951200 },
11961201 {
1197 style: BarStyle().TipOnComplete(">"),
1202 style: BarStyle().TipOnComplete(),
11981203 name: "t,c,r{100,100,100}",
11991204 total: 100,
12001205 current: 100,
12021207 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
12031208 },
12041209 {
1205 style: BarStyle().TipOnComplete(">"),
1210 style: BarStyle().TipOnComplete(),
12061211 name: "t,c,r{100,100,100}trim",
12071212 total: 100,
12081213 current: 100,
12221227 s.current = tc.current
12231228 s.trimSpace = tc.trim
12241229 s.refill = tc.refill
1230 s.completed = tc.total > 0 && tc.current >= tc.total
12251231 tmpBuf.Reset()
1226 _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
1232 r, err := s.draw(newStatistics(tw, s))
1233 if err != nil {
1234 t.FailNow()
1235 }
1236 _, err = tmpBuf.ReadFrom(r)
12271237 if err != nil {
12281238 t.FailNow()
12291239 }
13031313 want: " [===============================================================================================] ",
13041314 },
13051315 {
1306 style: BarStyle().TipOnComplete("だ"),
1307 name: `t,c{100,100}TipOnComplete("だ")`,
1316 style: BarStyle().Tip("だ").TipOnComplete(),
1317 name: `t,c{100,100}.Tip("だ").TipOnComplete()`,
13081318 total: 100,
13091319 current: 100,
13101320 want: " [=============================================================================================だ] ",
13351345 name: `t,c{100,100}.Filler("の").Tip("だ").Padding("つ")`,
13361346 total: 100,
13371347 current: 100,
1338 want: " […ののののののののののののののののののののののののののののののののののののののののののののののの] ",
1348 want: " [ののののののののののののののののののののののののののののののののののののののののののののののの…] ",
13391349 },
13401350 {
13411351 style: BarStyle().Filler("の").Tip("だ").Padding("つ").Reverse(),
13421352 name: `t,c{100,100}Filler("の").Tip("だ").Padding("つ").Reverse()`,
13431353 total: 100,
13441354 current: 100,
1345 want: " [ののののののののののののののののののののののののののののののののののののののののののののののの…] ",
1346 },
1347 {
1348 style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ"),
1349 name: `t,c{100,99}Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ")`,
1355 want: " […ののののののののののののののののののののののののののののののののののののののののののののののの] ",
1356 },
1357 {
1358 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ"),
1359 name: `t,c{100,99}Filler("の").Tip("だ").TipOnComplete().Padding("つ")`,
13501360 total: 100,
13511361 current: 99,
13521362 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
13531363 },
13541364 {
1355 style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ"),
1356 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ")`,
1357 total: 100,
1358 current: 100,
1359 want: " […ののののののののののののののののののののののののののののののののののののののののののののののだ] ",
1360 },
1361 {
1362 style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ").Reverse(),
1363 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ").Reverse()`,
1364 total: 100,
1365 current: 100,
1366 want: " [だのののののののののののののののののののののののののののののののののののののののののののののの…] ",
1365 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ"),
1366 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete().Padding("つ")`,
1367 total: 100,
1368 current: 100,
1369 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1370 },
1371 {
1372 style: BarStyle().Filler("の").Tip("だ").TipOnComplete().Padding("つ").Reverse(),
1373 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete().Padding("つ").Reverse()`,
1374 total: 100,
1375 current: 100,
1376 want: " […だのののののののののののののののののののののののののののののののののののののののののののののの] ",
13671377 },
13681378 {
13691379 style: BarStyle().Refiller("の"),
13941404 s.current = tc.current
13951405 s.trimSpace = tc.trim
13961406 s.refill = tc.refill
1407 s.completed = tc.total > 0 && tc.current >= tc.total
13971408 tmpBuf.Reset()
1398 _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
1409 r, err := s.draw(newStatistics(tw, s))
1410 if err != nil {
1411 t.FailNow()
1412 }
1413 _, err = tmpBuf.ReadFrom(r)
13991414 if err != nil {
14001415 t.FailNow()
14011416 }
22 import (
33 crand "crypto/rand"
44 "io"
5 "io/ioutil"
65 "math/rand"
76 "time"
87
9 "github.com/vbauerster/mpb/v7"
10 "github.com/vbauerster/mpb/v7/decor"
8 "github.com/vbauerster/mpb/v8"
9 "github.com/vbauerster/mpb/v8/decor"
1110 )
1211
1312 func Example() {
2524 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
2625 // replace ETA decorator with "done" message, OnComplete event
2726 decor.OnComplete(
28 // ETA decorator with ewma age of 60, and width reservation of 4
29 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WC{W: 4}), "done",
27 decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "done",
3028 ),
3129 ),
3230 mpb.AppendDecorators(decor.Percentage()),
3432 // simulating some work
3533 max := 100 * time.Millisecond
3634 for i := 0; i < total; i++ {
37 // start variable is solely for EWMA calculation
38 // EWMA's unit of measure is an iteration's duration
39 start := time.Now()
4035 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
4136 bar.Increment()
42 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
43 bar.DecoratorEwmaUpdate(time.Since(start))
4437 }
4538 // wait for our bar to complete and flush
4639 p.Wait()
7770 defer proxyReader.Close()
7871
7972 // and copy from reader, ignoring errors
80 _, _ = io.Copy(ioutil.Discard, proxyReader)
73 _, _ = io.Copy(io.Discard, proxyReader)
8174
8275 p.Wait()
8376 }
11
22 // make syncWidth func public in test
33 var SyncWidth = syncWidth
4 var MaxWidthDistributor = &maxWidthDistributor
4
5 type PriorityQueue = priorityQueue
0 module github.com/vbauerster/mpb/v7
0 module github.com/vbauerster/mpb/v8
11
22 require (
33 github.com/VividCortex/ewma v1.2.0
44 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
5 github.com/mattn/go-runewidth v0.0.13
6 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
5 github.com/mattn/go-runewidth v0.0.15
6 golang.org/x/sys v0.11.0
77 )
88
9 go 1.14
9 require github.com/rivo/uniseg v0.4.4 // indirect
10
11 go 1.17
11 github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
22 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
33 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
4 github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
5 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
6 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
4 github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
5 github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
76 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
8 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
9 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
7 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
8 github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
9 golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
10 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
0 package mpb
1
2 import "container/heap"
3
4 type heapManager chan heapRequest
5
6 type heapCmd int
7
8 const (
9 h_sync heapCmd = iota
10 h_push
11 h_iter
12 h_drain
13 h_fix
14 h_state
15 h_end
16 )
17
18 type heapRequest struct {
19 cmd heapCmd
20 data interface{}
21 }
22
23 type iterData struct {
24 iter chan<- *Bar
25 drop <-chan struct{}
26 }
27
28 type pushData struct {
29 bar *Bar
30 sync bool
31 }
32
33 type fixData struct {
34 bar *Bar
35 priority int
36 lazy bool
37 }
38
39 func (m heapManager) run() {
40 var bHeap priorityQueue
41 var pMatrix, aMatrix map[int][]chan int
42
43 var l int
44 var sync bool
45
46 for req := range m {
47 switch req.cmd {
48 case h_push:
49 data := req.data.(pushData)
50 heap.Push(&bHeap, data.bar)
51 if !sync {
52 sync = data.sync
53 }
54 case h_sync:
55 if sync || l != bHeap.Len() {
56 pMatrix = make(map[int][]chan int)
57 aMatrix = make(map[int][]chan int)
58 for _, b := range bHeap {
59 table := b.wSyncTable()
60 for i, ch := range table[0] {
61 pMatrix[i] = append(pMatrix[i], ch)
62 }
63 for i, ch := range table[1] {
64 aMatrix[i] = append(aMatrix[i], ch)
65 }
66 }
67 sync = false
68 l = bHeap.Len()
69 }
70 drop := req.data.(<-chan struct{})
71 syncWidth(pMatrix, drop)
72 syncWidth(aMatrix, drop)
73 case h_iter:
74 data := req.data.(iterData)
75 drop_iter:
76 for _, b := range bHeap {
77 select {
78 case data.iter <- b:
79 case <-data.drop:
80 break drop_iter
81 }
82 }
83 close(data.iter)
84 case h_drain:
85 data := req.data.(iterData)
86 drop_drain:
87 for bHeap.Len() != 0 {
88 select {
89 case data.iter <- heap.Pop(&bHeap).(*Bar):
90 case <-data.drop:
91 break drop_drain
92 }
93 }
94 close(data.iter)
95 case h_fix:
96 data := req.data.(fixData)
97 if data.bar.index < 0 {
98 break
99 }
100 data.bar.priority = data.priority
101 if !data.lazy {
102 heap.Fix(&bHeap, data.bar.index)
103 }
104 case h_state:
105 ch := req.data.(chan<- bool)
106 ch <- sync || l != bHeap.Len()
107 case h_end:
108 ch := req.data.(chan<- interface{})
109 if ch != nil {
110 go func() {
111 ch <- []*Bar(bHeap)
112 }()
113 }
114 close(m)
115 }
116 }
117 }
118
119 func (m heapManager) sync(drop <-chan struct{}) {
120 m <- heapRequest{cmd: h_sync, data: drop}
121 }
122
123 func (m heapManager) push(b *Bar, sync bool) {
124 data := pushData{b, sync}
125 m <- heapRequest{cmd: h_push, data: data}
126 }
127
128 func (m heapManager) iter(iter chan<- *Bar, drop <-chan struct{}) {
129 data := iterData{iter, drop}
130 m <- heapRequest{cmd: h_iter, data: data}
131 }
132
133 func (m heapManager) drain(iter chan<- *Bar, drop <-chan struct{}) {
134 data := iterData{iter, drop}
135 m <- heapRequest{cmd: h_drain, data: data}
136 }
137
138 func (m heapManager) fix(b *Bar, priority int, lazy bool) {
139 data := fixData{b, priority, lazy}
140 m <- heapRequest{cmd: h_fix, data: data}
141 }
142
143 func (m heapManager) state(ch chan<- bool) {
144 m <- heapRequest{cmd: h_state, data: ch}
145 }
146
147 func (m heapManager) end(ch chan<- interface{}) {
148 m <- heapRequest{cmd: h_end, data: ch}
149 }
150
151 func syncWidth(matrix map[int][]chan int, drop <-chan struct{}) {
152 for _, column := range matrix {
153 go maxWidthDistributor(column, drop)
154 }
155 }
156
157 func maxWidthDistributor(column []chan int, drop <-chan struct{}) {
158 var maxWidth int
159 for _, ch := range column {
160 select {
161 case w := <-ch:
162 if w > maxWidth {
163 maxWidth = w
164 }
165 case <-drop:
166 return
167 }
168 }
169 for _, ch := range column {
170 ch <- maxWidth
171 }
172 }
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"
109 "sync"
1110 "time"
1211
13 "github.com/vbauerster/mpb/v7/cwriter"
14 "github.com/vbauerster/mpb/v7/decor"
12 "github.com/vbauerster/mpb/v8/cwriter"
13 "github.com/vbauerster/mpb/v8/decor"
1514 )
1615
1716 const (
18 // default RefreshRate
19 prr = 150 * time.Millisecond
17 defaultRefreshRate = 150 * time.Millisecond
2018 )
2119
22 // Progress represents a container that renders one or more progress
23 // bars.
20 // DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
21 var DoneError = fmt.Errorf("%T instance can't be reused after it's done", (*Progress)(nil))
22
23 // Progress represents a container that renders one or more progress bars.
2424 type Progress struct {
25 uwg *sync.WaitGroup
26 pwg, bwg sync.WaitGroup
27 operateState chan func(*pState)
28 interceptIO chan func(io.Writer)
29 done <-chan struct{}
30 cancel func()
31 }
32
33 // pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
34 type pState struct {
2535 ctx context.Context
26 uwg *sync.WaitGroup
27 cwg *sync.WaitGroup
28 bwg *sync.WaitGroup
29 operateState chan func(*pState)
30 done chan struct{}
31 refreshCh chan time.Time
32 once sync.Once
33 }
34
35 // pState holds bars in its priorityQueue. It gets passed to
36 // *Progress.serve(...) monitor goroutine.
37 type pState struct {
38 bHeap priorityQueue
39 heapUpdated bool
40 pMatrix map[int][]chan int
41 aMatrix map[int][]chan int
42 barShutdownQueue []*Bar
36 hm heapManager
37 dropS, dropD chan struct{}
38 renderReq chan time.Time
39 idCount int
40 popPriority int
4341
4442 // following are provided/overrided by user
45 idCount int
43 refreshRate time.Duration
4644 reqWidth int
4745 popCompleted bool
48 outputDiscarded bool
49 rr time.Duration
50 uwg *sync.WaitGroup
51 externalRefresh <-chan interface{}
52 renderDelay <-chan struct{}
53 shutdownNotifier chan struct{}
54 parkedBars map[*Bar]*Bar
46 autoRefresh bool
47 delayRC <-chan struct{}
48 manualRC <-chan interface{}
49 shutdownNotifier chan<- interface{}
50 queueBars map[*Bar]*Bar
5551 output io.Writer
5652 debugOut io.Writer
53 uwg *sync.WaitGroup
5754 }
5855
5956 // New creates new Progress container instance. It's not possible to
60 // reuse instance after *Progress.Wait() method has been called.
57 // reuse instance after (*Progress).Wait method has been called.
6158 func New(options ...ContainerOption) *Progress {
6259 return NewWithContext(context.Background(), options...)
6360 }
6461
6562 // NewWithContext creates new Progress container instance with provided
66 // context. It's not possible to reuse instance after *Progress.Wait()
63 // context. It's not possible to reuse instance after (*Progress).Wait
6764 // method has been called.
6865 func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
66 if ctx == nil {
67 ctx = context.Background()
68 }
69 ctx, cancel := context.WithCancel(ctx)
6970 s := &pState{
70 bHeap: priorityQueue{},
71 rr: prr,
72 parkedBars: make(map[*Bar]*Bar),
73 output: os.Stdout,
71 ctx: ctx,
72 hm: make(heapManager),
73 dropS: make(chan struct{}),
74 dropD: make(chan struct{}),
75 renderReq: make(chan time.Time),
76 refreshRate: defaultRefreshRate,
77 popPriority: math.MinInt32,
78 queueBars: make(map[*Bar]*Bar),
79 output: os.Stdout,
80 debugOut: io.Discard,
7481 }
7582
7683 for _, opt := range options {
8087 }
8188
8289 p := &Progress{
83 ctx: ctx,
8490 uwg: s.uwg,
85 cwg: new(sync.WaitGroup),
86 bwg: new(sync.WaitGroup),
8791 operateState: make(chan func(*pState)),
88 done: make(chan struct{}),
89 }
90
91 p.cwg.Add(1)
92 go p.serve(s, cwriter.New(s.output))
92 interceptIO: make(chan func(io.Writer)),
93 cancel: cancel,
94 }
95
96 cw := cwriter.New(s.output)
97 if s.manualRC != nil {
98 done := make(chan struct{})
99 p.done = done
100 s.autoRefresh = false
101 go s.manualRefreshListener(done)
102 } else if cw.IsTerminal() || s.autoRefresh {
103 done := make(chan struct{})
104 p.done = done
105 s.autoRefresh = true
106 go s.autoRefreshListener(done)
107 } else {
108 p.done = ctx.Done()
109 s.autoRefresh = false
110 }
111
112 p.pwg.Add(1)
113 go p.serve(s, cw)
114 go s.hm.run()
93115 return p
94116 }
95117
103125 return p.New(total, SpinnerStyle(), options...)
104126 }
105127
106 // New creates a bar with provided BarFillerBuilder.
128 // New creates a bar by calling `Build` method on provided `BarFillerBuilder`.
107129 func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
108 return p.Add(total, builder.Build(), options...)
109 }
110
111 // Add creates a bar which renders itself by provided filler.
112 // If `total <= 0` trigger complete event is disabled until reset with *bar.SetTotal(int64, bool).
113 // Panics if *Progress instance is done, i.e. called after *Progress.Wait().
114 func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
130 return p.MustAdd(total, builder.Build(), options...)
131 }
132
133 // MustAdd creates a bar which renders itself by provided BarFiller.
134 // If `total <= 0` triggering complete event by increment methods is
135 // disabled. Panics if *Progress instance is done, i.e. called after
136 // (*Progress).Wait().
137 func (p *Progress) MustAdd(total int64, filler BarFiller, options ...BarOption) *Bar {
138 bar, err := p.Add(total, filler, options...)
139 if err != nil {
140 panic(err)
141 }
142 return bar
143 }
144
145 // Add creates a bar which renders itself by provided BarFiller.
146 // If `total <= 0` triggering complete event by increment methods
147 // is disabled. If *Progress instance is done, i.e. called after
148 // (*Progress).Wait(), return error == DoneError.
149 func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) (*Bar, error) {
115150 if filler == nil {
116151 filler = NopStyle().Build()
117152 }
118 p.bwg.Add(1)
119 result := make(chan *Bar)
153 type result struct {
154 bar *Bar
155 bs *bState
156 }
157 ch := make(chan result)
120158 select {
121159 case p.operateState <- func(ps *pState) {
122160 bs := ps.makeBarState(total, filler, options...)
123 bar := newBar(p, bs)
124 if bs.runningBar != nil {
125 bs.runningBar.noPop = true
126 ps.parkedBars[bs.runningBar] = bar
161 bar := newBar(ps.ctx, p, bs)
162 if bs.waitBar != nil {
163 ps.queueBars[bs.waitBar] = bar
127164 } else {
128 heap.Push(&ps.bHeap, bar)
129 ps.heapUpdated = true
165 ps.hm.push(bar, true)
130166 }
131167 ps.idCount++
132 result <- bar
168 ch <- result{bar, bs}
133169 }:
134 bar := <-result
135 bar.subscribeDecorators()
136 return bar
170 res := <-ch
171 bar, bs := res.bar, res.bs
172 bar.TraverseDecorators(func(d decor.Decorator) {
173 if d, ok := d.(decor.AverageDecorator); ok {
174 bs.averageDecorators = append(bs.averageDecorators, d)
175 }
176 if d, ok := d.(decor.EwmaDecorator); ok {
177 bs.ewmaDecorators = append(bs.ewmaDecorators, d)
178 }
179 if d, ok := d.(decor.ShutdownListener); ok {
180 bs.shutdownListeners = append(bs.shutdownListeners, d)
181 }
182 })
183 return bar, nil
137184 case <-p.done:
138 p.bwg.Done()
139 panic(fmt.Sprintf("%T instance can't be reused after it's done!", p))
140 }
141 }
142
143 func (p *Progress) dropBar(b *Bar) {
185 return nil, DoneError
186 }
187 }
188
189 func (p *Progress) traverseBars(cb func(b *Bar) bool) {
190 iter, drop := make(chan *Bar), make(chan struct{})
144191 select {
145 case p.operateState <- func(s *pState) {
146 if b.index < 0 {
147 return
148 }
149 heap.Remove(&s.bHeap, b.index)
150 s.heapUpdated = true
192 case p.operateState <- func(s *pState) { s.hm.iter(iter, drop) }:
193 for b := range iter {
194 if cb(b) {
195 close(drop)
196 break
197 }
198 }
199 case <-p.done:
200 }
201 }
202
203 // UpdateBarPriority either immediately or lazy.
204 // With lazy flag order is updated after the next refresh cycle.
205 // If you don't care about laziness just use *Bar.SetPriority(int).
206 func (p *Progress) UpdateBarPriority(b *Bar, priority int, lazy bool) {
207 if b == nil {
208 return
209 }
210 select {
211 case p.operateState <- func(s *pState) { s.hm.fix(b, priority, lazy) }:
212 case <-p.done:
213 }
214 }
215
216 // Write is implementation of io.Writer.
217 // Writing to `*mpb.Progress` will print lines above a running bar.
218 // Writes aren't flushed immediately, but at next refresh cycle.
219 // If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
220 // is returned.
221 func (p *Progress) Write(b []byte) (int, error) {
222 type result struct {
223 n int
224 err error
225 }
226 ch := make(chan result)
227 select {
228 case p.interceptIO <- func(w io.Writer) {
229 n, err := w.Write(b)
230 ch <- result{n, err}
151231 }:
232 res := <-ch
233 return res.n, res.err
152234 case <-p.done:
153 }
154 }
155
156 func (p *Progress) traverseBars(cb func(b *Bar) bool) {
157 done := make(chan struct{})
158 select {
159 case p.operateState <- func(s *pState) {
160 for i := 0; i < s.bHeap.Len(); i++ {
161 bar := s.bHeap[i]
162 if !cb(bar) {
163 break
164 }
165 }
166 close(done)
167 }:
168 <-done
169 case <-p.done:
170 }
171 }
172
173 // UpdateBarPriority same as *Bar.SetPriority(int).
174 func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
175 select {
176 case p.operateState <- func(s *pState) {
177 if b.index < 0 {
178 return
179 }
180 b.priority = priority
181 heap.Fix(&s.bHeap, b.index)
182 }:
183 case <-p.done:
184 }
185 }
186
187 // BarCount returns bars count.
188 func (p *Progress) BarCount() int {
189 result := make(chan int)
190 select {
191 case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
192 return <-result
193 case <-p.done:
194 return 0
195 }
196 }
197
198 // Wait waits for all bars to complete and finally shutdowns container.
199 // After this method has been called, there is no way to reuse *Progress
200 // instance.
235 return 0, DoneError
236 }
237 }
238
239 // Wait waits for all bars to complete and finally shutdowns container. After
240 // this method has been called, there is no way to reuse (*Progress) instance.
201241 func (p *Progress) Wait() {
242 // wait for user wg, if any
202243 if p.uwg != nil {
203 // wait for user wg
204244 p.uwg.Wait()
205245 }
206246
207 // wait for bars to quit, if any
208247 p.bwg.Wait()
209
210 p.once.Do(p.shutdown)
211
212 // wait for container to quit
213 p.cwg.Wait()
214 }
215
216 func (p *Progress) shutdown() {
217 close(p.done)
248 p.Shutdown()
249 }
250
251 // Shutdown cancels any running bar immediately and then shutdowns (*Progress)
252 // instance. Normally this method shouldn't be called unless you know what you
253 // are doing. Proper way to shutdown is to call (*Progress).Wait() instead.
254 func (p *Progress) Shutdown() {
255 p.cancel()
256 p.pwg.Wait()
218257 }
219258
220259 func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
221 defer p.cwg.Done()
222
223 p.refreshCh = s.newTicker(p.done)
260 defer p.pwg.Done()
261 render := func() error { return s.render(cw) }
262 var err error
224263
225264 for {
226265 select {
227266 case op := <-p.operateState:
228267 op(s)
229 case <-p.refreshCh:
230 if err := s.render(cw); err != nil {
231 if s.debugOut != nil {
232 _, e := fmt.Fprintln(s.debugOut, err)
233 if e != nil {
234 panic(err)
235 }
268 case fn := <-p.interceptIO:
269 fn(cw)
270 case <-s.renderReq:
271 e := render()
272 if e != nil {
273 p.cancel() // cancel all bars
274 render = func() error { return nil }
275 err = e
276 }
277 case <-p.done:
278 update := make(chan bool)
279 for s.autoRefresh && err == nil {
280 s.hm.state(update)
281 if <-update {
282 err = render()
236283 } else {
237 panic(err)
284 break
238285 }
239286 }
240 case <-s.shutdownNotifier:
241 for s.heapUpdated {
242 if err := s.render(cw); err != nil {
243 if s.debugOut != nil {
244 _, e := fmt.Fprintln(s.debugOut, err)
245 if e != nil {
246 panic(err)
247 }
248 } else {
249 panic(err)
250 }
287 if err != nil {
288 _, _ = fmt.Fprintln(s.debugOut, err.Error())
289 }
290 s.hm.end(s.shutdownNotifier)
291 return
292 }
293 }
294 }
295
296 func (s pState) autoRefreshListener(done chan struct{}) {
297 if s.delayRC != nil {
298 <-s.delayRC
299 }
300 ticker := time.NewTicker(s.refreshRate)
301 defer ticker.Stop()
302 for {
303 select {
304 case t := <-ticker.C:
305 s.renderReq <- t
306 case <-s.ctx.Done():
307 close(done)
308 return
309 }
310 }
311 }
312
313 func (s pState) manualRefreshListener(done chan struct{}) {
314 for {
315 select {
316 case x := <-s.manualRC:
317 if t, ok := x.(time.Time); ok {
318 s.renderReq <- t
319 } else {
320 s.renderReq <- time.Now()
321 }
322 case <-s.ctx.Done():
323 close(done)
324 return
325 }
326 }
327 }
328
329 func (s *pState) render(cw *cwriter.Writer) (err error) {
330 s.hm.sync(s.dropS)
331 iter := make(chan *Bar)
332 go s.hm.iter(iter, s.dropS)
333
334 var width, height int
335 if cw.IsTerminal() {
336 width, height, err = cw.GetTermSize()
337 if err != nil {
338 close(s.dropS)
339 return err
340 }
341 } else {
342 if s.reqWidth > 0 {
343 width = s.reqWidth
344 } else {
345 width = 100
346 }
347 height = 100
348 }
349
350 for b := range iter {
351 go b.render(width)
352 }
353
354 return s.flush(cw, height)
355 }
356
357 func (s *pState) flush(cw *cwriter.Writer, height int) error {
358 var wg sync.WaitGroup
359 defer wg.Wait() // waiting for all s.hm.push to complete
360
361 var popCount int
362 var rows []io.Reader
363
364 iter := make(chan *Bar)
365 s.hm.drain(iter, s.dropD)
366
367 for b := range iter {
368 frame := <-b.frameCh
369 if frame.err != nil {
370 close(s.dropD)
371 b.cancel()
372 return frame.err // b.frameCh is buffered it's ok to return here
373 }
374 var usedRows int
375 for i := len(frame.rows) - 1; i >= 0; i-- {
376 if row := frame.rows[i]; len(rows) < height {
377 rows = append(rows, row)
378 usedRows++
379 } else {
380 _, _ = io.Copy(io.Discard, row)
381 }
382 }
383 if frame.shutdown != 0 && !frame.done {
384 if qb, ok := s.queueBars[b]; ok {
385 b.cancel()
386 delete(s.queueBars, b)
387 qb.priority = b.priority
388 wg.Add(1)
389 go func(b *Bar) {
390 s.hm.push(b, true)
391 wg.Done()
392 }(qb)
393 continue
394 }
395 if s.popCompleted && !frame.noPop {
396 switch frame.shutdown {
397 case 1:
398 b.priority = s.popPriority
399 s.popPriority++
400 default:
401 b.cancel()
402 popCount += usedRows
403 continue
251404 }
252 }
253 return
254 }
255 }
256 }
257
258 func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
259 ch := make(chan time.Time)
260 if s.shutdownNotifier == nil {
261 s.shutdownNotifier = make(chan struct{})
262 }
263 go func() {
264 if s.renderDelay != nil {
265 <-s.renderDelay
266 }
267 var internalRefresh <-chan time.Time
268 if !s.outputDiscarded {
269 if s.externalRefresh == nil {
270 ticker := time.NewTicker(s.rr)
271 defer ticker.Stop()
272 internalRefresh = ticker.C
273 }
274 } else {
275 s.externalRefresh = nil
276 }
277 for {
278 select {
279 case t := <-internalRefresh:
280 ch <- t
281 case x := <-s.externalRefresh:
282 if t, ok := x.(time.Time); ok {
283 ch <- t
284 } else {
285 ch <- time.Now()
286 }
287 case <-done:
288 close(s.shutdownNotifier)
289 return
290 }
291 }
292 }()
293 return ch
294 }
295
296 func (s *pState) render(cw *cwriter.Writer) error {
297 if s.heapUpdated {
298 s.updateSyncMatrix()
299 s.heapUpdated = false
300 }
301 syncWidth(s.pMatrix)
302 syncWidth(s.aMatrix)
303
304 tw, err := cw.GetWidth()
305 if err != nil {
306 tw = s.reqWidth
307 }
308 for i := 0; i < s.bHeap.Len(); i++ {
309 bar := s.bHeap[i]
310 go bar.render(tw)
311 }
312
313 return s.flush(cw)
314 }
315
316 func (s *pState) flush(cw *cwriter.Writer) error {
317 var totalLines int
318 bm := make(map[*Bar]int, s.bHeap.Len())
319 for s.bHeap.Len() > 0 {
320 b := heap.Pop(&s.bHeap).(*Bar)
321 frame := <-b.frameCh
322 _, err := cw.ReadFrom(frame.reader)
405 } else if frame.rmOnComplete {
406 b.cancel()
407 continue
408 } else {
409 b.cancel()
410 }
411 }
412 wg.Add(1)
413 go func(b *Bar) {
414 s.hm.push(b, false)
415 wg.Done()
416 }(b)
417 }
418
419 for i := len(rows) - 1; i >= 0; i-- {
420 _, err := cw.ReadFrom(rows[i])
323421 if err != nil {
324422 return err
325423 }
326 if b.toShutdown {
327 if b.recoveredPanic != nil {
328 s.barShutdownQueue = append(s.barShutdownQueue, b)
329 b.toShutdown = false
330 } else {
331 // shutdown at next flush
332 // this ensures no bar ends up with less than 100% rendered
333 defer func() {
334 s.barShutdownQueue = append(s.barShutdownQueue, b)
335 }()
336 }
337 }
338 bm[b] = frame.lines
339 totalLines += frame.lines
340 }
341
342 for _, b := range s.barShutdownQueue {
343 if parkedBar := s.parkedBars[b]; parkedBar != nil {
344 parkedBar.priority = b.priority
345 heap.Push(&s.bHeap, parkedBar)
346 delete(s.parkedBars, b)
347 b.toDrop = true
348 }
349 if s.popCompleted && !b.noPop {
350 totalLines -= bm[b]
351 b.toDrop = true
352 }
353 if b.toDrop {
354 delete(bm, b)
355 s.heapUpdated = true
356 }
357 b.cancel()
358 }
359 s.barShutdownQueue = s.barShutdownQueue[0:0]
360
361 for b := range bm {
362 heap.Push(&s.bHeap, b)
363 }
364
365 return cw.Flush(totalLines)
366 }
367
368 func (s *pState) updateSyncMatrix() {
369 s.pMatrix = make(map[int][]chan int)
370 s.aMatrix = make(map[int][]chan int)
371 for i := 0; i < s.bHeap.Len(); i++ {
372 bar := s.bHeap[i]
373 table := bar.wSyncTable()
374 pRow, aRow := table[0], table[1]
375
376 for i, ch := range pRow {
377 s.pMatrix[i] = append(s.pMatrix[i], ch)
378 }
379
380 for i, ch := range aRow {
381 s.aMatrix[i] = append(s.aMatrix[i], ch)
382 }
383 }
384 }
385
386 func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
424 }
425
426 return cw.Flush(len(rows) - popCount)
427 }
428
429 func (s pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
387430 bs := &bState{
388 id: s.idCount,
389 priority: s.idCount,
390 reqWidth: s.reqWidth,
391 total: total,
392 filler: filler,
393 extender: func(r io.Reader, _ int, _ decor.Statistics) (io.Reader, int) { return r, 0 },
394 debugOut: s.debugOut,
431 id: s.idCount,
432 priority: s.idCount,
433 reqWidth: s.reqWidth,
434 total: total,
435 filler: filler,
436 renderReq: s.renderReq,
437 autoRefresh: s.autoRefresh,
395438 }
396439
397440 if total > 0 {
404447 }
405448 }
406449
407 if bs.middleware != nil {
408 bs.filler = bs.middleware(filler)
409 bs.middleware = nil
410 }
411
412 if s.popCompleted && !bs.noPop {
413 bs.priority = -(math.MaxInt32 - s.idCount)
414 }
415
416450 for i := 0; i < len(bs.buffers); i++ {
417451 bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
418452 }
419453
420454 return bs
421455 }
422
423 func syncWidth(matrix map[int][]chan int) {
424 for _, column := range matrix {
425 go maxWidthDistributor(column)
426 }
427 }
428
429 var maxWidthDistributor = func(column []chan int) {
430 var maxWidth int
431 for _, ch := range column {
432 if w := <-ch; w > maxWidth {
433 maxWidth = w
434 }
435 }
436 for _, ch := range column {
437 ch <- maxWidth
438 }
439 }
11
22 import (
33 "bytes"
4 "container/heap"
45 "context"
5 "io/ioutil"
6 "math/rand"
7 "sync"
6 "errors"
7 "io"
8 "strings"
89 "testing"
910 "time"
1011
11 "github.com/vbauerster/mpb/v7"
12 "github.com/vbauerster/mpb/v7/decor"
12 "github.com/vbauerster/mpb/v8"
13 "github.com/vbauerster/mpb/v8/decor"
1314 )
1415
15 func init() {
16 rand.Seed(time.Now().UnixNano())
17 }
18
19 func TestBarCount(t *testing.T) {
20 p := mpb.New(mpb.WithOutput(ioutil.Discard))
21
22 var wg sync.WaitGroup
23 wg.Add(1)
16 const (
17 timeout = 300 * time.Millisecond
18 )
19
20 func TestWithContext(t *testing.T) {
21 shutdown := make(chan interface{})
22 ctx, cancel := context.WithCancel(context.Background())
23 p := mpb.NewWithContext(ctx,
24 mpb.WithOutput(io.Discard),
25 mpb.WithShutdownNotifier(shutdown),
26 )
27 _ = p.AddBar(0) // never complete bar
28 _ = p.AddBar(0) // never complete bar
29 go func() {
30 time.Sleep(10 * time.Millisecond)
31 cancel()
32 }()
33
34 p.Wait()
35
36 select {
37 case v := <-shutdown:
38 if l := len(v.([]*mpb.Bar)); l != 2 {
39 t.Errorf("Expected len of bars: %d, got: %d", 2, l)
40 }
41 case <-time.After(timeout):
42 t.Errorf("Progress didn't shutdown after %v", timeout)
43 }
44 }
45
46 func TestShutdownsWithErrFiller(t *testing.T) {
47 var debug bytes.Buffer
48 shutdown := make(chan interface{})
49 p := mpb.New(
50 mpb.WithShutdownNotifier(shutdown),
51 mpb.WithOutput(io.Discard),
52 mpb.WithDebugOutput(&debug),
53 mpb.WithAutoRefresh(),
54 )
55
56 var errReturnCount int
57 testError := errors.New("test error")
58 bar := p.AddBar(100,
59 mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller {
60 return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
61 if st.Current >= 22 {
62 errReturnCount++
63 return testError
64 }
65 return base.Fill(w, st)
66 })
67 }),
68 )
69
70 go func() {
71 for bar.IsRunning() {
72 bar.Increment()
73 time.Sleep(10 * time.Millisecond)
74 }
75 }()
76
77 p.Wait()
78
79 if errReturnCount != 1 {
80 t.Errorf("Expected errReturnCount: %d, got: %d\n", 1, errReturnCount)
81 }
82
83 select {
84 case v := <-shutdown:
85 if l := len(v.([]*mpb.Bar)); l != 0 {
86 t.Errorf("Expected len of bars: %d, got: %d\n", 0, l)
87 }
88 if err := strings.TrimSpace(debug.String()); err != testError.Error() {
89 t.Errorf("Expected err: %q, got %q\n", testError.Error(), err)
90 }
91 case <-time.After(timeout):
92 t.Errorf("Progress didn't shutdown after %v", timeout)
93 }
94 }
95
96 func TestShutdownAfterBarAbortWithDrop(t *testing.T) {
97 shutdown := make(chan interface{})
98 p := mpb.New(
99 mpb.WithShutdownNotifier(shutdown),
100 mpb.WithOutput(io.Discard),
101 mpb.WithAutoRefresh(),
102 )
24103 b := p.AddBar(100)
104
105 var count int
106 for i := 0; !b.Aborted(); i++ {
107 if i >= 10 {
108 count++
109 b.Abort(true)
110 } else {
111 b.Increment()
112 time.Sleep(10 * time.Millisecond)
113 }
114 }
115
116 p.Wait()
117
118 if count != 1 {
119 t.Errorf("Expected count: %d, got: %d", 1, count)
120 }
121
122 select {
123 case v := <-shutdown:
124 if l := len(v.([]*mpb.Bar)); l != 0 {
125 t.Errorf("Expected len of bars: %d, got: %d", 0, l)
126 }
127 case <-time.After(timeout):
128 t.Errorf("Progress didn't shutdown after %v", timeout)
129 }
130 }
131
132 func TestShutdownAfterBarAbortWithNoDrop(t *testing.T) {
133 shutdown := make(chan interface{})
134 p := mpb.New(
135 mpb.WithShutdownNotifier(shutdown),
136 mpb.WithOutput(io.Discard),
137 mpb.WithAutoRefresh(),
138 )
139 b := p.AddBar(100)
140
141 var count int
142 for i := 0; !b.Aborted(); i++ {
143 if i >= 10 {
144 count++
145 b.Abort(false)
146 } else {
147 b.Increment()
148 time.Sleep(10 * time.Millisecond)
149 }
150 }
151
152 p.Wait()
153
154 if count != 1 {
155 t.Errorf("Expected count: %d, got: %d", 1, count)
156 }
157
158 select {
159 case v := <-shutdown:
160 if l := len(v.([]*mpb.Bar)); l != 1 {
161 t.Errorf("Expected len of bars: %d, got: %d", 1, l)
162 }
163 case <-time.After(timeout):
164 t.Errorf("Progress didn't shutdown after %v", timeout)
165 }
166 }
167
168 func TestBarPristinePopOrder(t *testing.T) {
169 shutdown := make(chan interface{})
170 ctx, cancel := context.WithCancel(context.Background())
171 p := mpb.NewWithContext(ctx,
172 mpb.WithOutput(io.Discard), // auto refresh is disabled
173 mpb.WithShutdownNotifier(shutdown),
174 )
175 a := p.AddBar(100, mpb.BarPriority(1), mpb.BarID(1))
176 b := p.AddBar(100, mpb.BarPriority(2), mpb.BarID(2))
177 c := p.AddBar(100, mpb.BarPriority(3), mpb.BarID(3))
178 pristineOrder := []*mpb.Bar{c, b, a}
179
180 go cancel()
181
182 bars := (<-shutdown).([]*mpb.Bar)
183 if l := len(bars); l != 3 {
184 t.Fatalf("Expected len of bars: %d, got: %d", 3, l)
185 }
186
187 p.Wait()
188 pq := mpb.PriorityQueue(bars)
189
190 for _, b := range pristineOrder {
191 // higher priority pops first
192 if bar := heap.Pop(&pq).(*mpb.Bar); bar.ID() != b.ID() {
193 t.Errorf("Expected bar id: %d, got bar id: %d", b.ID(), bar.ID())
194 }
195 }
196 }
197
198 func makeUpdateBarPriorityTest(refresh, lazy bool) func(*testing.T) {
199 return func(t *testing.T) {
200 shutdown := make(chan interface{})
201 refreshCh := make(chan interface{})
202 ctx, cancel := context.WithCancel(context.Background())
203 p := mpb.NewWithContext(ctx,
204 mpb.WithOutput(io.Discard),
205 mpb.WithManualRefresh(refreshCh),
206 mpb.WithShutdownNotifier(shutdown),
207 )
208 a := p.AddBar(100, mpb.BarPriority(1), mpb.BarID(1))
209 b := p.AddBar(100, mpb.BarPriority(2), mpb.BarID(2))
210 c := p.AddBar(100, mpb.BarPriority(3), mpb.BarID(3))
211
212 p.UpdateBarPriority(c, 2, lazy)
213 p.UpdateBarPriority(b, 3, lazy)
214 checkOrder := []*mpb.Bar{b, c, a} // updated order
215
216 if refresh {
217 refreshCh <- time.Now()
218 } else if lazy {
219 checkOrder = []*mpb.Bar{c, b, a} // pristine order
220 }
221
222 go cancel()
223
224 bars := (<-shutdown).([]*mpb.Bar)
225 if l := len(bars); l != 3 {
226 t.Fatalf("Expected len of bars: %d, got: %d", 3, l)
227 }
228
229 p.Wait()
230 pq := mpb.PriorityQueue(bars)
231
232 for _, b := range checkOrder {
233 // higher priority pops first
234 if bar := heap.Pop(&pq).(*mpb.Bar); bar.ID() != b.ID() {
235 t.Errorf("Expected bar id: %d, got bar id: %d", b.ID(), bar.ID())
236 }
237 }
238 }
239 }
240
241 func TestUpdateBarPriority(t *testing.T) {
242 makeUpdateBarPriorityTest(false, false)(t)
243 makeUpdateBarPriorityTest(true, false)(t)
244 }
245
246 func TestUpdateBarPriorityLazy(t *testing.T) {
247 makeUpdateBarPriorityTest(false, true)(t)
248 makeUpdateBarPriorityTest(true, true)(t)
249 }
250
251 func TestNoOutput(t *testing.T) {
252 var buf bytes.Buffer
253 p := mpb.New(mpb.WithOutput(&buf))
254 bar := p.AddBar(100)
255
25256 go func() {
26 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
27 for i := 0; i < 100; i++ {
28 if i == 33 {
29 wg.Done()
30 }
31 b.Increment()
32 time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2)
33 }
34 }()
35
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: %d, got: %d\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 shutdown := make(chan struct{})
80 ctx, cancel := context.WithCancel(context.Background())
81 p := mpb.NewWithContext(ctx, mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(ioutil.Discard))
82
83 start := make(chan struct{})
84 done := make(chan struct{})
85 fail := make(chan struct{})
86 bar := p.AddBar(0) // never complete bar
87 go func() {
88 close(start)
89257 for !bar.Completed() {
90258 bar.Increment()
91 time.Sleep(randomDuration(100 * time.Millisecond))
92 }
93 close(done)
259 }
94260 }()
95261
96 go func() {
97 select {
98 case <-done:
99 p.Wait()
100 case <-time.After(150 * time.Millisecond):
101 close(fail)
102 }
103 }()
104
105 <-start
106 cancel()
107 select {
108 case <-shutdown:
109 case <-fail:
110 t.Error("Progress didn't shutdown")
111 }
112 }
113
114 // MaxWidthDistributor shouldn't stuck in the middle while removing or aborting a bar
115 func TestMaxWidthDistributor(t *testing.T) {
116
117 makeWrapper := func(f func([]chan int), start, end chan struct{}) func([]chan int) {
118 return func(column []chan int) {
119 start <- struct{}{}
120 f(column)
121 <-end
122 }
123 }
124
125 ready := make(chan struct{})
126 start := make(chan struct{})
127 end := make(chan struct{})
128 *mpb.MaxWidthDistributor = makeWrapper(*mpb.MaxWidthDistributor, start, end)
129
130 total := 80
131 numBars := 6
132 p := mpb.New(mpb.WithOutput(ioutil.Discard))
133 for i := 0; i < numBars; i++ {
134 bar := p.AddBar(int64(total),
135 mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0),
136 mpb.PrependDecorators(
137 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace),
138 ),
139 )
140 go func() {
141 <-ready
142 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
143 for i := 0; i < total; i++ {
144 start := time.Now()
145 if id := bar.ID(); id > 1 && i >= 42 {
146 if id&1 == 1 {
147 bar.Abort(true)
148 } else {
149 bar.Abort(false)
150 }
151 }
152 time.Sleep((time.Duration(rng.Intn(10)+1) * (50 * time.Millisecond)) / 2)
153 bar.IncrInt64(rand.Int63n(5) + 1)
154 bar.DecoratorEwmaUpdate(time.Since(start))
155 }
156 }()
157 }
158
159 go func() {
160 <-ready
161 p.Wait()
162 close(start)
163 }()
164
165 res := t.Run("maxWidthDistributor", func(t *testing.T) {
166 close(ready)
167 for v := range start {
168 timer := time.NewTimer(100 * time.Millisecond)
169 select {
170 case end <- v:
171 timer.Stop()
172 case <-timer.C:
173 t.FailNow()
174 }
175 }
176 })
177
178 if !res {
179 t.Error("maxWidthDistributor stuck in the middle")
180 }
181 }
182
183 func getLastLine(bb []byte) []byte {
184 split := bytes.Split(bb, []byte("\n"))
185 return split[len(split)-2]
186 }
187
188 func randomDuration(max time.Duration) time.Duration {
189 return time.Duration(rand.Intn(10)+1) * max / 10
190 }
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
1312 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(-1, true)
18 }
1915 return n, err
2016 }
2117
2218 type proxyWriterTo struct {
2319 proxyReader
24 wt io.WriterTo
2520 }
2621
2722 func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
28 n, err := x.wt.WriteTo(w)
23 n, err := x.ReadCloser.(io.WriterTo).WriteTo(w)
2924 x.bar.IncrInt64(n)
30 if err == io.EOF {
31 go x.bar.SetTotal(-1, true)
32 }
3325 return n, err
3426 }
3527
3628 type ewmaProxyReader struct {
37 proxyReader
29 io.ReadCloser
30 bar *Bar
3831 }
3932
4033 func (x ewmaProxyReader) Read(p []byte) (int, error) {
4134 start := time.Now()
42 n, err := x.proxyReader.Read(p)
43 if n > 0 {
44 x.bar.DecoratorEwmaUpdate(time.Since(start))
45 }
35 n, err := x.ReadCloser.Read(p)
36 x.bar.EwmaIncrBy(n, time.Since(start))
4637 return n, err
4738 }
4839
4940 type ewmaProxyWriterTo struct {
5041 ewmaProxyReader
51 wt proxyWriterTo
5242 }
5343
5444 func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
5545 start := time.Now()
56 n, err := x.wt.WriteTo(w)
57 if n > 0 {
58 x.bar.DecoratorEwmaUpdate(time.Since(start))
59 }
46 n, err := x.ReadCloser.(io.WriterTo).WriteTo(w)
47 x.bar.EwmaIncrInt64(n, time.Since(start))
6048 return n, err
6149 }
6250
63 func (b *Bar) newProxyReader(r io.Reader) (rc io.ReadCloser) {
64 pr := proxyReader{toReadCloser(r), b}
65 if wt, ok := r.(io.WriterTo); ok {
66 pw := proxyWriterTo{pr, wt}
67 if b.hasEwmaDecorators {
68 rc = ewmaProxyWriterTo{ewmaProxyReader{pr}, pw}
69 } else {
70 rc = pw
51 func newProxyReader(r io.Reader, b *Bar, hasEwma bool) io.ReadCloser {
52 rc := toReadCloser(r)
53 if hasEwma {
54 epr := ewmaProxyReader{rc, b}
55 if _, ok := r.(io.WriterTo); ok {
56 return ewmaProxyWriterTo{epr}
7157 }
72 } else if b.hasEwmaDecorators {
73 rc = ewmaProxyReader{pr}
74 } else {
75 rc = pr
58 return epr
7659 }
77 return rc
60 pr := proxyReader{rc, b}
61 if _, ok := r.(io.WriterTo); ok {
62 return proxyWriterTo{pr}
63 }
64 return pr
7865 }
7966
8067 func toReadCloser(r io.Reader) io.ReadCloser {
8168 if rc, ok := r.(io.ReadCloser); ok {
8269 return rc
8370 }
84 return ioutil.NopCloser(r)
71 return toNopReadCloser(r)
8572 }
73
74 func toNopReadCloser(r io.Reader) io.ReadCloser {
75 if _, ok := r.(io.WriterTo); ok {
76 return nopReadCloserWriterTo{r}
77 }
78 return nopReadCloser{r}
79 }
80
81 type nopReadCloser struct {
82 io.Reader
83 }
84
85 func (nopReadCloser) Close() error { return nil }
86
87 type nopReadCloserWriterTo struct {
88 io.Reader
89 }
90
91 func (nopReadCloserWriterTo) Close() error { return nil }
92
93 func (c nopReadCloserWriterTo) WriteTo(w io.Writer) (int64, error) {
94 return c.Reader.(io.WriterTo).WriteTo(w)
95 }
22 import (
33 "bytes"
44 "io"
5 "io/ioutil"
65 "strings"
76 "testing"
87
9 "github.com/vbauerster/mpb/v7"
8 "github.com/vbauerster/mpb/v8"
109 )
1110
1211 const content = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
2827 }
2928
3029 func TestProxyReader(t *testing.T) {
31 p := mpb.New(mpb.WithOutput(ioutil.Discard))
30 p := mpb.New(mpb.WithOutput(io.Discard))
3231
33 tReader := &testReader{strings.NewReader(content), false}
32 tr := &testReader{strings.NewReader(content), false}
3433
35 bar := p.AddBar(int64(len(content)), mpb.BarFillerTrim())
34 bar := p.New(int64(len(content)), mpb.NopStyle())
3635
3736 var buf bytes.Buffer
38 _, err := io.Copy(&buf, bar.ProxyReader(tReader))
37 _, err := io.Copy(&buf, bar.ProxyReader(tr))
3938 if err != nil {
40 t.Errorf("Error copying from reader: %+v\n", err)
39 t.Errorf("io.Copy: %s\n", err.Error())
4140 }
4241
4342 p.Wait()
4443
45 if !tReader.called {
44 if !tr.called {
4645 t.Error("Read not called")
4746 }
4847
5150 }
5251 }
5352
54 type testWriterTo struct {
53 type testReadCloser struct {
5554 io.Reader
56 wt io.WriterTo
5755 called bool
5856 }
5957
60 func (wt *testWriterTo) WriteTo(w io.Writer) (n int64, err error) {
61 wt.called = true
62 return wt.wt.WriteTo(w)
58 func (r *testReadCloser) Close() error {
59 r.called = true
60 return nil
6361 }
6462
65 func TestProxyWriterTo(t *testing.T) {
66 p := mpb.New(mpb.WithOutput(ioutil.Discard))
63 func TestProxyReadCloser(t *testing.T) {
64 p := mpb.New(mpb.WithOutput(io.Discard))
6765
68 var reader io.Reader = strings.NewReader(content)
69 tReader := &testWriterTo{reader, reader.(io.WriterTo), false}
66 tr := &testReadCloser{strings.NewReader(content), false}
7067
71 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())
72100
73101 var buf bytes.Buffer
74 _, err := io.Copy(&buf, bar.ProxyReader(tReader))
102 _, err := io.Copy(&buf, bar.ProxyReader(tr))
75103 if err != nil {
76 t.Errorf("Error copying from reader: %+v\n", err)
104 t.Errorf("io.Copy: %s\n", err.Error())
77105 }
78106
79107 p.Wait()
80108
81 if !tReader.called {
109 if !tr.called {
82110 t.Error("WriteTo not called")
83111 }
84112
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 }