summaryrefslogtreecommitdiff
path: root/vendor/github.com/vbauerster/mpb
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2021-03-25 10:50:01 -0700
committerGitHub <noreply@github.com>2021-03-25 10:50:01 -0700
commitdb356748738762c31a036c179d23488d7978dabf (patch)
treea01d257cba3349b47ff743b11eaa7f496bac991b /vendor/github.com/vbauerster/mpb
parent24581d8760691af1657c4f890d42ebd76f5e85c4 (diff)
parent4ab8a6f67eb9de0de40d478cb0cbec05b1b725c0 (diff)
downloadpodman-db356748738762c31a036c179d23488d7978dabf.tar.gz
podman-db356748738762c31a036c179d23488d7978dabf.tar.bz2
podman-db356748738762c31a036c179d23488d7978dabf.zip
Merge pull request #9781 from baude/addqemu
introduce podman machine
Diffstat (limited to 'vendor/github.com/vbauerster/mpb')
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/.gitignore5
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/.travis.yml11
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/README.md120
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/UNLICENSE24
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/bar.go492
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/bar_filler.go31
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/bar_filler_bar.go191
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/bar_filler_spinner.go65
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/bar_option.go153
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/container_option.go112
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/cwriter/doc.go2
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/cwriter/util_bsd.go7
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/cwriter/util_linux.go7
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/cwriter/util_solaris.go7
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/cwriter/writer.go84
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/cwriter/writer_posix.go26
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/cwriter/writer_windows.go73
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/any.go21
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/counters.go243
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/decorator.go191
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/doc.go20
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/elapsed.go35
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/eta.go203
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/merge.go107
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/moving_average.go68
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/name.go12
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/on_complete.go37
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/percentage.go58
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/size_type.go109
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/sizeb1000_string.go41
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/sizeb1024_string.go41
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/speed.go171
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/decor/spinner.go21
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/doc.go2
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/go.mod11
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/go.sum11
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/internal/percentage.go19
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/internal/predicate.go6
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/internal/width.go10
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/priority_queue.go32
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/progress.go412
-rw-r--r--vendor/github.com/vbauerster/mpb/v6/proxyreader.go90
42 files changed, 3381 insertions, 0 deletions
diff --git a/vendor/github.com/vbauerster/mpb/v6/.gitignore b/vendor/github.com/vbauerster/mpb/v6/.gitignore
new file mode 100644
index 000000000..63bd91672
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/.gitignore
@@ -0,0 +1,5 @@
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
diff --git a/vendor/github.com/vbauerster/mpb/v6/.travis.yml b/vendor/github.com/vbauerster/mpb/v6/.travis.yml
new file mode 100644
index 000000000..9a203a67d
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/.travis.yml
@@ -0,0 +1,11 @@
+language: go
+arch:
+ - amd64
+ - ppc64le
+
+go:
+ - 1.14.x
+
+script:
+ - go test -race ./...
+ - for i in _examples/*/; do go build $i/*.go || exit 1; done
diff --git a/vendor/github.com/vbauerster/mpb/v6/README.md b/vendor/github.com/vbauerster/mpb/v6/README.md
new file mode 100644
index 000000000..a87786d72
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/README.md
@@ -0,0 +1,120 @@
+# Multi Progress Bar
+
+[![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v6)
+[![Build Status](https://travis-ci.org/vbauerster/mpb.svg?branch=master)](https://travis-ci.org/vbauerster/mpb)
+[![Go Report Card](https://goreportcard.com/badge/github.com/vbauerster/mpb)](https://goreportcard.com/report/github.com/vbauerster/mpb)
+
+**mpb** is a Go lib for rendering progress bars in terminal applications.
+
+## Features
+
+- **Multiple Bars**: Multiple progress bars are supported
+- **Dynamic Total**: Set total while bar is running
+- **Dynamic Add/Remove**: Dynamically add or remove bars
+- **Cancellation**: Cancel whole rendering process
+- **Predefined Decorators**: Elapsed time, [ewma](https://github.com/VividCortex/ewma) based ETA, Percentage, Bytes counter
+- **Decorator's width sync**: Synchronized decorator's width among multiple bars
+
+## Usage
+
+#### [Rendering single bar](_examples/singleBar/main.go)
+
+```go
+package main
+
+import (
+ "math/rand"
+ "time"
+
+ "github.com/vbauerster/mpb/v6"
+ "github.com/vbauerster/mpb/v6/decor"
+)
+
+func main() {
+ // initialize progress container, with custom width
+ p := mpb.New(mpb.WithWidth(64))
+
+ total := 100
+ name := "Single Bar:"
+ // adding a single bar, which will inherit container's width
+ bar := p.Add(int64(total),
+ // progress bar filler with customized style
+ mpb.NewBarFiller("╢▌▌░╟"),
+ mpb.PrependDecorators(
+ // display our name with one space on the right
+ decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
+ // replace ETA decorator with "done" message, OnComplete event
+ decor.OnComplete(
+ decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "done",
+ ),
+ ),
+ mpb.AppendDecorators(decor.Percentage()),
+ )
+ // simulating some work
+ max := 100 * time.Millisecond
+ for i := 0; i < total; i++ {
+ time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
+ bar.Increment()
+ }
+ // wait for our bar to complete and flush
+ p.Wait()
+}
+```
+
+#### [Rendering multiple bars](_examples/multiBars/main.go)
+
+```go
+ var wg sync.WaitGroup
+ // pass &wg (optional), so p will wait for it eventually
+ p := mpb.New(mpb.WithWaitGroup(&wg))
+ total, numBars := 100, 3
+ wg.Add(numBars)
+
+ for i := 0; i < numBars; i++ {
+ name := fmt.Sprintf("Bar#%d:", i)
+ bar := p.AddBar(int64(total),
+ mpb.PrependDecorators(
+ // simple name decorator
+ decor.Name(name),
+ // decor.DSyncWidth bit enables column width synchronization
+ decor.Percentage(decor.WCSyncSpace),
+ ),
+ mpb.AppendDecorators(
+ // replace ETA decorator with "done" message, OnComplete event
+ decor.OnComplete(
+ // ETA decorator with ewma age of 60
+ decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
+ ),
+ ),
+ )
+ // simulating some work
+ go func() {
+ defer wg.Done()
+ rng := rand.New(rand.NewSource(time.Now().UnixNano()))
+ max := 100 * time.Millisecond
+ for i := 0; i < total; i++ {
+ // start variable is solely for EWMA calculation
+ // EWMA's unit of measure is an iteration's duration
+ start := time.Now()
+ time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
+ bar.Increment()
+ // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
+ bar.DecoratorEwmaUpdate(time.Since(start))
+ }
+ }()
+ }
+ // Waiting for passed &wg and for all bars to complete and flush
+ p.Wait()
+```
+
+#### [Dynamic total](_examples/dynTotal/main.go)
+
+![dynamic total](_svg/godEMrCZmJkHYH1X9dN4Nm0U7.svg)
+
+#### [Complex example](_examples/complex/main.go)
+
+![complex](_svg/wHzf1M7sd7B3zVa2scBMnjqRf.svg)
+
+#### [Bytes counters](_examples/io/main.go)
+
+![byte counters](_svg/hIpTa3A5rQz65ssiVuRJu87X6.svg)
diff --git a/vendor/github.com/vbauerster/mpb/v6/UNLICENSE b/vendor/github.com/vbauerster/mpb/v6/UNLICENSE
new file mode 100644
index 000000000..68a49daad
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/UNLICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
diff --git a/vendor/github.com/vbauerster/mpb/v6/bar.go b/vendor/github.com/vbauerster/mpb/v6/bar.go
new file mode 100644
index 000000000..f18ef967f
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/bar.go
@@ -0,0 +1,492 @@
+package mpb
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "runtime/debug"
+ "strings"
+ "time"
+
+ "github.com/acarl005/stripansi"
+ "github.com/mattn/go-runewidth"
+ "github.com/vbauerster/mpb/v6/decor"
+)
+
+// Bar represents a progress bar.
+type Bar struct {
+ priority int // used by heap
+ index int // used by heap
+
+ extendedLines int
+ toShutdown bool
+ toDrop bool
+ noPop bool
+ hasEwmaDecorators bool
+ operateState chan func(*bState)
+ frameCh chan io.Reader
+ syncTableCh chan [][]chan int
+ completed chan bool
+
+ // cancel is called either by user or on complete event
+ cancel func()
+ // done is closed after cacheState is assigned
+ done chan struct{}
+ // cacheState is populated, right after close(shutdown)
+ cacheState *bState
+
+ container *Progress
+ dlogger *log.Logger
+ recoveredPanic interface{}
+}
+
+type extenderFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int)
+
+// bState is actual bar state. It gets passed to *Bar.serve(...) monitor
+// goroutine.
+type bState struct {
+ id int
+ priority int
+ reqWidth int
+ total int64
+ current int64
+ refill int64
+ lastN int64
+ iterated bool
+ trimSpace bool
+ completed bool
+ completeFlushed bool
+ triggerComplete bool
+ dropOnComplete bool
+ noPop bool
+ aDecorators []decor.Decorator
+ pDecorators []decor.Decorator
+ averageDecorators []decor.AverageDecorator
+ ewmaDecorators []decor.EwmaDecorator
+ shutdownListeners []decor.ShutdownListener
+ bufP, bufB, bufA *bytes.Buffer
+ filler BarFiller
+ middleware func(BarFiller) BarFiller
+ extender extenderFunc
+
+ // runningBar is a key for *pState.parkedBars
+ runningBar *Bar
+
+ debugOut io.Writer
+}
+
+func newBar(container *Progress, bs *bState) *Bar {
+ logPrefix := fmt.Sprintf("%sbar#%02d ", container.dlogger.Prefix(), bs.id)
+ ctx, cancel := context.WithCancel(container.ctx)
+
+ bar := &Bar{
+ container: container,
+ priority: bs.priority,
+ toDrop: bs.dropOnComplete,
+ noPop: bs.noPop,
+ operateState: make(chan func(*bState)),
+ frameCh: make(chan io.Reader, 1),
+ syncTableCh: make(chan [][]chan int, 1),
+ completed: make(chan bool, 1),
+ done: make(chan struct{}),
+ cancel: cancel,
+ dlogger: log.New(bs.debugOut, logPrefix, log.Lshortfile),
+ }
+
+ go bar.serve(ctx, bs)
+ return bar
+}
+
+// ProxyReader wraps r with metrics required for progress tracking.
+// Panics if r is nil.
+func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
+ if r == nil {
+ panic("expected non nil io.Reader")
+ }
+ return newProxyReader(r, b)
+}
+
+// ID returs id of the bar.
+func (b *Bar) ID() int {
+ result := make(chan int)
+ select {
+ case b.operateState <- func(s *bState) { result <- s.id }:
+ return <-result
+ case <-b.done:
+ return b.cacheState.id
+ }
+}
+
+// Current returns bar's current number, in other words sum of all increments.
+func (b *Bar) Current() int64 {
+ result := make(chan int64)
+ select {
+ case b.operateState <- func(s *bState) { result <- s.current }:
+ return <-result
+ case <-b.done:
+ return b.cacheState.current
+ }
+}
+
+// SetRefill sets refill flag with specified amount.
+// The underlying BarFiller will change its visual representation, to
+// indicate refill event. Refill event may be referred to some retry
+// operation for example.
+func (b *Bar) SetRefill(amount int64) {
+ select {
+ case b.operateState <- func(s *bState) {
+ s.refill = amount
+ }:
+ case <-b.done:
+ }
+}
+
+// TraverseDecorators traverses all available decorators and calls cb func on each.
+func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
+ select {
+ case b.operateState <- func(s *bState) {
+ for _, decorators := range [...][]decor.Decorator{
+ s.pDecorators,
+ s.aDecorators,
+ } {
+ for _, d := range decorators {
+ cb(extractBaseDecorator(d))
+ }
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// SetTotal sets total dynamically.
+// If total is less than or equal to zero it takes progress' current value.
+func (b *Bar) SetTotal(total int64, triggerComplete bool) {
+ select {
+ case b.operateState <- func(s *bState) {
+ s.triggerComplete = triggerComplete
+ if total <= 0 {
+ s.total = s.current
+ } else {
+ s.total = total
+ }
+ if s.triggerComplete && !s.completed {
+ s.current = s.total
+ s.completed = true
+ go b.refreshTillShutdown()
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// SetCurrent sets progress' current to an arbitrary value.
+// Setting a negative value will cause a panic.
+func (b *Bar) SetCurrent(current int64) {
+ select {
+ case b.operateState <- func(s *bState) {
+ s.iterated = true
+ s.lastN = current - s.current
+ s.current = current
+ if s.triggerComplete && s.current >= s.total {
+ s.current = s.total
+ s.completed = true
+ go b.refreshTillShutdown()
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// Increment is a shorthand for b.IncrInt64(1).
+func (b *Bar) Increment() {
+ b.IncrInt64(1)
+}
+
+// IncrBy is a shorthand for b.IncrInt64(int64(n)).
+func (b *Bar) IncrBy(n int) {
+ b.IncrInt64(int64(n))
+}
+
+// IncrInt64 increments progress by amount of n.
+func (b *Bar) IncrInt64(n int64) {
+ select {
+ case b.operateState <- func(s *bState) {
+ s.iterated = true
+ s.lastN = n
+ s.current += n
+ if s.triggerComplete && s.current >= s.total {
+ s.current = s.total
+ s.completed = true
+ go b.refreshTillShutdown()
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// DecoratorEwmaUpdate updates all EWMA based decorators. Should be
+// called on each iteration, because EWMA's unit of measure is an
+// iteration's duration. Panics if called before *Bar.Incr... family
+// methods.
+func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
+ select {
+ case b.operateState <- func(s *bState) {
+ ewmaIterationUpdate(false, s, dur)
+ }:
+ case <-b.done:
+ ewmaIterationUpdate(true, b.cacheState, dur)
+ }
+}
+
+// DecoratorAverageAdjust adjusts all average based decorators. Call
+// if you need to adjust start time of all average based decorators
+// or after progress resume.
+func (b *Bar) DecoratorAverageAdjust(start time.Time) {
+ select {
+ case b.operateState <- func(s *bState) {
+ for _, d := range s.averageDecorators {
+ d.AverageAdjust(start)
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// SetPriority changes bar's order among multiple bars. Zero is highest
+// priority, i.e. bar will be on top. If you don't need to set priority
+// dynamically, better use BarPriority option.
+func (b *Bar) SetPriority(priority int) {
+ select {
+ case <-b.done:
+ default:
+ b.container.setBarPriority(b, priority)
+ }
+}
+
+// Abort interrupts bar's running goroutine. Call this, if you'd like
+// to stop/remove bar before completion event. It has no effect after
+// completion event. If drop is true bar will be removed as well.
+func (b *Bar) Abort(drop bool) {
+ select {
+ case <-b.done:
+ default:
+ if drop {
+ b.container.dropBar(b)
+ }
+ b.cancel()
+ }
+}
+
+// Completed reports whether the bar is in completed state.
+func (b *Bar) Completed() bool {
+ select {
+ case b.operateState <- func(s *bState) { b.completed <- s.completed }:
+ return <-b.completed
+ case <-b.done:
+ return true
+ }
+}
+
+func (b *Bar) serve(ctx context.Context, s *bState) {
+ defer b.container.bwg.Done()
+ for {
+ select {
+ case op := <-b.operateState:
+ op(s)
+ case <-ctx.Done():
+ b.cacheState = s
+ close(b.done)
+ // Notifying decorators about shutdown event
+ for _, sl := range s.shutdownListeners {
+ sl.Shutdown()
+ }
+ return
+ }
+ }
+}
+
+func (b *Bar) render(tw int) {
+ select {
+ case b.operateState <- func(s *bState) {
+ stat := newStatistics(tw, s)
+ defer func() {
+ // recovering if user defined decorator panics for example
+ if p := recover(); p != nil {
+ if b.recoveredPanic == nil {
+ s.extender = makePanicExtender(p)
+ b.toShutdown = !b.toShutdown
+ b.recoveredPanic = p
+ }
+ frame, lines := s.extender(nil, s.reqWidth, stat)
+ b.extendedLines = lines
+ b.frameCh <- frame
+ b.dlogger.Println(p)
+ }
+ s.completeFlushed = s.completed
+ }()
+ frame, lines := s.extender(s.draw(stat), s.reqWidth, stat)
+ b.extendedLines = lines
+ b.toShutdown = s.completed && !s.completeFlushed
+ b.frameCh <- frame
+ }:
+ case <-b.done:
+ s := b.cacheState
+ stat := newStatistics(tw, s)
+ var r io.Reader
+ if b.recoveredPanic == nil {
+ r = s.draw(stat)
+ }
+ frame, lines := s.extender(r, s.reqWidth, stat)
+ b.extendedLines = lines
+ b.frameCh <- frame
+ }
+}
+
+func (b *Bar) subscribeDecorators() {
+ var averageDecorators []decor.AverageDecorator
+ var ewmaDecorators []decor.EwmaDecorator
+ var shutdownListeners []decor.ShutdownListener
+ b.TraverseDecorators(func(d decor.Decorator) {
+ if d, ok := d.(decor.AverageDecorator); ok {
+ averageDecorators = append(averageDecorators, d)
+ }
+ if d, ok := d.(decor.EwmaDecorator); ok {
+ ewmaDecorators = append(ewmaDecorators, d)
+ }
+ if d, ok := d.(decor.ShutdownListener); ok {
+ shutdownListeners = append(shutdownListeners, d)
+ }
+ })
+ select {
+ case b.operateState <- func(s *bState) {
+ s.averageDecorators = averageDecorators
+ s.ewmaDecorators = ewmaDecorators
+ s.shutdownListeners = shutdownListeners
+ }:
+ b.hasEwmaDecorators = len(ewmaDecorators) != 0
+ case <-b.done:
+ }
+}
+
+func (b *Bar) refreshTillShutdown() {
+ for {
+ select {
+ case b.container.refreshCh <- time.Now():
+ case <-b.done:
+ return
+ }
+ }
+}
+
+func (b *Bar) wSyncTable() [][]chan int {
+ select {
+ case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
+ return <-b.syncTableCh
+ case <-b.done:
+ return b.cacheState.wSyncTable()
+ }
+}
+
+func (s *bState) draw(stat decor.Statistics) io.Reader {
+ if !s.trimSpace {
+ stat.AvailableWidth -= 2
+ s.bufB.WriteByte(' ')
+ defer s.bufB.WriteByte(' ')
+ }
+
+ nlr := strings.NewReader("\n")
+ tw := stat.AvailableWidth
+ for _, d := range s.pDecorators {
+ str := d.Decor(stat)
+ stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
+ s.bufP.WriteString(str)
+ }
+ if stat.AvailableWidth <= 0 {
+ trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufP.String()), tw, "…"))
+ s.bufP.Reset()
+ return io.MultiReader(trunc, s.bufB, nlr)
+ }
+
+ tw = stat.AvailableWidth
+ for _, d := range s.aDecorators {
+ str := d.Decor(stat)
+ stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
+ s.bufA.WriteString(str)
+ }
+ if stat.AvailableWidth <= 0 {
+ trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufA.String()), tw, "…"))
+ s.bufA.Reset()
+ return io.MultiReader(s.bufP, s.bufB, trunc, nlr)
+ }
+
+ s.filler.Fill(s.bufB, s.reqWidth, stat)
+
+ return io.MultiReader(s.bufP, s.bufB, s.bufA, nlr)
+}
+
+func (s *bState) wSyncTable() [][]chan int {
+ columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
+ var pCount int
+ for _, d := range s.pDecorators {
+ if ch, ok := d.Sync(); ok {
+ columns = append(columns, ch)
+ pCount++
+ }
+ }
+ var aCount int
+ for _, d := range s.aDecorators {
+ if ch, ok := d.Sync(); ok {
+ columns = append(columns, ch)
+ aCount++
+ }
+ }
+ table := make([][]chan int, 2)
+ table[0] = columns[0:pCount]
+ table[1] = columns[pCount : pCount+aCount : pCount+aCount]
+ return table
+}
+
+func newStatistics(tw int, s *bState) decor.Statistics {
+ return decor.Statistics{
+ ID: s.id,
+ AvailableWidth: tw,
+ Total: s.total,
+ Current: s.current,
+ Refill: s.refill,
+ Completed: s.completeFlushed,
+ }
+}
+
+func extractBaseDecorator(d decor.Decorator) decor.Decorator {
+ if d, ok := d.(decor.Wrapper); ok {
+ return extractBaseDecorator(d.Base())
+ }
+ return d
+}
+
+func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) {
+ if !done && !s.iterated {
+ panic("increment required before ewma iteration update")
+ } else {
+ s.iterated = false
+ }
+ for _, d := range s.ewmaDecorators {
+ d.EwmaUpdate(s.lastN, dur)
+ }
+}
+
+func makePanicExtender(p interface{}) extenderFunc {
+ pstr := fmt.Sprint(p)
+ stack := debug.Stack()
+ stackLines := bytes.Count(stack, []byte("\n"))
+ return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) {
+ mr := io.MultiReader(
+ strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")),
+ strings.NewReader(fmt.Sprintf("\n%#v\n", st)),
+ bytes.NewReader(stack),
+ )
+ return mr, stackLines + 1
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/bar_filler.go b/vendor/github.com/vbauerster/mpb/v6/bar_filler.go
new file mode 100644
index 000000000..c8cedaa4a
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/bar_filler.go
@@ -0,0 +1,31 @@
+package mpb
+
+import (
+ "io"
+
+ "github.com/vbauerster/mpb/v6/decor"
+)
+
+// BarFiller interface.
+// Bar (without decorators) renders itself by calling BarFiller's Fill method.
+//
+// reqWidth is requested width, set by `func WithWidth(int) ContainerOption`.
+// If not set, it defaults to terminal width.
+//
+// Default implementations can be obtained via:
+//
+// func NewBarFiller(style string) BarFiller
+// func NewBarFillerRev(style string) BarFiller
+// func NewBarFillerPick(style string, rev bool) BarFiller
+// func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller
+//
+type BarFiller interface {
+ Fill(w io.Writer, reqWidth int, stat decor.Statistics)
+}
+
+// BarFillerFunc is function type adapter to convert function into BarFiller.
+type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
+
+func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
+ f(w, reqWidth, stat)
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/bar_filler_bar.go b/vendor/github.com/vbauerster/mpb/v6/bar_filler_bar.go
new file mode 100644
index 000000000..50494485e
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/bar_filler_bar.go
@@ -0,0 +1,191 @@
+package mpb
+
+import (
+ "bytes"
+ "io"
+ "unicode/utf8"
+
+ "github.com/mattn/go-runewidth"
+ "github.com/rivo/uniseg"
+ "github.com/vbauerster/mpb/v6/decor"
+ "github.com/vbauerster/mpb/v6/internal"
+)
+
+const (
+ rLeft = iota
+ rFill
+ rTip
+ rSpace
+ rRight
+ rRevTip
+ rRefill
+)
+
+// BarDefaultStyle is a style for rendering a progress bar.
+// It consist of 7 ordered runes:
+//
+// '1st rune' stands for left boundary rune
+//
+// '2nd rune' stands for fill rune
+//
+// '3rd rune' stands for tip rune
+//
+// '4th rune' stands for space rune
+//
+// '5th rune' stands for right boundary rune
+//
+// '6th rune' stands for reverse tip rune
+//
+// '7th rune' stands for refill rune
+//
+const BarDefaultStyle string = "[=>-]<+"
+
+type barFiller struct {
+ format [][]byte
+ rwidth []int
+ tip []byte
+ refill int64
+ reverse bool
+ flush func(io.Writer, *space, [][]byte)
+}
+
+type space struct {
+ space []byte
+ rwidth int
+ count int
+}
+
+// NewBarFiller returns a BarFiller implementation which renders a
+// progress bar in regular direction. If style is empty string,
+// BarDefaultStyle is applied. To be used with `*Progress.Add(...)
+// *Bar` method.
+func NewBarFiller(style string) BarFiller {
+ return newBarFiller(style, false)
+}
+
+// NewBarFillerRev returns a BarFiller implementation which renders a
+// progress bar in reverse direction. If style is empty string,
+// BarDefaultStyle is applied. To be used with `*Progress.Add(...)
+// *Bar` method.
+func NewBarFillerRev(style string) BarFiller {
+ return newBarFiller(style, true)
+}
+
+// NewBarFillerPick pick between regular and reverse BarFiller implementation
+// based on rev param. To be used with `*Progress.Add(...) *Bar` method.
+func NewBarFillerPick(style string, rev bool) BarFiller {
+ return newBarFiller(style, rev)
+}
+
+func newBarFiller(style string, rev bool) BarFiller {
+ bf := &barFiller{
+ format: make([][]byte, len(BarDefaultStyle)),
+ rwidth: make([]int, len(BarDefaultStyle)),
+ reverse: rev,
+ }
+ bf.parse(BarDefaultStyle)
+ if style != "" && style != BarDefaultStyle {
+ bf.parse(style)
+ }
+ return bf
+}
+
+func (s *barFiller) parse(style string) {
+ if !utf8.ValidString(style) {
+ panic("invalid bar style")
+ }
+ srcFormat := make([][]byte, 0, len(BarDefaultStyle))
+ srcRwidth := make([]int, 0, len(BarDefaultStyle))
+ gr := uniseg.NewGraphemes(style)
+ for gr.Next() {
+ srcFormat = append(srcFormat, gr.Bytes())
+ srcRwidth = append(srcRwidth, runewidth.StringWidth(gr.Str()))
+ }
+ copy(s.format, srcFormat)
+ copy(s.rwidth, srcRwidth)
+ if s.reverse {
+ s.tip = s.format[rRevTip]
+ s.flush = reverseFlush
+ } else {
+ s.tip = s.format[rTip]
+ s.flush = regularFlush
+ }
+}
+
+func (s *barFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
+ width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth)
+ brackets := s.rwidth[rLeft] + s.rwidth[rRight]
+ if width < brackets {
+ return
+ }
+ // don't count brackets as progress
+ width -= brackets
+
+ w.Write(s.format[rLeft])
+ defer w.Write(s.format[rRight])
+
+ cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width))
+ space := &space{
+ space: s.format[rSpace],
+ rwidth: s.rwidth[rSpace],
+ count: width - cwidth,
+ }
+
+ index, refill := 0, 0
+ bb := make([][]byte, cwidth)
+
+ if cwidth > 0 && cwidth != width {
+ bb[index] = s.tip
+ cwidth -= s.rwidth[rTip]
+ index++
+ }
+
+ if stat.Refill > 0 {
+ refill = int(internal.PercentageRound(stat.Total, int64(stat.Refill), width))
+ if refill > cwidth {
+ refill = cwidth
+ }
+ cwidth -= refill
+ }
+
+ for cwidth > 0 {
+ bb[index] = s.format[rFill]
+ cwidth -= s.rwidth[rFill]
+ index++
+ }
+
+ for refill > 0 {
+ bb[index] = s.format[rRefill]
+ refill -= s.rwidth[rRefill]
+ index++
+ }
+
+ if cwidth+refill < 0 || space.rwidth > 1 {
+ buf := new(bytes.Buffer)
+ s.flush(buf, space, bb[:index])
+ io.WriteString(w, runewidth.Truncate(buf.String(), width, "…"))
+ return
+ }
+
+ s.flush(w, space, bb)
+}
+
+func regularFlush(w io.Writer, space *space, bb [][]byte) {
+ for i := len(bb) - 1; i >= 0; i-- {
+ w.Write(bb[i])
+ }
+ for space.count > 0 {
+ w.Write(space.space)
+ space.count -= space.rwidth
+ }
+}
+
+func reverseFlush(w io.Writer, space *space, bb [][]byte) {
+ for space.count > 0 {
+ w.Write(space.space)
+ space.count -= space.rwidth
+ }
+ for i := 0; i < len(bb); i++ {
+ w.Write(bb[i])
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/bar_filler_spinner.go b/vendor/github.com/vbauerster/mpb/v6/bar_filler_spinner.go
new file mode 100644
index 000000000..0817b19ec
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/bar_filler_spinner.go
@@ -0,0 +1,65 @@
+package mpb
+
+import (
+ "io"
+ "strings"
+
+ "github.com/mattn/go-runewidth"
+ "github.com/vbauerster/mpb/v6/decor"
+ "github.com/vbauerster/mpb/v6/internal"
+)
+
+// SpinnerAlignment enum.
+type SpinnerAlignment int
+
+// SpinnerAlignment kinds.
+const (
+ SpinnerOnLeft SpinnerAlignment = iota
+ SpinnerOnMiddle
+ SpinnerOnRight
+)
+
+// SpinnerDefaultStyle is a style for rendering a spinner.
+var SpinnerDefaultStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
+
+type spinnerFiller struct {
+ frames []string
+ count uint
+ alignment SpinnerAlignment
+}
+
+// NewSpinnerFiller returns a BarFiller implementation which renders
+// a spinner. If style is nil or zero length, SpinnerDefaultStyle is
+// applied. To be used with `*Progress.Add(...) *Bar` method.
+func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller {
+ if len(style) == 0 {
+ style = SpinnerDefaultStyle
+ }
+ filler := &spinnerFiller{
+ frames: style,
+ alignment: alignment,
+ }
+ return filler
+}
+
+func (s *spinnerFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
+ width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth)
+
+ frame := s.frames[s.count%uint(len(s.frames))]
+ frameWidth := runewidth.StringWidth(frame)
+
+ if width < frameWidth {
+ return
+ }
+
+ switch rest := width - frameWidth; s.alignment {
+ case SpinnerOnLeft:
+ io.WriteString(w, frame+strings.Repeat(" ", rest))
+ case SpinnerOnMiddle:
+ str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
+ io.WriteString(w, str)
+ case SpinnerOnRight:
+ io.WriteString(w, strings.Repeat(" ", rest)+frame)
+ }
+ s.count++
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/bar_option.go b/vendor/github.com/vbauerster/mpb/v6/bar_option.go
new file mode 100644
index 000000000..e359c110c
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/bar_option.go
@@ -0,0 +1,153 @@
+package mpb
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/vbauerster/mpb/v6/decor"
+ "github.com/vbauerster/mpb/v6/internal"
+)
+
+// BarOption is a func option to alter default behavior of a bar.
+type BarOption func(*bState)
+
+func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
+ type mergeWrapper interface {
+ MergeUnwrap() []decor.Decorator
+ }
+ for _, decorator := range decorators {
+ if mw, ok := decorator.(mergeWrapper); ok {
+ *dest = append(*dest, mw.MergeUnwrap()...)
+ }
+ *dest = append(*dest, decorator)
+ }
+}
+
+// AppendDecorators let you inject decorators to the bar's right side.
+func AppendDecorators(decorators ...decor.Decorator) BarOption {
+ return func(s *bState) {
+ s.addDecorators(&s.aDecorators, decorators...)
+ }
+}
+
+// PrependDecorators let you inject decorators to the bar's left side.
+func PrependDecorators(decorators ...decor.Decorator) BarOption {
+ return func(s *bState) {
+ s.addDecorators(&s.pDecorators, decorators...)
+ }
+}
+
+// BarID sets bar id.
+func BarID(id int) BarOption {
+ return func(s *bState) {
+ s.id = id
+ }
+}
+
+// BarWidth sets bar width independent of the container.
+func BarWidth(width int) BarOption {
+ return func(s *bState) {
+ s.reqWidth = width
+ }
+}
+
+// BarQueueAfter queues this (being constructed) bar to relplace
+// runningBar after it has been completed.
+func BarQueueAfter(runningBar *Bar) BarOption {
+ if runningBar == nil {
+ return nil
+ }
+ return func(s *bState) {
+ s.runningBar = runningBar
+ }
+}
+
+// BarRemoveOnComplete removes both bar's filler and its decorators
+// on complete event.
+func BarRemoveOnComplete() BarOption {
+ return func(s *bState) {
+ s.dropOnComplete = true
+ }
+}
+
+// BarFillerClearOnComplete clears bar's filler on complete event.
+// It's shortcut for BarFillerOnComplete("").
+func BarFillerClearOnComplete() BarOption {
+ return BarFillerOnComplete("")
+}
+
+// BarFillerOnComplete replaces bar's filler with message, on complete event.
+func BarFillerOnComplete(message string) BarOption {
+ return BarFillerMiddleware(func(base BarFiller) BarFiller {
+ return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
+ if st.Completed {
+ io.WriteString(w, message)
+ } else {
+ base.Fill(w, reqWidth, st)
+ }
+ })
+ })
+}
+
+// BarFillerMiddleware provides a way to augment the underlying BarFiller.
+func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption {
+ return func(s *bState) {
+ s.middleware = middle
+ }
+}
+
+// BarPriority sets bar's priority. Zero is highest priority, i.e. bar
+// will be on top. If `BarReplaceOnComplete` option is supplied, this
+// option is ignored.
+func BarPriority(priority int) BarOption {
+ return func(s *bState) {
+ s.priority = priority
+ }
+}
+
+// BarExtender provides a way to extend bar to the next new line.
+func BarExtender(filler BarFiller) BarOption {
+ if filler == nil {
+ return nil
+ }
+ return func(s *bState) {
+ s.extender = makeExtenderFunc(filler)
+ }
+}
+
+func makeExtenderFunc(filler BarFiller) extenderFunc {
+ buf := new(bytes.Buffer)
+ return func(r io.Reader, reqWidth int, st decor.Statistics) (io.Reader, int) {
+ filler.Fill(buf, reqWidth, st)
+ return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), []byte("\n"))
+ }
+}
+
+// BarFillerTrim removes leading and trailing space around the underlying BarFiller.
+func BarFillerTrim() BarOption {
+ return func(s *bState) {
+ s.trimSpace = true
+ }
+}
+
+// BarNoPop disables bar pop out of container. Effective when
+// PopCompletedMode of container is enabled.
+func BarNoPop() BarOption {
+ return func(s *bState) {
+ s.noPop = true
+ }
+}
+
+// BarOptional will invoke provided option only when pick is true.
+func BarOptional(option BarOption, pick bool) BarOption {
+ return BarOptOn(option, internal.Predicate(pick))
+}
+
+// BarOptOn will invoke provided option only when higher order predicate
+// evaluates to true.
+func BarOptOn(option BarOption, predicate func() bool) BarOption {
+ if predicate() {
+ return option
+ }
+ return nil
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/container_option.go b/vendor/github.com/vbauerster/mpb/v6/container_option.go
new file mode 100644
index 000000000..b92c7578c
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/container_option.go
@@ -0,0 +1,112 @@
+package mpb
+
+import (
+ "io"
+ "io/ioutil"
+ "sync"
+ "time"
+
+ "github.com/vbauerster/mpb/v6/internal"
+)
+
+// ContainerOption is a func option to alter default behavior of a bar
+// container. Container term refers to a Progress struct which can
+// hold one or more Bars.
+type ContainerOption func(*pState)
+
+// WithWaitGroup provides means to have a single joint point. If
+// *sync.WaitGroup is provided, you can safely call just p.Wait()
+// without calling Wait() on provided *sync.WaitGroup. Makes sense
+// when there are more than one bar to render.
+func WithWaitGroup(wg *sync.WaitGroup) ContainerOption {
+ return func(s *pState) {
+ s.uwg = wg
+ }
+}
+
+// WithWidth sets container width. If not set it defaults to terminal
+// width. A bar added to the container will inherit its width, unless
+// overridden by `func BarWidth(int) BarOption`.
+func WithWidth(width int) ContainerOption {
+ return func(s *pState) {
+ s.reqWidth = width
+ }
+}
+
+// WithRefreshRate overrides default 120ms refresh rate.
+func WithRefreshRate(d time.Duration) ContainerOption {
+ return func(s *pState) {
+ s.rr = d
+ }
+}
+
+// WithManualRefresh disables internal auto refresh time.Ticker.
+// Refresh will occur upon receive value from provided ch.
+func WithManualRefresh(ch <-chan interface{}) ContainerOption {
+ return func(s *pState) {
+ s.externalRefresh = ch
+ }
+}
+
+// WithRenderDelay delays rendering. By default rendering starts as
+// soon as bar is added, with this option it's possible to delay
+// rendering process by keeping provided chan unclosed. In other words
+// rendering will start as soon as provided chan is closed.
+func WithRenderDelay(ch <-chan struct{}) ContainerOption {
+ return func(s *pState) {
+ s.renderDelay = ch
+ }
+}
+
+// WithShutdownNotifier provided chanel will be closed, after all bars
+// have been rendered.
+func WithShutdownNotifier(ch chan struct{}) ContainerOption {
+ return func(s *pState) {
+ s.shutdownNotifier = ch
+ }
+}
+
+// WithOutput overrides default os.Stdout output. Setting it to nil
+// will effectively disable auto refresh rate and discard any output,
+// useful if you want to disable progress bars with little overhead.
+func WithOutput(w io.Writer) ContainerOption {
+ return func(s *pState) {
+ if w == nil {
+ s.output = ioutil.Discard
+ s.outputDiscarded = true
+ return
+ }
+ s.output = w
+ }
+}
+
+// WithDebugOutput sets debug output.
+func WithDebugOutput(w io.Writer) ContainerOption {
+ if w == nil {
+ return nil
+ }
+ return func(s *pState) {
+ s.debugOut = w
+ }
+}
+
+// PopCompletedMode will pop and stop rendering completed bars.
+func PopCompletedMode() ContainerOption {
+ return func(s *pState) {
+ s.popCompleted = true
+ }
+}
+
+// ContainerOptional will invoke provided option only when pick is true.
+func ContainerOptional(option ContainerOption, pick bool) ContainerOption {
+ return ContainerOptOn(option, internal.Predicate(pick))
+}
+
+// ContainerOptOn will invoke provided option only when higher order
+// predicate evaluates to true.
+func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption {
+ if predicate() {
+ return option
+ }
+ return nil
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/doc.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/doc.go
new file mode 100644
index 000000000..93c8f8268
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/cwriter/doc.go
@@ -0,0 +1,2 @@
+// Package cwriter is a console writer abstraction for the underlying OS.
+package cwriter
diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_bsd.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_bsd.go
new file mode 100644
index 000000000..4e3564ece
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_bsd.go
@@ -0,0 +1,7 @@
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package cwriter
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TIOCGETA
diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_linux.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_linux.go
new file mode 100644
index 000000000..253f12dd2
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_linux.go
@@ -0,0 +1,7 @@
+// +build aix linux
+
+package cwriter
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/util_solaris.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_solaris.go
new file mode 100644
index 000000000..4b29ff5c0
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/cwriter/util_solaris.go
@@ -0,0 +1,7 @@
+// +build solaris
+
+package cwriter
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETA
diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer.go
new file mode 100644
index 000000000..1ade54761
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer.go
@@ -0,0 +1,84 @@
+package cwriter
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "os"
+ "strconv"
+)
+
+// ErrNotTTY not a TeleTYpewriter error.
+var ErrNotTTY = errors.New("not a terminal")
+
+// http://ascii-table.com/ansi-escape-sequences.php
+const (
+ escOpen = "\x1b["
+ cuuAndEd = "A\x1b[J"
+)
+
+// Writer is a buffered the writer that updates the terminal. The
+// contents of writer will be flushed when Flush is called.
+type Writer struct {
+ out io.Writer
+ buf bytes.Buffer
+ lineCount int
+ fd int
+ isTerminal bool
+}
+
+// New returns a new Writer with defaults.
+func New(out io.Writer) *Writer {
+ w := &Writer{out: out}
+ if f, ok := out.(*os.File); ok {
+ w.fd = int(f.Fd())
+ w.isTerminal = IsTerminal(w.fd)
+ }
+ return w
+}
+
+// Flush flushes the underlying buffer.
+func (w *Writer) Flush(lineCount int) (err error) {
+ // some terminals interpret 'cursor up 0' as 'cursor up 1'
+ if w.lineCount > 0 {
+ err = w.clearLines()
+ if err != nil {
+ return
+ }
+ }
+ w.lineCount = lineCount
+ _, err = w.buf.WriteTo(w.out)
+ return
+}
+
+// Write appends the contents of p to the underlying buffer.
+func (w *Writer) Write(p []byte) (n int, err error) {
+ return w.buf.Write(p)
+}
+
+// WriteString writes string to the underlying buffer.
+func (w *Writer) WriteString(s string) (n int, err error) {
+ return w.buf.WriteString(s)
+}
+
+// ReadFrom reads from the provided io.Reader and writes to the
+// underlying buffer.
+func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
+ return w.buf.ReadFrom(r)
+}
+
+// GetWidth returns width of underlying terminal.
+func (w *Writer) GetWidth() (int, error) {
+ if !w.isTerminal {
+ return -1, ErrNotTTY
+ }
+ tw, _, err := GetSize(w.fd)
+ return tw, err
+}
+
+func (w *Writer) ansiCuuAndEd() (err error) {
+ buf := make([]byte, 8)
+ buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lineCount), 10)
+ _, err = w.out.Write(append(buf, cuuAndEd...))
+ return
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_posix.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_posix.go
new file mode 100644
index 000000000..f54a5d06b
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_posix.go
@@ -0,0 +1,26 @@
+// +build !windows
+
+package cwriter
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+func (w *Writer) clearLines() error {
+ return w.ansiCuuAndEd()
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+ if err != nil {
+ return -1, -1, err
+ }
+ return int(ws.Col), int(ws.Row), nil
+}
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ return err == nil
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_windows.go b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_windows.go
new file mode 100644
index 000000000..1a69c81ac
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/cwriter/writer_windows.go
@@ -0,0 +1,73 @@
+// +build windows
+
+package cwriter
+
+import (
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var kernel32 = windows.NewLazySystemDLL("kernel32.dll")
+
+var (
+ procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
+ procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
+)
+
+func (w *Writer) clearLines() error {
+ if !w.isTerminal {
+ // hope it's cygwin or similar
+ return w.ansiCuuAndEd()
+ }
+
+ var info windows.ConsoleScreenBufferInfo
+ if err := windows.GetConsoleScreenBufferInfo(windows.Handle(w.fd), &info); err != nil {
+ return err
+ }
+
+ info.CursorPosition.Y -= int16(w.lineCount)
+ if info.CursorPosition.Y < 0 {
+ info.CursorPosition.Y = 0
+ }
+ _, _, _ = procSetConsoleCursorPosition.Call(
+ uintptr(w.fd),
+ uintptr(uint32(uint16(info.CursorPosition.Y))<<16|uint32(uint16(info.CursorPosition.X))),
+ )
+
+ // clear the lines
+ cursor := &windows.Coord{
+ X: info.Window.Left,
+ Y: info.CursorPosition.Y,
+ }
+ count := uint32(info.Size.X) * uint32(w.lineCount)
+ _, _, _ = procFillConsoleOutputCharacter.Call(
+ uintptr(w.fd),
+ uintptr(' '),
+ uintptr(count),
+ *(*uintptr)(unsafe.Pointer(cursor)),
+ uintptr(unsafe.Pointer(new(uint32))),
+ )
+ return nil
+}
+
+// GetSize returns the visible dimensions of the given terminal.
+//
+// These dimensions don't include any scrollback buffer height.
+func GetSize(fd int) (width, height int, err error) {
+ var info windows.ConsoleScreenBufferInfo
+ if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
+ return 0, 0, err
+ }
+ // terminal.GetSize from crypto/ssh adds "+ 1" to both width and height:
+ // https://go.googlesource.com/crypto/+/refs/heads/release-branch.go1.14/ssh/terminal/util_windows.go#75
+ // but looks like this is a root cause of issue #66, so removing both "+ 1" have fixed it.
+ return int(info.Window.Right - info.Window.Left), int(info.Window.Bottom - info.Window.Top), nil
+}
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ var st uint32
+ err := windows.GetConsoleMode(windows.Handle(fd), &st)
+ return err == nil
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/any.go b/vendor/github.com/vbauerster/mpb/v6/decor/any.go
new file mode 100644
index 000000000..39518f594
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/any.go
@@ -0,0 +1,21 @@
+package decor
+
+// Any decorator displays text, that can be changed during decorator's
+// lifetime via provided DecorFunc.
+//
+// `fn` DecorFunc callback
+//
+// `wcc` optional WC config
+//
+func Any(fn DecorFunc, wcc ...WC) Decorator {
+ return &any{initWC(wcc...), fn}
+}
+
+type any struct {
+ WC
+ fn DecorFunc
+}
+
+func (d *any) Decor(s Statistics) string {
+ return d.FormatMsg(d.fn(s))
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/counters.go b/vendor/github.com/vbauerster/mpb/v6/decor/counters.go
new file mode 100644
index 000000000..4a5343d41
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/counters.go
@@ -0,0 +1,243 @@
+package decor
+
+import (
+ "fmt"
+ "strings"
+)
+
+const (
+ _ = iota
+ UnitKiB
+ UnitKB
+)
+
+// CountersNoUnit is a wrapper around Counters with no unit param.
+func CountersNoUnit(pairFmt string, wcc ...WC) Decorator {
+ return Counters(0, pairFmt, wcc...)
+}
+
+// CountersKibiByte is a wrapper around Counters with predefined unit
+// UnitKiB (bytes/1024).
+func CountersKibiByte(pairFmt string, wcc ...WC) Decorator {
+ return Counters(UnitKiB, pairFmt, wcc...)
+}
+
+// CountersKiloByte is a wrapper around Counters with predefined unit
+// UnitKB (bytes/1000).
+func CountersKiloByte(pairFmt string, wcc ...WC) Decorator {
+ return Counters(UnitKB, pairFmt, wcc...)
+}
+
+// Counters decorator with dynamic unit measure adjustment.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `pairFmt` printf compatible verbs for current and total pair
+//
+// `wcc` optional WC config
+//
+// pairFmt example if unit=UnitKB:
+//
+// pairFmt="%.1f / %.1f" output: "1.0MB / 12.0MB"
+// pairFmt="% .1f / % .1f" output: "1.0 MB / 12.0 MB"
+// pairFmt="%d / %d" output: "1MB / 12MB"
+// pairFmt="% d / % d" output: "1 MB / 12 MB"
+//
+func Counters(unit int, pairFmt string, wcc ...WC) Decorator {
+ producer := func(unit int, pairFmt string) DecorFunc {
+ if pairFmt == "" {
+ pairFmt = "%d / %d"
+ } else if strings.Count(pairFmt, "%") != 2 {
+ panic("expected pairFmt with exactly 2 verbs")
+ }
+ switch unit {
+ case UnitKiB:
+ return func(s Statistics) string {
+ return fmt.Sprintf(pairFmt, SizeB1024(s.Current), SizeB1024(s.Total))
+ }
+ case UnitKB:
+ return func(s Statistics) string {
+ return fmt.Sprintf(pairFmt, SizeB1000(s.Current), SizeB1000(s.Total))
+ }
+ default:
+ return func(s Statistics) string {
+ return fmt.Sprintf(pairFmt, s.Current, s.Total)
+ }
+ }
+ }
+ return Any(producer(unit, pairFmt), wcc...)
+}
+
+// TotalNoUnit is a wrapper around Total with no unit param.
+func TotalNoUnit(format string, wcc ...WC) Decorator {
+ return Total(0, format, wcc...)
+}
+
+// TotalKibiByte is a wrapper around Total with predefined unit
+// UnitKiB (bytes/1024).
+func TotalKibiByte(format string, wcc ...WC) Decorator {
+ return Total(UnitKiB, format, wcc...)
+}
+
+// TotalKiloByte is a wrapper around Total with predefined unit
+// UnitKB (bytes/1000).
+func TotalKiloByte(format string, wcc ...WC) Decorator {
+ return Total(UnitKB, format, wcc...)
+}
+
+// Total decorator with dynamic unit measure adjustment.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `format` printf compatible verb for Total
+//
+// `wcc` optional WC config
+//
+// format example if unit=UnitKiB:
+//
+// format="%.1f" output: "12.0MiB"
+// format="% .1f" output: "12.0 MiB"
+// format="%d" output: "12MiB"
+// format="% d" output: "12 MiB"
+//
+func Total(unit int, format string, wcc ...WC) Decorator {
+ producer := func(unit int, format string) DecorFunc {
+ if format == "" {
+ format = "%d"
+ } else if strings.Count(format, "%") != 1 {
+ panic("expected format with exactly 1 verb")
+ }
+
+ switch unit {
+ case UnitKiB:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, SizeB1024(s.Total))
+ }
+ case UnitKB:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, SizeB1000(s.Total))
+ }
+ default:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, s.Total)
+ }
+ }
+ }
+ return Any(producer(unit, format), wcc...)
+}
+
+// CurrentNoUnit is a wrapper around Current with no unit param.
+func CurrentNoUnit(format string, wcc ...WC) Decorator {
+ return Current(0, format, wcc...)
+}
+
+// CurrentKibiByte is a wrapper around Current with predefined unit
+// UnitKiB (bytes/1024).
+func CurrentKibiByte(format string, wcc ...WC) Decorator {
+ return Current(UnitKiB, format, wcc...)
+}
+
+// CurrentKiloByte is a wrapper around Current with predefined unit
+// UnitKB (bytes/1000).
+func CurrentKiloByte(format string, wcc ...WC) Decorator {
+ return Current(UnitKB, format, wcc...)
+}
+
+// Current decorator with dynamic unit measure adjustment.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `format` printf compatible verb for Current
+//
+// `wcc` optional WC config
+//
+// format example if unit=UnitKiB:
+//
+// format="%.1f" output: "12.0MiB"
+// format="% .1f" output: "12.0 MiB"
+// format="%d" output: "12MiB"
+// format="% d" output: "12 MiB"
+//
+func Current(unit int, format string, wcc ...WC) Decorator {
+ producer := func(unit int, format string) DecorFunc {
+ if format == "" {
+ format = "%d"
+ } else if strings.Count(format, "%") != 1 {
+ panic("expected format with exactly 1 verb")
+ }
+
+ switch unit {
+ case UnitKiB:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, SizeB1024(s.Current))
+ }
+ case UnitKB:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, SizeB1000(s.Current))
+ }
+ default:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, s.Current)
+ }
+ }
+ }
+ return Any(producer(unit, format), wcc...)
+}
+
+// InvertedCurrentNoUnit is a wrapper around InvertedCurrent with no unit param.
+func InvertedCurrentNoUnit(format string, wcc ...WC) Decorator {
+ return InvertedCurrent(0, format, wcc...)
+}
+
+// InvertedCurrentKibiByte is a wrapper around InvertedCurrent with predefined unit
+// UnitKiB (bytes/1024).
+func InvertedCurrentKibiByte(format string, wcc ...WC) Decorator {
+ return InvertedCurrent(UnitKiB, format, wcc...)
+}
+
+// InvertedCurrentKiloByte is a wrapper around InvertedCurrent with predefined unit
+// UnitKB (bytes/1000).
+func InvertedCurrentKiloByte(format string, wcc ...WC) Decorator {
+ return InvertedCurrent(UnitKB, format, wcc...)
+}
+
+// InvertedCurrent decorator with dynamic unit measure adjustment.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `format` printf compatible verb for InvertedCurrent
+//
+// `wcc` optional WC config
+//
+// format example if unit=UnitKiB:
+//
+// format="%.1f" output: "12.0MiB"
+// format="% .1f" output: "12.0 MiB"
+// format="%d" output: "12MiB"
+// format="% d" output: "12 MiB"
+//
+func InvertedCurrent(unit int, format string, wcc ...WC) Decorator {
+ producer := func(unit int, format string) DecorFunc {
+ if format == "" {
+ format = "%d"
+ } else if strings.Count(format, "%") != 1 {
+ panic("expected format with exactly 1 verb")
+ }
+
+ switch unit {
+ case UnitKiB:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, SizeB1024(s.Total-s.Current))
+ }
+ case UnitKB:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, SizeB1000(s.Total-s.Current))
+ }
+ default:
+ return func(s Statistics) string {
+ return fmt.Sprintf(format, s.Total-s.Current)
+ }
+ }
+ }
+ return Any(producer(unit, format), wcc...)
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/decorator.go b/vendor/github.com/vbauerster/mpb/v6/decor/decorator.go
new file mode 100644
index 000000000..e81fae367
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/decorator.go
@@ -0,0 +1,191 @@
+package decor
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/acarl005/stripansi"
+ "github.com/mattn/go-runewidth"
+)
+
+const (
+ // DidentRight bit specifies identation direction.
+ // |foo |b | With DidentRight
+ // | foo| b| Without DidentRight
+ DidentRight = 1 << iota
+
+ // DextraSpace bit adds extra space, makes sense with DSyncWidth only.
+ // When DidentRight bit set, the space will be added to the right,
+ // otherwise to the left.
+ DextraSpace
+
+ // DSyncWidth bit enables same column width synchronization.
+ // Effective with multiple bars only.
+ DSyncWidth
+
+ // DSyncWidthR is shortcut for DSyncWidth|DidentRight
+ DSyncWidthR = DSyncWidth | DidentRight
+
+ // DSyncSpace is shortcut for DSyncWidth|DextraSpace
+ DSyncSpace = DSyncWidth | DextraSpace
+
+ // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DidentRight
+ DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight
+)
+
+// TimeStyle enum.
+type TimeStyle int
+
+// TimeStyle kinds.
+const (
+ ET_STYLE_GO TimeStyle = iota
+ ET_STYLE_HHMMSS
+ ET_STYLE_HHMM
+ ET_STYLE_MMSS
+)
+
+// Statistics consists of progress related statistics, that Decorator
+// may need.
+type Statistics struct {
+ ID int
+ AvailableWidth int
+ Total int64
+ Current int64
+ Refill int64
+ Completed bool
+}
+
+// Decorator interface.
+// Most of the time there is no need to implement this interface
+// manually, as decor package already provides a wide range of decorators
+// which implement this interface. If however built-in decorators don't
+// meet your needs, you're free to implement your own one by implementing
+// this particular interface. The easy way to go is to convert a
+// `DecorFunc` into a `Decorator` interface by using provided
+// `func Any(DecorFunc, ...WC) Decorator`.
+type Decorator interface {
+ Configurator
+ Synchronizer
+ Decor(Statistics) string
+}
+
+// DecorFunc func type.
+// To be used with `func Any`(DecorFunc, ...WC) Decorator`.
+type DecorFunc func(Statistics) string
+
+// Synchronizer interface.
+// All decorators implement this interface implicitly. Its Sync
+// method exposes width sync channel, if DSyncWidth bit is set.
+type Synchronizer interface {
+ Sync() (chan int, bool)
+}
+
+// Configurator interface.
+type Configurator interface {
+ GetConf() WC
+ SetConf(WC)
+}
+
+// Wrapper interface.
+// If you're implementing custom Decorator by wrapping a built-in one,
+// it is necessary to implement this interface to retain functionality
+// of built-in Decorator.
+type Wrapper interface {
+ Base() Decorator
+}
+
+// EwmaDecorator interface.
+// EWMA based decorators should implement this one.
+type EwmaDecorator interface {
+ EwmaUpdate(int64, time.Duration)
+}
+
+// AverageDecorator interface.
+// Average decorators should implement this interface to provide start
+// time adjustment facility, for resume-able tasks.
+type AverageDecorator interface {
+ AverageAdjust(time.Time)
+}
+
+// ShutdownListener interface.
+// If decorator needs to be notified once upon bar shutdown event, so
+// this is the right interface to implement.
+type ShutdownListener interface {
+ Shutdown()
+}
+
+// Global convenience instances of WC with sync width bit set.
+// To be used with multiple bars only, i.e. not effective for single bar usage.
+var (
+ WCSyncWidth = WC{C: DSyncWidth}
+ WCSyncWidthR = WC{C: DSyncWidthR}
+ WCSyncSpace = WC{C: DSyncSpace}
+ WCSyncSpaceR = WC{C: DSyncSpaceR}
+)
+
+// WC is a struct with two public fields W and C, both of int type.
+// W represents width and C represents bit set of width related config.
+// A decorator should embed WC, to enable width synchronization.
+type WC struct {
+ W int
+ C int
+ fill func(s string, w int) string
+ wsync chan int
+}
+
+// FormatMsg formats final message according to WC.W and WC.C.
+// Should be called by any Decorator implementation.
+func (wc *WC) FormatMsg(msg string) string {
+ pureWidth := runewidth.StringWidth(msg)
+ stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
+ maxCell := wc.W
+ if (wc.C & DSyncWidth) != 0 {
+ cellCount := stripWidth
+ if (wc.C & DextraSpace) != 0 {
+ cellCount++
+ }
+ wc.wsync <- cellCount
+ maxCell = <-wc.wsync
+ }
+ return wc.fill(msg, maxCell+(pureWidth-stripWidth))
+}
+
+// Init initializes width related config.
+func (wc *WC) Init() WC {
+ wc.fill = runewidth.FillLeft
+ if (wc.C & DidentRight) != 0 {
+ wc.fill = runewidth.FillRight
+ }
+ if (wc.C & DSyncWidth) != 0 {
+ // it's deliberate choice to override wsync on each Init() call,
+ // this way globals like WCSyncSpace can be reused
+ wc.wsync = make(chan int)
+ }
+ return *wc
+}
+
+// Sync is implementation of Synchronizer interface.
+func (wc *WC) Sync() (chan int, bool) {
+ if (wc.C&DSyncWidth) != 0 && wc.wsync == nil {
+ panic(fmt.Sprintf("%T is not initialized", wc))
+ }
+ return wc.wsync, (wc.C & DSyncWidth) != 0
+}
+
+// GetConf is implementation of Configurator interface.
+func (wc *WC) GetConf() WC {
+ return *wc
+}
+
+// SetConf is implementation of Configurator interface.
+func (wc *WC) SetConf(conf WC) {
+ *wc = conf.Init()
+}
+
+func initWC(wcc ...WC) WC {
+ var wc WC
+ for _, nwc := range wcc {
+ wc = nwc
+ }
+ return wc.Init()
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/doc.go b/vendor/github.com/vbauerster/mpb/v6/decor/doc.go
new file mode 100644
index 000000000..bfbb82e11
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/doc.go
@@ -0,0 +1,20 @@
+// Package decor provides common decorators for "github.com/vbauerster/mpb/v6" module.
+/*
+ Some decorators returned by this package might have a closure state. It is ok to use
+ decorators concurrently, unless you share the same decorator among multiple
+ *mpb.Bar instances. To avoid data races, create new decorator per *mpb.Bar instance.
+
+ Don't:
+
+ p := mpb.New()
+ name := decor.Name("bar")
+ p.AddBar(100, mpb.AppendDecorators(name))
+ p.AddBar(100, mpb.AppendDecorators(name))
+
+ Do:
+
+ p := mpb.New()
+ p.AddBar(100, mpb.AppendDecorators(decor.Name("bar1")))
+ p.AddBar(100, mpb.AppendDecorators(decor.Name("bar2")))
+*/
+package decor
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/elapsed.go b/vendor/github.com/vbauerster/mpb/v6/decor/elapsed.go
new file mode 100644
index 000000000..e389f1581
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/elapsed.go
@@ -0,0 +1,35 @@
+package decor
+
+import (
+ "time"
+)
+
+// Elapsed decorator. It's wrapper of NewElapsed.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `wcc` optional WC config
+//
+func Elapsed(style TimeStyle, wcc ...WC) Decorator {
+ return NewElapsed(style, time.Now(), wcc...)
+}
+
+// NewElapsed returns elapsed time decorator.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `startTime` start time
+//
+// `wcc` optional WC config
+//
+func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
+ var msg string
+ producer := chooseTimeProducer(style)
+ fn := func(s Statistics) string {
+ if !s.Completed {
+ msg = producer(time.Since(startTime))
+ }
+ return msg
+ }
+ return Any(fn, wcc...)
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/eta.go b/vendor/github.com/vbauerster/mpb/v6/decor/eta.go
new file mode 100644
index 000000000..d03caa735
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/eta.go
@@ -0,0 +1,203 @@
+package decor
+
+import (
+ "fmt"
+ "math"
+ "time"
+
+ "github.com/VividCortex/ewma"
+)
+
+// TimeNormalizer interface. Implementors could be passed into
+// MovingAverageETA, in order to affect i.e. normalize its output.
+type TimeNormalizer interface {
+ Normalize(time.Duration) time.Duration
+}
+
+// TimeNormalizerFunc is function type adapter to convert function
+// into TimeNormalizer.
+type TimeNormalizerFunc func(time.Duration) time.Duration
+
+func (f TimeNormalizerFunc) Normalize(src time.Duration) time.Duration {
+ return f(src)
+}
+
+// EwmaETA exponential-weighted-moving-average based ETA decorator.
+// For this decorator to work correctly you have to measure each
+// iteration's duration and pass it to the
+// *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
+func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
+ var average ewma.MovingAverage
+ if age == 0 {
+ average = ewma.NewMovingAverage()
+ } else {
+ average = ewma.NewMovingAverage(age)
+ }
+ return MovingAverageETA(style, NewThreadSafeMovingAverage(average), nil, wcc...)
+}
+
+// MovingAverageETA decorator relies on MovingAverage implementation to calculate its average.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `average` implementation of MovingAverage interface
+//
+// `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
+//
+// `wcc` optional WC config
+//
+func MovingAverageETA(style TimeStyle, average ewma.MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
+ d := &movingAverageETA{
+ WC: initWC(wcc...),
+ average: average,
+ normalizer: normalizer,
+ producer: chooseTimeProducer(style),
+ }
+ return d
+}
+
+type movingAverageETA struct {
+ WC
+ average ewma.MovingAverage
+ normalizer TimeNormalizer
+ producer func(time.Duration) string
+}
+
+func (d *movingAverageETA) Decor(s Statistics) string {
+ v := math.Round(d.average.Value())
+ remaining := time.Duration((s.Total - s.Current) * int64(v))
+ if d.normalizer != nil {
+ remaining = d.normalizer.Normalize(remaining)
+ }
+ return d.FormatMsg(d.producer(remaining))
+}
+
+func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) {
+ durPerItem := float64(dur) / float64(n)
+ if math.IsInf(durPerItem, 0) || math.IsNaN(durPerItem) {
+ return
+ }
+ d.average.Add(durPerItem)
+}
+
+// AverageETA decorator. It's wrapper of NewAverageETA.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `wcc` optional WC config
+//
+func AverageETA(style TimeStyle, wcc ...WC) Decorator {
+ return NewAverageETA(style, time.Now(), nil, wcc...)
+}
+
+// NewAverageETA decorator with user provided start time.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `startTime` start time
+//
+// `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
+//
+// `wcc` optional WC config
+//
+func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator {
+ d := &averageETA{
+ WC: initWC(wcc...),
+ startTime: startTime,
+ normalizer: normalizer,
+ producer: chooseTimeProducer(style),
+ }
+ return d
+}
+
+type averageETA struct {
+ WC
+ startTime time.Time
+ normalizer TimeNormalizer
+ producer func(time.Duration) string
+}
+
+func (d *averageETA) Decor(s Statistics) string {
+ var remaining time.Duration
+ if s.Current != 0 {
+ durPerItem := float64(time.Since(d.startTime)) / float64(s.Current)
+ durPerItem = math.Round(durPerItem)
+ remaining = time.Duration((s.Total - s.Current) * int64(durPerItem))
+ if d.normalizer != nil {
+ remaining = d.normalizer.Normalize(remaining)
+ }
+ }
+ return d.FormatMsg(d.producer(remaining))
+}
+
+func (d *averageETA) AverageAdjust(startTime time.Time) {
+ d.startTime = startTime
+}
+
+// MaxTolerateTimeNormalizer returns implementation of TimeNormalizer.
+func MaxTolerateTimeNormalizer(maxTolerate time.Duration) TimeNormalizer {
+ var normalized time.Duration
+ var lastCall time.Time
+ return TimeNormalizerFunc(func(remaining time.Duration) time.Duration {
+ if diff := normalized - remaining; diff <= 0 || diff > maxTolerate || remaining < time.Minute {
+ normalized = remaining
+ lastCall = time.Now()
+ return remaining
+ }
+ normalized -= time.Since(lastCall)
+ lastCall = time.Now()
+ return normalized
+ })
+}
+
+// FixedIntervalTimeNormalizer returns implementation of TimeNormalizer.
+func FixedIntervalTimeNormalizer(updInterval int) TimeNormalizer {
+ var normalized time.Duration
+ var lastCall time.Time
+ var count int
+ return TimeNormalizerFunc(func(remaining time.Duration) time.Duration {
+ if count == 0 || remaining < time.Minute {
+ count = updInterval
+ normalized = remaining
+ lastCall = time.Now()
+ return remaining
+ }
+ count--
+ normalized -= time.Since(lastCall)
+ lastCall = time.Now()
+ return normalized
+ })
+}
+
+func chooseTimeProducer(style TimeStyle) func(time.Duration) string {
+ switch style {
+ case ET_STYLE_HHMMSS:
+ return func(remaining time.Duration) string {
+ hours := int64(remaining/time.Hour) % 60
+ minutes := int64(remaining/time.Minute) % 60
+ seconds := int64(remaining/time.Second) % 60
+ return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+ }
+ case ET_STYLE_HHMM:
+ return func(remaining time.Duration) string {
+ hours := int64(remaining/time.Hour) % 60
+ minutes := int64(remaining/time.Minute) % 60
+ return fmt.Sprintf("%02d:%02d", hours, minutes)
+ }
+ case ET_STYLE_MMSS:
+ return func(remaining time.Duration) string {
+ hours := int64(remaining/time.Hour) % 60
+ minutes := int64(remaining/time.Minute) % 60
+ seconds := int64(remaining/time.Second) % 60
+ if hours > 0 {
+ return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+ }
+ return fmt.Sprintf("%02d:%02d", minutes, seconds)
+ }
+ default:
+ return func(remaining time.Duration) string {
+ // strip off nanoseconds
+ return ((remaining / time.Second) * time.Second).String()
+ }
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/merge.go b/vendor/github.com/vbauerster/mpb/v6/decor/merge.go
new file mode 100644
index 000000000..e41406a64
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/merge.go
@@ -0,0 +1,107 @@
+package decor
+
+import (
+ "strings"
+
+ "github.com/acarl005/stripansi"
+ "github.com/mattn/go-runewidth"
+)
+
+// Merge wraps its decorator argument with intention to sync width
+// with several decorators of another bar. Visual example:
+//
+// +----+--------+---------+--------+
+// | B1 | MERGE(D, P1, Pn) |
+// +----+--------+---------+--------+
+// | B2 | D0 | D1 | Dn |
+// +----+--------+---------+--------+
+//
+func Merge(decorator Decorator, placeholders ...WC) Decorator {
+ if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 {
+ return decorator
+ }
+ md := &mergeDecorator{
+ Decorator: decorator,
+ wc: decorator.GetConf(),
+ placeHolders: make([]*placeHolderDecorator, len(placeholders)),
+ }
+ decorator.SetConf(WC{})
+ for i, wc := range placeholders {
+ if (wc.C & DSyncWidth) == 0 {
+ return decorator
+ }
+ md.placeHolders[i] = &placeHolderDecorator{wc.Init()}
+ }
+ return md
+}
+
+type mergeDecorator struct {
+ Decorator
+ wc WC
+ placeHolders []*placeHolderDecorator
+}
+
+func (d *mergeDecorator) GetConf() WC {
+ return d.wc
+}
+
+func (d *mergeDecorator) SetConf(conf WC) {
+ d.wc = conf.Init()
+}
+
+func (d *mergeDecorator) MergeUnwrap() []Decorator {
+ decorators := make([]Decorator, len(d.placeHolders))
+ for i, ph := range d.placeHolders {
+ decorators[i] = ph
+ }
+ return decorators
+}
+
+func (d *mergeDecorator) Sync() (chan int, bool) {
+ return d.wc.Sync()
+}
+
+func (d *mergeDecorator) Base() Decorator {
+ return d.Decorator
+}
+
+func (d *mergeDecorator) Decor(s Statistics) string {
+ msg := d.Decorator.Decor(s)
+ pureWidth := runewidth.StringWidth(msg)
+ stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
+ cellCount := stripWidth
+ if (d.wc.C & DextraSpace) != 0 {
+ cellCount++
+ }
+
+ total := runewidth.StringWidth(d.placeHolders[0].FormatMsg(""))
+ pw := (cellCount - total) / len(d.placeHolders)
+ rem := (cellCount - total) % len(d.placeHolders)
+
+ var diff int
+ for i := 1; i < len(d.placeHolders); i++ {
+ ph := d.placeHolders[i]
+ width := pw - diff
+ if (ph.WC.C & DextraSpace) != 0 {
+ width--
+ if width < 0 {
+ width = 0
+ }
+ }
+ max := runewidth.StringWidth(ph.FormatMsg(strings.Repeat(" ", width)))
+ total += max
+ diff = max - pw
+ }
+
+ d.wc.wsync <- pw + rem
+ max := <-d.wc.wsync
+ return d.wc.fill(msg, max+total+(pureWidth-stripWidth))
+}
+
+type placeHolderDecorator struct {
+ WC
+}
+
+func (d *placeHolderDecorator) Decor(Statistics) string {
+ return ""
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/moving_average.go b/vendor/github.com/vbauerster/mpb/v6/decor/moving_average.go
new file mode 100644
index 000000000..50ac9c393
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/moving_average.go
@@ -0,0 +1,68 @@
+package decor
+
+import (
+ "sort"
+ "sync"
+
+ "github.com/VividCortex/ewma"
+)
+
+type threadSafeMovingAverage struct {
+ ewma.MovingAverage
+ mu sync.Mutex
+}
+
+func (s *threadSafeMovingAverage) Add(value float64) {
+ s.mu.Lock()
+ s.MovingAverage.Add(value)
+ s.mu.Unlock()
+}
+
+func (s *threadSafeMovingAverage) Value() float64 {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.MovingAverage.Value()
+}
+
+func (s *threadSafeMovingAverage) Set(value float64) {
+ s.mu.Lock()
+ s.MovingAverage.Set(value)
+ s.mu.Unlock()
+}
+
+// NewThreadSafeMovingAverage converts provided ewma.MovingAverage
+// into thread safe ewma.MovingAverage.
+func NewThreadSafeMovingAverage(average ewma.MovingAverage) ewma.MovingAverage {
+ if tsma, ok := average.(*threadSafeMovingAverage); ok {
+ return tsma
+ }
+ return &threadSafeMovingAverage{MovingAverage: average}
+}
+
+type medianWindow [3]float64
+
+func (s *medianWindow) Len() int { return len(s) }
+func (s *medianWindow) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s *medianWindow) Less(i, j int) bool { return s[i] < s[j] }
+
+func (s *medianWindow) Add(value float64) {
+ s[0], s[1] = s[1], s[2]
+ s[2] = value
+}
+
+func (s *medianWindow) Value() float64 {
+ tmp := *s
+ sort.Sort(&tmp)
+ return tmp[1]
+}
+
+func (s *medianWindow) Set(value float64) {
+ for i := 0; i < len(s); i++ {
+ s[i] = value
+ }
+}
+
+// NewMedian is fixed last 3 samples median MovingAverage.
+func NewMedian() ewma.MovingAverage {
+ return NewThreadSafeMovingAverage(new(medianWindow))
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/name.go b/vendor/github.com/vbauerster/mpb/v6/decor/name.go
new file mode 100644
index 000000000..3af311254
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/name.go
@@ -0,0 +1,12 @@
+package decor
+
+// Name decorator displays text that is set once and can't be changed
+// during decorator's lifetime.
+//
+// `str` string to display
+//
+// `wcc` optional WC config
+//
+func Name(str string, wcc ...WC) Decorator {
+ return Any(func(Statistics) string { return str }, wcc...)
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/on_complete.go b/vendor/github.com/vbauerster/mpb/v6/decor/on_complete.go
new file mode 100644
index 000000000..f46b19aba
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/on_complete.go
@@ -0,0 +1,37 @@
+package decor
+
+// OnComplete returns decorator, which wraps provided decorator, with
+// sole purpose to display provided message on complete event.
+//
+// `decorator` Decorator to wrap
+//
+// `message` message to display on complete event
+//
+func OnComplete(decorator Decorator, message string) Decorator {
+ d := &onCompleteWrapper{
+ Decorator: decorator,
+ msg: message,
+ }
+ if md, ok := decorator.(*mergeDecorator); ok {
+ d.Decorator, md.Decorator = md.Decorator, d
+ return md
+ }
+ return d
+}
+
+type onCompleteWrapper struct {
+ Decorator
+ msg string
+}
+
+func (d *onCompleteWrapper) Decor(s Statistics) string {
+ if s.Completed {
+ wc := d.GetConf()
+ return wc.FormatMsg(d.msg)
+ }
+ return d.Decorator.Decor(s)
+}
+
+func (d *onCompleteWrapper) Base() Decorator {
+ return d.Decorator
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/percentage.go b/vendor/github.com/vbauerster/mpb/v6/decor/percentage.go
new file mode 100644
index 000000000..f4922bb52
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/percentage.go
@@ -0,0 +1,58 @@
+package decor
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+
+ "github.com/vbauerster/mpb/v6/internal"
+)
+
+type percentageType float64
+
+func (s percentageType) Format(st fmt.State, verb rune) {
+ var prec int
+ switch verb {
+ case 'd':
+ case 's':
+ prec = -1
+ default:
+ if p, ok := st.Precision(); ok {
+ prec = p
+ } else {
+ prec = 6
+ }
+ }
+
+ io.WriteString(st, strconv.FormatFloat(float64(s), 'f', prec, 64))
+
+ if st.Flag(' ') {
+ io.WriteString(st, " ")
+ }
+ io.WriteString(st, "%")
+}
+
+// Percentage returns percentage decorator. It's a wrapper of NewPercentage.
+func Percentage(wcc ...WC) Decorator {
+ return NewPercentage("% d", wcc...)
+}
+
+// NewPercentage percentage decorator with custom format string.
+//
+// format examples:
+//
+// format="%.1f" output: "1.0%"
+// format="% .1f" output: "1.0 %"
+// format="%d" output: "1%"
+// format="% d" output: "1 %"
+//
+func NewPercentage(format string, wcc ...WC) Decorator {
+ if format == "" {
+ format = "% d"
+ }
+ f := func(s Statistics) string {
+ p := internal.Percentage(s.Total, s.Current, 100)
+ return fmt.Sprintf(format, percentageType(p))
+ }
+ return Any(f, wcc...)
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/size_type.go b/vendor/github.com/vbauerster/mpb/v6/decor/size_type.go
new file mode 100644
index 000000000..e4b974058
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/size_type.go
@@ -0,0 +1,109 @@
+package decor
+
+import (
+ "fmt"
+ "io"
+ "math"
+ "strconv"
+)
+
+//go:generate stringer -type=SizeB1024 -trimprefix=_i
+//go:generate stringer -type=SizeB1000 -trimprefix=_
+
+const (
+ _ib SizeB1024 = iota + 1
+ _iKiB SizeB1024 = 1 << (iota * 10)
+ _iMiB
+ _iGiB
+ _iTiB
+)
+
+// SizeB1024 named type, which implements fmt.Formatter interface. It
+// adjusts its value according to byte size multiple by 1024 and appends
+// appropriate size marker (KiB, MiB, GiB, TiB).
+type SizeB1024 int64
+
+func (self SizeB1024) Format(st fmt.State, verb rune) {
+ var prec int
+ switch verb {
+ case 'd':
+ case 's':
+ prec = -1
+ default:
+ if p, ok := st.Precision(); ok {
+ prec = p
+ } else {
+ prec = 6
+ }
+ }
+
+ var unit SizeB1024
+ switch {
+ case self < _iKiB:
+ unit = _ib
+ case self < _iMiB:
+ unit = _iKiB
+ case self < _iGiB:
+ unit = _iMiB
+ case self < _iTiB:
+ unit = _iGiB
+ case self <= math.MaxInt64:
+ unit = _iTiB
+ }
+
+ io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
+
+ if st.Flag(' ') {
+ io.WriteString(st, " ")
+ }
+ io.WriteString(st, unit.String())
+}
+
+const (
+ _b SizeB1000 = 1
+ _KB SizeB1000 = _b * 1000
+ _MB SizeB1000 = _KB * 1000
+ _GB SizeB1000 = _MB * 1000
+ _TB SizeB1000 = _GB * 1000
+)
+
+// SizeB1000 named type, which implements fmt.Formatter interface. It
+// adjusts its value according to byte size multiple by 1000 and appends
+// appropriate size marker (KB, MB, GB, TB).
+type SizeB1000 int64
+
+func (self SizeB1000) Format(st fmt.State, verb rune) {
+ var prec int
+ switch verb {
+ case 'd':
+ case 's':
+ prec = -1
+ default:
+ if p, ok := st.Precision(); ok {
+ prec = p
+ } else {
+ prec = 6
+ }
+ }
+
+ var unit SizeB1000
+ switch {
+ case self < _KB:
+ unit = _b
+ case self < _MB:
+ unit = _KB
+ case self < _GB:
+ unit = _MB
+ case self < _TB:
+ unit = _GB
+ case self <= math.MaxInt64:
+ unit = _TB
+ }
+
+ io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
+
+ if st.Flag(' ') {
+ io.WriteString(st, " ")
+ }
+ io.WriteString(st, unit.String())
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1000_string.go b/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1000_string.go
new file mode 100644
index 000000000..3f32ef715
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1000_string.go
@@ -0,0 +1,41 @@
+// Code generated by "stringer -type=SizeB1000 -trimprefix=_"; DO NOT EDIT.
+
+package decor
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[_b-1]
+ _ = x[_KB-1000]
+ _ = x[_MB-1000000]
+ _ = x[_GB-1000000000]
+ _ = x[_TB-1000000000000]
+}
+
+const (
+ _SizeB1000_name_0 = "b"
+ _SizeB1000_name_1 = "KB"
+ _SizeB1000_name_2 = "MB"
+ _SizeB1000_name_3 = "GB"
+ _SizeB1000_name_4 = "TB"
+)
+
+func (i SizeB1000) String() string {
+ switch {
+ case i == 1:
+ return _SizeB1000_name_0
+ case i == 1000:
+ return _SizeB1000_name_1
+ case i == 1000000:
+ return _SizeB1000_name_2
+ case i == 1000000000:
+ return _SizeB1000_name_3
+ case i == 1000000000000:
+ return _SizeB1000_name_4
+ default:
+ return "SizeB1000(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1024_string.go b/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1024_string.go
new file mode 100644
index 000000000..9fca66cc7
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/sizeb1024_string.go
@@ -0,0 +1,41 @@
+// Code generated by "stringer -type=SizeB1024 -trimprefix=_i"; DO NOT EDIT.
+
+package decor
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[_ib-1]
+ _ = x[_iKiB-1024]
+ _ = x[_iMiB-1048576]
+ _ = x[_iGiB-1073741824]
+ _ = x[_iTiB-1099511627776]
+}
+
+const (
+ _SizeB1024_name_0 = "b"
+ _SizeB1024_name_1 = "KiB"
+ _SizeB1024_name_2 = "MiB"
+ _SizeB1024_name_3 = "GiB"
+ _SizeB1024_name_4 = "TiB"
+)
+
+func (i SizeB1024) String() string {
+ switch {
+ case i == 1:
+ return _SizeB1024_name_0
+ case i == 1024:
+ return _SizeB1024_name_1
+ case i == 1048576:
+ return _SizeB1024_name_2
+ case i == 1073741824:
+ return _SizeB1024_name_3
+ case i == 1099511627776:
+ return _SizeB1024_name_4
+ default:
+ return "SizeB1024(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/speed.go b/vendor/github.com/vbauerster/mpb/v6/decor/speed.go
new file mode 100644
index 000000000..634edabfd
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/speed.go
@@ -0,0 +1,171 @@
+package decor
+
+import (
+ "fmt"
+ "io"
+ "math"
+ "time"
+
+ "github.com/VividCortex/ewma"
+)
+
+// FmtAsSpeed adds "/s" to the end of the input formatter. To be
+// used with SizeB1000 or SizeB1024 types, for example:
+//
+// fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048)))
+//
+func FmtAsSpeed(input fmt.Formatter) fmt.Formatter {
+ return &speedFormatter{input}
+}
+
+type speedFormatter struct {
+ fmt.Formatter
+}
+
+func (self *speedFormatter) Format(st fmt.State, verb rune) {
+ self.Formatter.Format(st, verb)
+ io.WriteString(st, "/s")
+}
+
+// EwmaSpeed exponential-weighted-moving-average based speed decorator.
+// For this decorator to work correctly you have to measure each
+// iteration's duration and pass it to the
+// *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
+func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
+ var average ewma.MovingAverage
+ if age == 0 {
+ average = ewma.NewMovingAverage()
+ } else {
+ average = ewma.NewMovingAverage(age)
+ }
+ return MovingAverageSpeed(unit, format, NewThreadSafeMovingAverage(average), wcc...)
+}
+
+// MovingAverageSpeed decorator relies on MovingAverage implementation
+// to calculate its average.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `format` printf compatible verb for value, like "%f" or "%d"
+//
+// `average` MovingAverage implementation
+//
+// `wcc` optional WC config
+//
+// format examples:
+//
+// unit=UnitKiB, format="%.1f" output: "1.0MiB/s"
+// unit=UnitKiB, format="% .1f" output: "1.0 MiB/s"
+// unit=UnitKB, format="%.1f" output: "1.0MB/s"
+// unit=UnitKB, format="% .1f" output: "1.0 MB/s"
+//
+func MovingAverageSpeed(unit int, format string, average ewma.MovingAverage, wcc ...WC) Decorator {
+ if format == "" {
+ format = "%.0f"
+ }
+ d := &movingAverageSpeed{
+ WC: initWC(wcc...),
+ average: average,
+ producer: chooseSpeedProducer(unit, format),
+ }
+ return d
+}
+
+type movingAverageSpeed struct {
+ WC
+ producer func(float64) string
+ average ewma.MovingAverage
+ msg string
+}
+
+func (d *movingAverageSpeed) Decor(s Statistics) string {
+ if !s.Completed {
+ var speed float64
+ if v := d.average.Value(); v > 0 {
+ speed = 1 / v
+ }
+ d.msg = d.producer(speed * 1e9)
+ }
+ return d.FormatMsg(d.msg)
+}
+
+func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) {
+ durPerByte := float64(dur) / float64(n)
+ if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) {
+ return
+ }
+ d.average.Add(durPerByte)
+}
+
+// AverageSpeed decorator with dynamic unit measure adjustment. It's
+// a wrapper of NewAverageSpeed.
+func AverageSpeed(unit int, format string, wcc ...WC) Decorator {
+ return NewAverageSpeed(unit, format, time.Now(), wcc...)
+}
+
+// NewAverageSpeed decorator with dynamic unit measure adjustment and
+// user provided start time.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `format` printf compatible verb for value, like "%f" or "%d"
+//
+// `startTime` start time
+//
+// `wcc` optional WC config
+//
+// format examples:
+//
+// unit=UnitKiB, format="%.1f" output: "1.0MiB/s"
+// unit=UnitKiB, format="% .1f" output: "1.0 MiB/s"
+// unit=UnitKB, format="%.1f" output: "1.0MB/s"
+// unit=UnitKB, format="% .1f" output: "1.0 MB/s"
+//
+func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator {
+ if format == "" {
+ format = "%.0f"
+ }
+ d := &averageSpeed{
+ WC: initWC(wcc...),
+ startTime: startTime,
+ producer: chooseSpeedProducer(unit, format),
+ }
+ return d
+}
+
+type averageSpeed struct {
+ WC
+ startTime time.Time
+ producer func(float64) string
+ msg string
+}
+
+func (d *averageSpeed) Decor(s Statistics) string {
+ if !s.Completed {
+ speed := float64(s.Current) / float64(time.Since(d.startTime))
+ d.msg = d.producer(speed * 1e9)
+ }
+
+ return d.FormatMsg(d.msg)
+}
+
+func (d *averageSpeed) AverageAdjust(startTime time.Time) {
+ d.startTime = startTime
+}
+
+func chooseSpeedProducer(unit int, format string) func(float64) string {
+ switch unit {
+ case UnitKiB:
+ return func(speed float64) string {
+ return fmt.Sprintf(format, FmtAsSpeed(SizeB1024(math.Round(speed))))
+ }
+ case UnitKB:
+ return func(speed float64) string {
+ return fmt.Sprintf(format, FmtAsSpeed(SizeB1000(math.Round(speed))))
+ }
+ default:
+ return func(speed float64) string {
+ return fmt.Sprintf(format, speed)
+ }
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/decor/spinner.go b/vendor/github.com/vbauerster/mpb/v6/decor/spinner.go
new file mode 100644
index 000000000..6871639db
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/decor/spinner.go
@@ -0,0 +1,21 @@
+package decor
+
+var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
+
+// Spinner returns spinner decorator.
+//
+// `frames` spinner frames, if nil or len==0, default is used
+//
+// `wcc` optional WC config
+func Spinner(frames []string, wcc ...WC) Decorator {
+ if len(frames) == 0 {
+ frames = defaultSpinnerStyle
+ }
+ var count uint
+ f := func(s Statistics) string {
+ frame := frames[count%uint(len(frames))]
+ count++
+ return frame
+ }
+ return Any(f, wcc...)
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/doc.go b/vendor/github.com/vbauerster/mpb/v6/doc.go
new file mode 100644
index 000000000..5ada71774
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/doc.go
@@ -0,0 +1,2 @@
+// Package mpb is a library for rendering progress bars in terminal applications.
+package mpb
diff --git a/vendor/github.com/vbauerster/mpb/v6/go.mod b/vendor/github.com/vbauerster/mpb/v6/go.mod
new file mode 100644
index 000000000..3a3d337d2
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/go.mod
@@ -0,0 +1,11 @@
+module github.com/vbauerster/mpb/v6
+
+require (
+ github.com/VividCortex/ewma v1.1.1
+ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
+ github.com/mattn/go-runewidth v0.0.10
+ github.com/rivo/uniseg v0.2.0
+ golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
+)
+
+go 1.14
diff --git a/vendor/github.com/vbauerster/mpb/v6/go.sum b/vendor/github.com/vbauerster/mpb/v6/go.sum
new file mode 100644
index 000000000..1c20f8921
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/go.sum
@@ -0,0 +1,11 @@
+github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
+github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
+github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
+github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
+github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
+github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
+github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
+golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/vendor/github.com/vbauerster/mpb/v6/internal/percentage.go b/vendor/github.com/vbauerster/mpb/v6/internal/percentage.go
new file mode 100644
index 000000000..a8ef8be12
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/internal/percentage.go
@@ -0,0 +1,19 @@
+package internal
+
+import "math"
+
+// Percentage is a helper function, to calculate percentage.
+func Percentage(total, current int64, width int) float64 {
+ if total <= 0 {
+ return 0
+ }
+ if current >= total {
+ return float64(width)
+ }
+ return float64(int64(width)*current) / float64(total)
+}
+
+// PercentageRound same as Percentage but with math.Round.
+func PercentageRound(total, current int64, width int) float64 {
+ return math.Round(Percentage(total, current, width))
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/internal/predicate.go b/vendor/github.com/vbauerster/mpb/v6/internal/predicate.go
new file mode 100644
index 000000000..1e4dd24d9
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/internal/predicate.go
@@ -0,0 +1,6 @@
+package internal
+
+// Predicate helper for internal use.
+func Predicate(pick bool) func() bool {
+ return func() bool { return pick }
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/internal/width.go b/vendor/github.com/vbauerster/mpb/v6/internal/width.go
new file mode 100644
index 000000000..216320f24
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/internal/width.go
@@ -0,0 +1,10 @@
+package internal
+
+// CheckRequestedWidth checks that requested width doesn't overflow
+// available width
+func CheckRequestedWidth(requested, available int) int {
+ if requested <= 0 || requested >= available {
+ return available
+ }
+ return requested
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/priority_queue.go b/vendor/github.com/vbauerster/mpb/v6/priority_queue.go
new file mode 100644
index 000000000..29d9bd5a8
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/priority_queue.go
@@ -0,0 +1,32 @@
+package mpb
+
+// A priorityQueue implements heap.Interface
+type priorityQueue []*Bar
+
+func (pq priorityQueue) Len() int { return len(pq) }
+
+func (pq priorityQueue) Less(i, j int) bool {
+ return pq[i].priority < pq[j].priority
+}
+
+func (pq priorityQueue) Swap(i, j int) {
+ pq[i], pq[j] = pq[j], pq[i]
+ pq[i].index = i
+ pq[j].index = j
+}
+
+func (pq *priorityQueue) Push(x interface{}) {
+ s := *pq
+ bar := x.(*Bar)
+ bar.index = len(s)
+ s = append(s, bar)
+ *pq = s
+}
+
+func (pq *priorityQueue) Pop() interface{} {
+ s := *pq
+ *pq = s[0 : len(s)-1]
+ bar := s[len(s)-1]
+ bar.index = -1 // for safety
+ return bar
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/progress.go b/vendor/github.com/vbauerster/mpb/v6/progress.go
new file mode 100644
index 000000000..5a3f96243
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/progress.go
@@ -0,0 +1,412 @@
+package mpb
+
+import (
+ "bytes"
+ "container/heap"
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "math"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/vbauerster/mpb/v6/cwriter"
+ "github.com/vbauerster/mpb/v6/decor"
+)
+
+const (
+ // default RefreshRate
+ prr = 120 * time.Millisecond
+)
+
+// Progress represents a container that renders one or more progress
+// bars.
+type Progress struct {
+ ctx context.Context
+ uwg *sync.WaitGroup
+ cwg *sync.WaitGroup
+ bwg *sync.WaitGroup
+ operateState chan func(*pState)
+ done chan struct{}
+ refreshCh chan time.Time
+ once sync.Once
+ dlogger *log.Logger
+}
+
+// pState holds bars in its priorityQueue. It gets passed to
+// *Progress.serve(...) monitor goroutine.
+type pState struct {
+ bHeap priorityQueue
+ heapUpdated bool
+ pMatrix map[int][]chan int
+ aMatrix map[int][]chan int
+ barShutdownQueue []*Bar
+
+ // following are provided/overrided by user
+ idCount int
+ reqWidth int
+ popCompleted bool
+ outputDiscarded bool
+ rr time.Duration
+ uwg *sync.WaitGroup
+ externalRefresh <-chan interface{}
+ renderDelay <-chan struct{}
+ shutdownNotifier chan struct{}
+ parkedBars map[*Bar]*Bar
+ output io.Writer
+ debugOut io.Writer
+}
+
+// New creates new Progress container instance. It's not possible to
+// reuse instance after *Progress.Wait() method has been called.
+func New(options ...ContainerOption) *Progress {
+ return NewWithContext(context.Background(), options...)
+}
+
+// NewWithContext creates new Progress container instance with provided
+// context. It's not possible to reuse instance after *Progress.Wait()
+// method has been called.
+func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
+ s := &pState{
+ bHeap: priorityQueue{},
+ rr: prr,
+ parkedBars: make(map[*Bar]*Bar),
+ output: os.Stdout,
+ debugOut: ioutil.Discard,
+ }
+
+ for _, opt := range options {
+ if opt != nil {
+ opt(s)
+ }
+ }
+
+ p := &Progress{
+ ctx: ctx,
+ uwg: s.uwg,
+ cwg: new(sync.WaitGroup),
+ bwg: new(sync.WaitGroup),
+ operateState: make(chan func(*pState)),
+ done: make(chan struct{}),
+ dlogger: log.New(s.debugOut, "[mpb] ", log.Lshortfile),
+ }
+
+ p.cwg.Add(1)
+ go p.serve(s, cwriter.New(s.output))
+ return p
+}
+
+// AddBar creates a bar with default bar filler. Different filler can
+// be choosen and applied via `*Progress.Add(...) *Bar` method.
+func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
+ return p.Add(total, NewBarFiller(BarDefaultStyle), options...)
+}
+
+// AddSpinner creates a bar with default spinner filler. Different
+// filler can be choosen and applied via `*Progress.Add(...) *Bar`
+// method.
+func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
+ return p.Add(total, NewSpinnerFiller(SpinnerDefaultStyle, alignment), options...)
+}
+
+// Add creates a bar which renders itself by provided filler.
+// If `total <= 0` trigger complete event is disabled until reset with *bar.SetTotal(int64, bool).
+// Panics if *Progress instance is done, i.e. called after *Progress.Wait().
+func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
+ if filler == nil {
+ filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
+ }
+ p.bwg.Add(1)
+ result := make(chan *Bar)
+ select {
+ case p.operateState <- func(ps *pState) {
+ bs := ps.makeBarState(total, filler, options...)
+ bar := newBar(p, bs)
+ if bs.runningBar != nil {
+ bs.runningBar.noPop = true
+ ps.parkedBars[bs.runningBar] = bar
+ } else {
+ heap.Push(&ps.bHeap, bar)
+ ps.heapUpdated = true
+ }
+ ps.idCount++
+ result <- bar
+ }:
+ bar := <-result
+ bar.subscribeDecorators()
+ return bar
+ case <-p.done:
+ p.bwg.Done()
+ panic(fmt.Sprintf("%T instance can't be reused after it's done!", p))
+ }
+}
+
+func (p *Progress) dropBar(b *Bar) {
+ select {
+ case p.operateState <- func(s *pState) {
+ if b.index < 0 {
+ return
+ }
+ heap.Remove(&s.bHeap, b.index)
+ s.heapUpdated = true
+ }:
+ case <-p.done:
+ }
+}
+
+func (p *Progress) setBarPriority(b *Bar, priority int) {
+ select {
+ case p.operateState <- func(s *pState) {
+ if b.index < 0 {
+ return
+ }
+ b.priority = priority
+ heap.Fix(&s.bHeap, b.index)
+ }:
+ case <-p.done:
+ }
+}
+
+// UpdateBarPriority same as *Bar.SetPriority(int).
+func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
+ p.setBarPriority(b, priority)
+}
+
+// BarCount returns bars count.
+func (p *Progress) BarCount() int {
+ result := make(chan int, 1)
+ select {
+ case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
+ return <-result
+ case <-p.done:
+ return 0
+ }
+}
+
+// Wait waits for all bars to complete and finally shutdowns container.
+// After this method has been called, there is no way to reuse *Progress
+// instance.
+func (p *Progress) Wait() {
+ if p.uwg != nil {
+ // wait for user wg
+ p.uwg.Wait()
+ }
+
+ // wait for bars to quit, if any
+ p.bwg.Wait()
+
+ p.once.Do(p.shutdown)
+
+ // wait for container to quit
+ p.cwg.Wait()
+}
+
+func (p *Progress) shutdown() {
+ close(p.done)
+}
+
+func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
+ defer p.cwg.Done()
+
+ p.refreshCh = s.newTicker(p.done)
+
+ for {
+ select {
+ case op := <-p.operateState:
+ op(s)
+ case <-p.refreshCh:
+ if err := s.render(cw); err != nil {
+ p.dlogger.Println(err)
+ }
+ case <-s.shutdownNotifier:
+ if s.heapUpdated {
+ if err := s.render(cw); err != nil {
+ p.dlogger.Println(err)
+ }
+ }
+ return
+ }
+ }
+}
+
+func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
+ ch := make(chan time.Time)
+ if s.shutdownNotifier == nil {
+ s.shutdownNotifier = make(chan struct{})
+ }
+ go func() {
+ if s.renderDelay != nil {
+ <-s.renderDelay
+ }
+ var internalRefresh <-chan time.Time
+ if !s.outputDiscarded {
+ if s.externalRefresh == nil {
+ ticker := time.NewTicker(s.rr)
+ defer ticker.Stop()
+ internalRefresh = ticker.C
+ }
+ } else {
+ s.externalRefresh = nil
+ }
+ for {
+ select {
+ case t := <-internalRefresh:
+ ch <- t
+ case x := <-s.externalRefresh:
+ if t, ok := x.(time.Time); ok {
+ ch <- t
+ } else {
+ ch <- time.Now()
+ }
+ case <-done:
+ close(s.shutdownNotifier)
+ return
+ }
+ }
+ }()
+ return ch
+}
+
+func (s *pState) render(cw *cwriter.Writer) error {
+ if s.heapUpdated {
+ s.updateSyncMatrix()
+ s.heapUpdated = false
+ }
+ syncWidth(s.pMatrix)
+ syncWidth(s.aMatrix)
+
+ tw, err := cw.GetWidth()
+ if err != nil {
+ tw = s.reqWidth
+ }
+ for i := 0; i < s.bHeap.Len(); i++ {
+ bar := s.bHeap[i]
+ go bar.render(tw)
+ }
+
+ return s.flush(cw)
+}
+
+func (s *pState) flush(cw *cwriter.Writer) error {
+ var lineCount int
+ bm := make(map[*Bar]struct{}, s.bHeap.Len())
+ for s.bHeap.Len() > 0 {
+ b := heap.Pop(&s.bHeap).(*Bar)
+ cw.ReadFrom(<-b.frameCh)
+ if b.toShutdown {
+ if b.recoveredPanic != nil {
+ s.barShutdownQueue = append(s.barShutdownQueue, b)
+ b.toShutdown = false
+ } else {
+ // shutdown at next flush
+ // this ensures no bar ends up with less than 100% rendered
+ defer func() {
+ s.barShutdownQueue = append(s.barShutdownQueue, b)
+ }()
+ }
+ }
+ lineCount += b.extendedLines + 1
+ bm[b] = struct{}{}
+ }
+
+ for _, b := range s.barShutdownQueue {
+ if parkedBar := s.parkedBars[b]; parkedBar != nil {
+ parkedBar.priority = b.priority
+ heap.Push(&s.bHeap, parkedBar)
+ delete(s.parkedBars, b)
+ b.toDrop = true
+ }
+ if s.popCompleted && !b.noPop {
+ lineCount -= b.extendedLines + 1
+ b.toDrop = true
+ }
+ if b.toDrop {
+ delete(bm, b)
+ s.heapUpdated = true
+ }
+ b.cancel()
+ }
+ s.barShutdownQueue = s.barShutdownQueue[0:0]
+
+ for b := range bm {
+ heap.Push(&s.bHeap, b)
+ }
+
+ return cw.Flush(lineCount)
+}
+
+func (s *pState) updateSyncMatrix() {
+ s.pMatrix = make(map[int][]chan int)
+ s.aMatrix = make(map[int][]chan int)
+ for i := 0; i < s.bHeap.Len(); i++ {
+ bar := s.bHeap[i]
+ table := bar.wSyncTable()
+ pRow, aRow := table[0], table[1]
+
+ for i, ch := range pRow {
+ s.pMatrix[i] = append(s.pMatrix[i], ch)
+ }
+
+ for i, ch := range aRow {
+ s.aMatrix[i] = append(s.aMatrix[i], ch)
+ }
+ }
+}
+
+func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
+ bs := &bState{
+ id: s.idCount,
+ priority: s.idCount,
+ reqWidth: s.reqWidth,
+ total: total,
+ filler: filler,
+ extender: func(r io.Reader, _ int, _ decor.Statistics) (io.Reader, int) { return r, 0 },
+ debugOut: s.debugOut,
+ }
+
+ if total > 0 {
+ bs.triggerComplete = true
+ }
+
+ for _, opt := range options {
+ if opt != nil {
+ opt(bs)
+ }
+ }
+
+ if bs.middleware != nil {
+ bs.filler = bs.middleware(filler)
+ bs.middleware = nil
+ }
+
+ if s.popCompleted && !bs.noPop {
+ bs.priority = -(math.MaxInt32 - s.idCount)
+ }
+
+ bs.bufP = bytes.NewBuffer(make([]byte, 0, 128))
+ bs.bufB = bytes.NewBuffer(make([]byte, 0, 256))
+ bs.bufA = bytes.NewBuffer(make([]byte, 0, 128))
+
+ return bs
+}
+
+func syncWidth(matrix map[int][]chan int) {
+ for _, column := range matrix {
+ go maxWidthDistributor(column)
+ }
+}
+
+var maxWidthDistributor = func(column []chan int) {
+ var maxWidth int
+ for _, ch := range column {
+ if w := <-ch; w > maxWidth {
+ maxWidth = w
+ }
+ }
+ for _, ch := range column {
+ ch <- maxWidth
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/v6/proxyreader.go b/vendor/github.com/vbauerster/mpb/v6/proxyreader.go
new file mode 100644
index 000000000..316f438d7
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v6/proxyreader.go
@@ -0,0 +1,90 @@
+package mpb
+
+import (
+ "io"
+ "io/ioutil"
+ "time"
+)
+
+type proxyReader struct {
+ io.ReadCloser
+ bar *Bar
+}
+
+func (x *proxyReader) Read(p []byte) (int, error) {
+ n, err := x.ReadCloser.Read(p)
+ x.bar.IncrBy(n)
+ if err == io.EOF {
+ go x.bar.SetTotal(0, true)
+ }
+ return n, err
+}
+
+type proxyWriterTo struct {
+ io.ReadCloser // *proxyReader
+ wt io.WriterTo
+ bar *Bar
+}
+
+func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
+ n, err := x.wt.WriteTo(w)
+ x.bar.IncrInt64(n)
+ if err == io.EOF {
+ go x.bar.SetTotal(0, true)
+ }
+ return n, err
+}
+
+type ewmaProxyReader struct {
+ io.ReadCloser // *proxyReader
+ bar *Bar
+ iT time.Time
+}
+
+func (x *ewmaProxyReader) Read(p []byte) (int, error) {
+ n, err := x.ReadCloser.Read(p)
+ if n > 0 {
+ x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
+ x.iT = time.Now()
+ }
+ return n, err
+}
+
+type ewmaProxyWriterTo struct {
+ io.ReadCloser // *ewmaProxyReader
+ wt io.WriterTo // *proxyWriterTo
+ bar *Bar
+ iT time.Time
+}
+
+func (x *ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
+ n, err := x.wt.WriteTo(w)
+ if n > 0 {
+ x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
+ x.iT = time.Now()
+ }
+ return n, err
+}
+
+func newProxyReader(r io.Reader, bar *Bar) io.ReadCloser {
+ rc := toReadCloser(r)
+ rc = &proxyReader{rc, bar}
+
+ if wt, isWriterTo := r.(io.WriterTo); bar.hasEwmaDecorators {
+ now := time.Now()
+ rc = &ewmaProxyReader{rc, bar, now}
+ if isWriterTo {
+ rc = &ewmaProxyWriterTo{rc, wt, bar, now}
+ }
+ } else if isWriterTo {
+ rc = &proxyWriterTo{rc, wt, bar}
+ }
+ return rc
+}
+
+func toReadCloser(r io.Reader) io.ReadCloser {
+ if rc, ok := r.(io.ReadCloser); ok {
+ return rc
+ }
+ return ioutil.NopCloser(r)
+}