Use github.com/vbauerster/mpb/v7 in pkg/machine

We already use v7 in c/image so podman should use the same version to
prevent duplication.

This saves 170 KB binary size.

[NO NEW TESTS NEEDED]

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger 2022-03-09 19:14:24 +01:00
parent 418ab2e5e1
commit 30bf065c3f
No known key found for this signature in database
GPG key ID: EB145DD938A3CAF2
46 changed files with 4 additions and 3394 deletions

2
go.mod
View file

@ -61,7 +61,7 @@ require (
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/ulikunitz/xz v0.5.10
github.com/vbauerster/mpb/v6 v6.0.4
github.com/vbauerster/mpb/v7 v7.3.2
github.com/vishvananda/netlink v1.1.1-0.20220115184804-dd687eb2f2d4
go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce

4
go.sum
View file

@ -941,7 +941,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
@ -1189,7 +1188,6 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQ
github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc=
github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50=
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
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=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@ -1349,8 +1347,6 @@ github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/V
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
github.com/vbauerster/mpb/v6 v6.0.4 h1:h6J5zM/2wimP5Hj00unQuV8qbo5EPcj6wbkCqgj7KcY=
github.com/vbauerster/mpb/v6 v6.0.4/go.mod h1:a/+JT57gqh6Du0Ay5jSR+uBMfXGdlR7VQlGP52fJxLM=
github.com/vbauerster/mpb/v7 v7.3.2 h1:tCuxMy8G9cLdjb61b6wO7I1vRT/LyMEzRbr3xCC0JPU=
github.com/vbauerster/mpb/v7 v7.3.2/go.mod h1:wfxIZcOJq/bG1/lAtfzMXcOiSvbqVi/5GX5WCSi+IsA=
github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE=

View file

@ -19,8 +19,8 @@ import (
"github.com/containers/storage/pkg/archive"
"github.com/sirupsen/logrus"
"github.com/ulikunitz/xz"
"github.com/vbauerster/mpb/v6"
"github.com/vbauerster/mpb/v6/decor"
"github.com/vbauerster/mpb/v7"
"github.com/vbauerster/mpb/v7/decor"
)
// GenericDownload is used when a user provides a URL

View file

@ -1,5 +0,0 @@
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

View file

@ -1,11 +0,0 @@
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

View file

@ -1,120 +0,0 @@
# 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)

View file

@ -1,24 +0,0 @@
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/>

View file

@ -1,492 +0,0 @@
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
}
}

View file

@ -1,31 +0,0 @@
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)
}

View file

@ -1,191 +0,0 @@
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, len(BarDefaultStyle))
srcRwidth := make([]int, len(BarDefaultStyle))
i := 0
for gr := uniseg.NewGraphemes(style); i < len(BarDefaultStyle) && gr.Next(); i++ {
srcFormat[i] = gr.Bytes()
srcRwidth[i] = runewidth.StringWidth(gr.Str())
}
copy(s.format, srcFormat[:i])
copy(s.rwidth, srcRwidth[:i])
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])
}
}

View file

@ -1,65 +0,0 @@
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++
}

View file

@ -1,153 +0,0 @@
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
}

View file

@ -1,112 +0,0 @@
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
}

View file

@ -1,2 +0,0 @@
// Package cwriter is a console writer abstraction for the underlying OS.
package cwriter

View file

@ -1,7 +0,0 @@
// +build darwin dragonfly freebsd netbsd openbsd
package cwriter
import "golang.org/x/sys/unix"
const ioctlReadTermios = unix.TIOCGETA

View file

@ -1,7 +0,0 @@
// +build aix linux
package cwriter
import "golang.org/x/sys/unix"
const ioctlReadTermios = unix.TCGETS

View file

@ -1,7 +0,0 @@
// +build solaris
package cwriter
import "golang.org/x/sys/unix"
const ioctlReadTermios = unix.TCGETA

View file

@ -1,84 +0,0 @@
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
}

View file

@ -1,26 +0,0 @@
// +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
}

View file

@ -1,73 +0,0 @@
// +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
}

View file

@ -1,21 +0,0 @@
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))
}

View file

@ -1,243 +0,0 @@
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...)
}

View file

@ -1,191 +0,0 @@
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()
}

View file

@ -1,20 +0,0 @@
// 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

View file

@ -1,35 +0,0 @@
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...)
}

View file

@ -1,203 +0,0 @@
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()
}
}
}

View file

@ -1,107 +0,0 @@
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 ""
}

View file

@ -1,68 +0,0 @@
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))
}

View file

@ -1,12 +0,0 @@
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...)
}

View file

@ -1,37 +0,0 @@
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
}

View file

@ -1,58 +0,0 @@
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...)
}

View file

@ -1,109 +0,0 @@
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())
}

View file

@ -1,41 +0,0 @@
// 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) + ")"
}
}

View file

@ -1,41 +0,0 @@
// 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) + ")"
}
}

View file

@ -1,171 +0,0 @@
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)
}
}
}

View file

@ -1,21 +0,0 @@
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...)
}

View file

@ -1,2 +0,0 @@
// Package mpb is a library for rendering progress bars in terminal applications.
package mpb

View file

@ -1,11 +0,0 @@
module github.com/vbauerster/mpb/v6
require (
github.com/VividCortex/ewma v1.2.0
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/mattn/go-runewidth v0.0.12
github.com/rivo/uniseg v0.2.0
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015
)
go 1.14

View file

@ -1,11 +0,0 @@
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
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.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
github.com/mattn/go-runewidth v0.0.12/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-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -1,19 +0,0 @@
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))
}

View file

@ -1,6 +0,0 @@
package internal
// Predicate helper for internal use.
func Predicate(pick bool) func() bool {
return func() bool { return pick }
}

View file

@ -1,10 +0,0 @@
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
}

View file

@ -1,32 +0,0 @@
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
}

View file

@ -1,412 +0,0 @@
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
}
}

View file

@ -1,90 +0,0 @@
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)
}

7
vendor/modules.txt vendored
View file

@ -683,13 +683,8 @@ github.com/ulikunitz/xz/lzma
github.com/vbatts/tar-split/archive/tar
github.com/vbatts/tar-split/tar/asm
github.com/vbatts/tar-split/tar/storage
# github.com/vbauerster/mpb/v6 v6.0.4
## explicit
github.com/vbauerster/mpb/v6
github.com/vbauerster/mpb/v6/cwriter
github.com/vbauerster/mpb/v6/decor
github.com/vbauerster/mpb/v6/internal
# github.com/vbauerster/mpb/v7 v7.3.2
## explicit
github.com/vbauerster/mpb/v7
github.com/vbauerster/mpb/v7/cwriter
github.com/vbauerster/mpb/v7/decor