summaryrefslogtreecommitdiff
path: root/vendor/github.com/vbauerster/mpb
diff options
context:
space:
mode:
authorValentin Rothberg <rothberg@redhat.com>2019-06-24 21:29:31 +0200
committerValentin Rothberg <rothberg@redhat.com>2019-06-24 21:29:31 +0200
commit2388222e98462fdbbe44f3e091b2b79d80956a9a (patch)
tree17078d861c20a3e48b19c750c6864c5f59248386 /vendor/github.com/vbauerster/mpb
parenta1a4a75abee2c381483a218e1660621ee416ef7c (diff)
downloadpodman-2388222e98462fdbbe44f3e091b2b79d80956a9a.tar.gz
podman-2388222e98462fdbbe44f3e091b2b79d80956a9a.tar.bz2
podman-2388222e98462fdbbe44f3e091b2b79d80956a9a.zip
update dependencies
Ran a `go get -u` and bumped K8s deps to 1.15.0. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'vendor/github.com/vbauerster/mpb')
-rw-r--r--vendor/github.com/vbauerster/mpb/.travis.yml4
-rw-r--r--vendor/github.com/vbauerster/mpb/README.md4
-rw-r--r--vendor/github.com/vbauerster/mpb/bar.go186
-rw-r--r--vendor/github.com/vbauerster/mpb/bar_filler.go111
-rw-r--r--vendor/github.com/vbauerster/mpb/bar_option.go151
-rw-r--r--vendor/github.com/vbauerster/mpb/cwriter/writer.go8
-rw-r--r--vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go2
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/counters.go6
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/decorator.go30
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/elapsed.go4
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/eta.go15
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/moving-average.go9
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/speed.go3
-rw-r--r--vendor/github.com/vbauerster/mpb/internal/percentage.go4
-rw-r--r--vendor/github.com/vbauerster/mpb/internal/round.go49
-rw-r--r--vendor/github.com/vbauerster/mpb/options.go43
-rw-r--r--vendor/github.com/vbauerster/mpb/options_go1.7.go15
-rw-r--r--vendor/github.com/vbauerster/mpb/progress.go52
-rw-r--r--vendor/github.com/vbauerster/mpb/spinner_filler.go48
19 files changed, 439 insertions, 305 deletions
diff --git a/vendor/github.com/vbauerster/mpb/.travis.yml b/vendor/github.com/vbauerster/mpb/.travis.yml
index a7fd7763a..c982d1f90 100644
--- a/vendor/github.com/vbauerster/mpb/.travis.yml
+++ b/vendor/github.com/vbauerster/mpb/.travis.yml
@@ -1,8 +1,8 @@
language: go
sudo: false
go:
- - 1.8.x
- - 1.9.x
+ - 1.10.x
+ - tip
before_install:
- go get -t -v ./...
diff --git a/vendor/github.com/vbauerster/mpb/README.md b/vendor/github.com/vbauerster/mpb/README.md
index 9b760647e..f96857c47 100644
--- a/vendor/github.com/vbauerster/mpb/README.md
+++ b/vendor/github.com/vbauerster/mpb/README.md
@@ -31,8 +31,6 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is
p := mpb.New(
// override default (80) width
mpb.WithWidth(64),
- // override default "[=>-]" format
- mpb.WithFormat("╢▌▌░╟"),
// override default 120ms refresh rate
mpb.WithRefreshRate(180*time.Millisecond),
)
@@ -41,6 +39,8 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is
name := "Single Bar:"
// adding a single bar
bar := p.AddBar(int64(total),
+ // override default "[=>-]" style
+ mpb.BarStyle("╢▌▌░╟"),
mpb.PrependDecorators(
// display our name with one space on the right
decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
diff --git a/vendor/github.com/vbauerster/mpb/bar.go b/vendor/github.com/vbauerster/mpb/bar.go
index 5a506fc84..a304a87cb 100644
--- a/vendor/github.com/vbauerster/mpb/bar.go
+++ b/vendor/github.com/vbauerster/mpb/bar.go
@@ -2,6 +2,7 @@ package mpb
import (
"bytes"
+ "context"
"fmt"
"io"
"io/ioutil"
@@ -11,21 +12,8 @@ import (
"unicode/utf8"
"github.com/vbauerster/mpb/decor"
- "github.com/vbauerster/mpb/internal"
)
-const (
- rLeft = iota
- rFill
- rTip
- rEmpty
- rRight
-)
-
-const formatLen = 5
-
-type barRunes [formatLen]rune
-
// Bar represents a progress Bar
type Bar struct {
priority int
@@ -45,15 +33,30 @@ type Bar struct {
shutdown chan struct{}
}
+// Filler interface.
+// Bar renders by calling Filler's Fill method. You can literally have
+// any bar kind, by implementing this interface and passing it to the
+// Add method.
+type Filler interface {
+ Fill(w io.Writer, width int, s *decor.Statistics)
+}
+
+// FillerFunc is function type adapter to convert function into Filler.
+type FillerFunc func(w io.Writer, width int, stat *decor.Statistics)
+
+func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
+ f(w, width, stat)
+}
+
type (
bState struct {
+ filler Filler
id int
width int
+ alignment int
total int64
current int64
- runes barRunes
- trimLeftSpace bool
- trimRightSpace bool
+ trimSpace bool
toComplete bool
removeOnComplete bool
barClearOnComplete bool
@@ -73,8 +76,8 @@ type (
runningBar *Bar
}
refill struct {
- char rune
- till int64
+ r rune
+ limit int64
}
frameReader struct {
io.Reader
@@ -84,14 +87,20 @@ type (
}
)
-func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar {
- if total <= 0 {
- total = time.Now().Unix()
- }
+func newBar(
+ ctx context.Context,
+ wg *sync.WaitGroup,
+ filler Filler,
+ id, width int,
+ total int64,
+ options ...BarOption,
+) *Bar {
s := &bState{
+ filler: filler,
id: id,
priority: id,
+ width: width,
total: total,
}
@@ -104,6 +113,9 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt
s.bufP = bytes.NewBuffer(make([]byte, 0, s.width))
s.bufB = bytes.NewBuffer(make([]byte, 0, s.width))
s.bufA = bytes.NewBuffer(make([]byte, 0, s.width))
+ if s.newLineExtendFn != nil {
+ s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
+ }
b := &Bar{
priority: s.priority,
@@ -121,11 +133,7 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt
b.priority = b.runningBar.priority
}
- if s.newLineExtendFn != nil {
- s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
- }
-
- go b.serve(wg, s, cancel)
+ go b.serve(ctx, wg, s)
return b
}
@@ -178,52 +186,42 @@ func (b *Bar) Current() int64 {
}
// SetTotal sets total dynamically.
-// Set final to true, when total is known, it will trigger bar complete event.
-func (b *Bar) SetTotal(total int64, final bool) bool {
+// Set complete to true, to trigger bar complete event now.
+func (b *Bar) SetTotal(total int64, complete bool) {
select {
case b.operateState <- func(s *bState) {
- if total > 0 {
- s.total = total
- }
- if final {
+ s.total = total
+ if complete && !s.toComplete {
s.current = s.total
s.toComplete = true
}
}:
- return true
case <-b.done:
- return false
}
}
-// SetRefill sets fill rune to r, up until n.
-func (b *Bar) SetRefill(n int, r rune) {
- if n <= 0 {
- return
- }
+// SetRefill sets refill, if supported by underlying Filler.
+func (b *Bar) SetRefill(amount int64) {
b.operateState <- func(s *bState) {
- s.refill = &refill{r, int64(n)}
+ if f, ok := s.filler.(interface{ SetRefill(int64) }); ok {
+ f.SetRefill(amount)
+ }
}
}
-// RefillBy is deprecated, use SetRefill
-func (b *Bar) RefillBy(n int, r rune) {
- b.SetRefill(n, r)
-}
-
// Increment is a shorthand for b.IncrBy(1).
func (b *Bar) Increment() {
b.IncrBy(1)
}
// IncrBy increments progress bar by amount of n.
-// wdd is optional work duration i.e. time.Since(start),
-// which expected to be provided, if any ewma based decorator is used.
+// wdd is optional work duration i.e. time.Since(start), which expected
+// to be provided, if any ewma based decorator is used.
func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
select {
case b.operateState <- func(s *bState) {
s.current += int64(n)
- if s.current >= s.total {
+ if s.total > 0 && s.current >= s.total {
s.current = s.total
s.toComplete = true
}
@@ -238,9 +236,9 @@ func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
// Completed reports whether the bar is in completed state.
func (b *Bar) Completed() bool {
// omit select here, because primary usage of the method is for loop
- // condition, like for !bar.Completed() {...}
- // so when toComplete=true it is called once (at which time, the bar is still alive),
- // then quits the loop and never suppose to be called afterwards.
+ // condition, like for !bar.Completed() {...} so when toComplete=true
+ // it is called once (at which time, the bar is still alive), then
+ // quits the loop and never suppose to be called afterwards.
return <-b.boolCh
}
@@ -253,8 +251,9 @@ func (b *Bar) wSyncTable() [][]chan int {
}
}
-func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) {
+func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) {
defer wg.Done()
+ cancel := ctx.Done()
for {
select {
case op := <-b.operateState:
@@ -322,8 +321,6 @@ func (b *Bar) render(debugOut io.Writer, tw int) {
}
func (s *bState) draw(termWidth int) io.Reader {
- defer s.bufA.WriteByte('\n')
-
if s.panicMsg != "" {
return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
}
@@ -338,77 +335,32 @@ func (s *bState) draw(termWidth int) io.Reader {
s.bufA.WriteString(d.Decor(stat))
}
- prependCount := utf8.RuneCount(s.bufP.Bytes())
- appendCount := utf8.RuneCount(s.bufA.Bytes())
-
if s.barClearOnComplete && s.completeFlushed {
+ s.bufA.WriteByte('\n')
return io.MultiReader(s.bufP, s.bufA)
}
- s.fillBar(s.width)
- barCount := utf8.RuneCount(s.bufB.Bytes())
- totalCount := prependCount + barCount + appendCount
- if spaceCount := 0; totalCount > termWidth {
- if !s.trimLeftSpace {
- spaceCount++
- }
- if !s.trimRightSpace {
- spaceCount++
- }
- s.fillBar(termWidth - prependCount - appendCount - spaceCount)
- }
-
- return io.MultiReader(s.bufP, s.bufB, s.bufA)
-}
-
-func (s *bState) fillBar(width int) {
- defer func() {
- s.bufB.WriteRune(s.runes[rRight])
- if !s.trimRightSpace {
- s.bufB.WriteByte(' ')
- }
- }()
+ prependCount := utf8.RuneCount(s.bufP.Bytes())
+ appendCount := utf8.RuneCount(s.bufA.Bytes())
- s.bufB.Reset()
- if !s.trimLeftSpace {
+ if !s.trimSpace {
+ // reserve space for edge spaces
+ termWidth -= 2
s.bufB.WriteByte(' ')
}
- s.bufB.WriteRune(s.runes[rLeft])
- if width <= 2 {
- return
- }
-
- // bar s.width without leftEnd and rightEnd runes
- barWidth := width - 2
-
- completedWidth := internal.Percentage(s.total, s.current, int64(barWidth))
- if s.refill != nil {
- till := internal.Percentage(s.total, s.refill.till, int64(barWidth))
- // append refill rune
- var i int64
- for i = 0; i < till; i++ {
- s.bufB.WriteRune(s.refill.char)
- }
- for i = till; i < completedWidth; i++ {
- s.bufB.WriteRune(s.runes[rFill])
- }
+ if prependCount+s.width+appendCount > termWidth {
+ s.filler.Fill(s.bufB, termWidth-prependCount-appendCount, stat)
} else {
- var i int64
- for i = 0; i < completedWidth; i++ {
- s.bufB.WriteRune(s.runes[rFill])
- }
+ s.filler.Fill(s.bufB, s.width, stat)
}
- if completedWidth < int64(barWidth) && completedWidth > 0 {
- _, size := utf8.DecodeLastRune(s.bufB.Bytes())
- s.bufB.Truncate(s.bufB.Len() - size)
- s.bufB.WriteRune(s.runes[rTip])
+ if !s.trimSpace {
+ s.bufB.WriteByte(' ')
}
- for i := completedWidth; i < int64(barWidth); i++ {
- s.bufB.WriteRune(s.runes[rEmpty])
- }
+ s.bufA.WriteByte('\n')
+ return io.MultiReader(s.bufP, s.bufB, s.bufA)
}
func (s *bState) wSyncTable() [][]chan int {
@@ -442,14 +394,6 @@ func newStatistics(s *bState) *decor.Statistics {
}
}
-func strToBarRunes(format string) (array barRunes) {
- for i, n := 0, 0; len(format) > 0; i++ {
- array[i], n = utf8.DecodeRuneInString(format)
- format = format[n:]
- }
- return
-}
-
func countLines(b []byte) int {
return bytes.Count(b, []byte("\n"))
}
diff --git a/vendor/github.com/vbauerster/mpb/bar_filler.go b/vendor/github.com/vbauerster/mpb/bar_filler.go
new file mode 100644
index 000000000..4e9285ca5
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/bar_filler.go
@@ -0,0 +1,111 @@
+package mpb
+
+import (
+ "io"
+ "unicode/utf8"
+
+ "github.com/vbauerster/mpb/decor"
+ "github.com/vbauerster/mpb/internal"
+)
+
+const (
+ rLeft = iota
+ rFill
+ rTip
+ rEmpty
+ rRight
+ rRevTip
+ rRefill
+)
+
+var defaultBarStyle = "[=>-]<+"
+
+type barFiller struct {
+ format [][]byte
+ refillAmount int64
+ reverse bool
+}
+
+func newDefaultBarFiller() Filler {
+ bf := &barFiller{
+ format: make([][]byte, utf8.RuneCountInString(defaultBarStyle)),
+ }
+ bf.setStyle(defaultBarStyle)
+ return bf
+}
+
+func (s *barFiller) setStyle(style string) {
+ if !utf8.ValidString(style) {
+ return
+ }
+ src := make([][]byte, 0, utf8.RuneCountInString(style))
+ for _, r := range style {
+ src = append(src, []byte(string(r)))
+ }
+ copy(s.format, src)
+}
+
+func (s *barFiller) setReverse() {
+ s.reverse = true
+}
+
+func (s *barFiller) SetRefill(amount int64) {
+ s.refillAmount = amount
+}
+
+func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
+
+ // don't count rLeft and rRight [brackets]
+ width -= 2
+ if width < 2 {
+ return
+ }
+
+ w.Write(s.format[rLeft])
+ if width == 2 {
+ w.Write(s.format[rRight])
+ return
+ }
+
+ bb := make([][]byte, width)
+
+ cwidth := int(internal.Percentage(stat.Total, stat.Current, int64(width)))
+
+ for i := 0; i < cwidth; i++ {
+ bb[i] = s.format[rFill]
+ }
+
+ if s.refillAmount > 0 {
+ var rwidth int
+ if s.refillAmount > stat.Current {
+ rwidth = cwidth
+ } else {
+ rwidth = int(internal.Percentage(stat.Total, int64(s.refillAmount), int64(width)))
+ }
+ for i := 0; i < rwidth; i++ {
+ bb[i] = s.format[rRefill]
+ }
+ }
+
+ if cwidth > 0 && cwidth < width {
+ bb[cwidth-1] = s.format[rTip]
+ }
+
+ for i := cwidth; i < width; i++ {
+ bb[i] = s.format[rEmpty]
+ }
+
+ if s.reverse {
+ if cwidth > 0 && cwidth < width {
+ bb[cwidth-1] = s.format[rRevTip]
+ }
+ for i := len(bb) - 1; i >= 0; i-- {
+ w.Write(bb[i])
+ }
+ } else {
+ for i := 0; i < len(bb); i++ {
+ w.Write(bb[i])
+ }
+ }
+ w.Write(s.format[rRight])
+}
diff --git a/vendor/github.com/vbauerster/mpb/bar_option.go b/vendor/github.com/vbauerster/mpb/bar_option.go
index e33bce4da..e9a4bd2a7 100644
--- a/vendor/github.com/vbauerster/mpb/bar_option.go
+++ b/vendor/github.com/vbauerster/mpb/bar_option.go
@@ -6,11 +6,10 @@ import (
"github.com/vbauerster/mpb/decor"
)
-// BarOption is a function option which changes the default behavior of a bar,
-// if passed to p.AddBar(int64, ...BarOption)
+// BarOption is a function option which changes the default behavior of a bar.
type BarOption func(*bState)
-// AppendDecorators let you inject decorators to the bar's right side
+// AppendDecorators let you inject decorators to the bar's right side.
func AppendDecorators(appenders ...decor.Decorator) BarOption {
return func(s *bState) {
for _, decorator := range appenders {
@@ -25,7 +24,7 @@ func AppendDecorators(appenders ...decor.Decorator) BarOption {
}
}
-// PrependDecorators let you inject decorators to the bar's left side
+// PrependDecorators let you inject decorators to the bar's left side.
func PrependDecorators(prependers ...decor.Decorator) BarOption {
return func(s *bState) {
for _, decorator := range prependers {
@@ -40,85 +39,155 @@ func PrependDecorators(prependers ...decor.Decorator) BarOption {
}
}
-// BarTrimLeft trims left side space of the bar
-func BarTrimLeft() BarOption {
- return func(s *bState) {
- s.trimLeftSpace = true
- }
-}
-
-// BarTrimRight trims right space of the bar
-func BarTrimRight() BarOption {
- return func(s *bState) {
- s.trimRightSpace = true
- }
-}
-
-// BarTrim trims both left and right spaces of the bar
-func BarTrim() BarOption {
+// BarID sets bar id.
+func BarID(id int) BarOption {
return func(s *bState) {
- s.trimLeftSpace = true
- s.trimRightSpace = true
+ s.id = id
}
}
-// BarID overwrites internal bar id
-func BarID(id int) BarOption {
+// BarWidth sets bar width independent of the container.
+func BarWidth(width int) BarOption {
return func(s *bState) {
- s.id = id
+ s.width = width
}
}
-// BarRemoveOnComplete is a flag, if set whole bar line will be removed on complete event.
-// If both BarRemoveOnComplete and BarClearOnComplete are set, first bar section gets cleared
-// and then whole bar line gets removed completely.
+// BarRemoveOnComplete is a flag, if set whole bar line will be removed
+// on complete event. If both BarRemoveOnComplete and BarClearOnComplete
+// are set, first bar section gets cleared and then whole bar line
+// gets removed completely.
func BarRemoveOnComplete() BarOption {
return func(s *bState) {
s.removeOnComplete = true
}
}
-// BarReplaceOnComplete is indicator for delayed bar start, after the `runningBar` is complete.
-// To achieve bar replacement effect, `runningBar` should has its `BarRemoveOnComplete` option set.
+// BarReplaceOnComplete is indicator for delayed bar start, after the
+// `runningBar` is complete. To achieve bar replacement effect,
+// `runningBar` should has its `BarRemoveOnComplete` option set.
func BarReplaceOnComplete(runningBar *Bar) BarOption {
+ return BarParkTo(runningBar)
+}
+
+// BarParkTo same as BarReplaceOnComplete
+func BarParkTo(runningBar *Bar) BarOption {
return func(s *bState) {
s.runningBar = runningBar
}
}
-// BarClearOnComplete is a flag, if set will clear bar section on complete event.
-// If you need to remove a whole bar line, refer to BarRemoveOnComplete.
+// BarClearOnComplete is a flag, if set will clear bar section on
+// complete event. If you need to remove a whole bar line, refer to
+// BarRemoveOnComplete.
func BarClearOnComplete() BarOption {
return func(s *bState) {
s.barClearOnComplete = true
}
}
-// 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.
+// 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
}
}
-// BarNewLineExtend takes user defined efn, which gets called each render cycle.
-// Any write to provided writer of efn, will appear on new line of respective bar.
+// BarNewLineExtend takes user defined efn, which gets called each
+// render cycle. Any write to provided writer of efn, will appear on
+// new line of respective bar.
func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption {
return func(s *bState) {
s.newLineExtendFn = efn
}
}
-func barWidth(w int) BarOption {
+// TrimSpace trims bar's edge spaces.
+func TrimSpace() BarOption {
return func(s *bState) {
- s.width = w
+ s.trimSpace = true
+ }
+}
+
+// BarStyle sets custom bar style, default one is "[=>-]<+".
+//
+// '[' left bracket rune
+//
+// '=' fill rune
+//
+// '>' tip rune
+//
+// '-' empty rune
+//
+// ']' right bracket rune
+//
+// '<' reverse tip rune, used when BarReverse option is set
+//
+// '+' refill rune, used when *Bar.SetRefill(int64) is called
+//
+// It's ok to provide first five runes only, for example mpb.BarStyle("╢▌▌░╟")
+func BarStyle(style string) BarOption {
+ chk := func(filler Filler) (interface{}, bool) {
+ if style == "" {
+ return nil, false
+ }
+ t, ok := filler.(*barFiller)
+ return t, ok
+ }
+ cb := func(t interface{}) {
+ t.(*barFiller).setStyle(style)
+ }
+ return MakeFillerTypeSpecificBarOption(chk, cb)
+}
+
+// BarReverse reverse mode, bar will progress from right to left.
+func BarReverse() BarOption {
+ chk := func(filler Filler) (interface{}, bool) {
+ t, ok := filler.(*barFiller)
+ return t, ok
+ }
+ cb := func(t interface{}) {
+ t.(*barFiller).setReverse()
+ }
+ return MakeFillerTypeSpecificBarOption(chk, cb)
+}
+
+// SpinnerStyle sets custom spinner style.
+// Effective when Filler type is spinner.
+func SpinnerStyle(frames []string) BarOption {
+ chk := func(filler Filler) (interface{}, bool) {
+ if len(frames) == 0 {
+ return nil, false
+ }
+ t, ok := filler.(*spinnerFiller)
+ return t, ok
+ }
+ cb := func(t interface{}) {
+ t.(*spinnerFiller).frames = frames
}
+ return MakeFillerTypeSpecificBarOption(chk, cb)
}
-func barFormat(format string) BarOption {
+// MakeFillerTypeSpecificBarOption makes BarOption specific to Filler's
+// actual type. If you implement your own Filler, so most probably
+// you'll need this. See BarStyle or SpinnerStyle for example.
+func MakeFillerTypeSpecificBarOption(
+ typeChecker func(Filler) (interface{}, bool),
+ cb func(interface{}),
+) BarOption {
return func(s *bState) {
- s.runes = strToBarRunes(format)
+ if t, ok := typeChecker(s.filler); ok {
+ cb(t)
+ }
+ }
+}
+
+// OptionOnCondition returns option when condition evaluates to true.
+func OptionOnCondition(option BarOption, condition func() bool) BarOption {
+ if condition() {
+ return option
}
+ return nil
}
diff --git a/vendor/github.com/vbauerster/mpb/cwriter/writer.go b/vendor/github.com/vbauerster/mpb/cwriter/writer.go
index 0b1470d4c..638237c18 100644
--- a/vendor/github.com/vbauerster/mpb/cwriter/writer.go
+++ b/vendor/github.com/vbauerster/mpb/cwriter/writer.go
@@ -22,8 +22,8 @@ var (
clearCursorAndLine = cursorUp + clearLine
)
-// Writer is a buffered the writer that updates the terminal.
-// The contents of writer will be flushed when Flush is called.
+// 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
@@ -64,11 +64,13 @@ 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.
+// 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 {
tw, _, err := terminal.GetSize(w.fd)
diff --git a/vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go b/vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go
index dad7f50b2..747a63484 100644
--- a/vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go
+++ b/vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go
@@ -8,7 +8,7 @@ import (
"syscall"
"unsafe"
- "github.com/mattn/go-isatty"
+ isatty "github.com/mattn/go-isatty"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
diff --git a/vendor/github.com/vbauerster/mpb/decor/counters.go b/vendor/github.com/vbauerster/mpb/decor/counters.go
index e4161dc4b..7d581eefb 100644
--- a/vendor/github.com/vbauerster/mpb/decor/counters.go
+++ b/vendor/github.com/vbauerster/mpb/decor/counters.go
@@ -141,12 +141,14 @@ func CountersNoUnit(pairFormat string, wcc ...WC) Decorator {
return Counters(0, pairFormat, wcc...)
}
-// CountersKibiByte is a wrapper around Counters with predefined unit UnitKiB (bytes/1024).
+// CountersKibiByte is a wrapper around Counters with predefined unit
+// UnitKiB (bytes/1024).
func CountersKibiByte(pairFormat string, wcc ...WC) Decorator {
return Counters(UnitKiB, pairFormat, wcc...)
}
-// CountersKiloByte is a wrapper around Counters with predefined unit UnitKB (bytes/1000).
+// CountersKiloByte is a wrapper around Counters with predefined unit
+// UnitKB (bytes/1000).
func CountersKiloByte(pairFormat string, wcc ...WC) Decorator {
return Counters(UnitKB, pairFormat, wcc...)
}
diff --git a/vendor/github.com/vbauerster/mpb/decor/decorator.go b/vendor/github.com/vbauerster/mpb/decor/decorator.go
index 6aaf6c830..2fe40aea6 100644
--- a/vendor/github.com/vbauerster/mpb/decor/decorator.go
+++ b/vendor/github.com/vbauerster/mpb/decor/decorator.go
@@ -31,8 +31,12 @@ const (
DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight
)
+// TimeStyle enum.
+type TimeStyle int
+
+// TimeStyle kinds.
const (
- ET_STYLE_GO = iota
+ ET_STYLE_GO TimeStyle = iota
ET_STYLE_HHMMSS
ET_STYLE_HHMM
ET_STYLE_MMSS
@@ -47,35 +51,37 @@ type Statistics struct {
}
// Decorator interface.
-// A decorator must implement this interface, in order to be used with mpb library.
+// A decorator must implement this interface, in order to be used with
+// mpb library.
type Decorator interface {
Decor(*Statistics) string
Syncable
}
// Syncable interface.
-// All decorators implement this interface implicitly.
-// Its Syncable method exposes width sync channel, if sync is enabled.
+// All decorators implement this interface implicitly. Its Syncable
+// method exposes width sync channel, if sync is enabled.
type Syncable interface {
Syncable() (bool, chan int)
}
// OnCompleteMessenger interface.
-// Decorators implementing this interface suppose to return provided string on complete event.
+// Decorators implementing this interface suppose to return provided
+// string on complete event.
type OnCompleteMessenger interface {
OnCompleteMessage(string)
}
// AmountReceiver interface.
-// If decorator needs to receive increment amount,
-// so this is the right interface to implement.
+// If decorator needs to receive increment amount, so this is the right
+// interface to implement.
type AmountReceiver interface {
NextAmount(int, ...time.Duration)
}
// ShutdownListener interface.
-// If decorator needs to be notified once upon bar shutdown event,
-// so this is the right interface to implement.
+// If decorator needs to be notified once upon bar shutdown event, so
+// this is the right interface to implement.
type ShutdownListener interface {
Shutdown()
}
@@ -90,6 +96,7 @@ var (
// 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, in order to become Syncable.
type WC struct {
W int
C int
@@ -126,12 +133,13 @@ func (wc *WC) Init() {
}
}
+// Syncable is implementation of Syncable interface.
func (wc *WC) Syncable() (bool, chan int) {
return (wc.C & DSyncWidth) != 0, wc.wsync
}
-// OnComplete returns decorator, which wraps provided decorator, with sole
-// purpose to display provided message on complete event.
+// OnComplete returns decorator, which wraps provided decorator, with
+// sole purpose to display provided message on complete event.
//
// `decorator` Decorator to wrap
//
diff --git a/vendor/github.com/vbauerster/mpb/decor/elapsed.go b/vendor/github.com/vbauerster/mpb/decor/elapsed.go
index 649d40a30..b2e75852c 100644
--- a/vendor/github.com/vbauerster/mpb/decor/elapsed.go
+++ b/vendor/github.com/vbauerster/mpb/decor/elapsed.go
@@ -10,7 +10,7 @@ import (
// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
//
// `wcc` optional WC config
-func Elapsed(style int, wcc ...WC) Decorator {
+func Elapsed(style TimeStyle, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@@ -26,7 +26,7 @@ func Elapsed(style int, wcc ...WC) Decorator {
type elapsedDecorator struct {
WC
- style int
+ style TimeStyle
startTime time.Time
msg string
completeMsg *string
diff --git a/vendor/github.com/vbauerster/mpb/decor/eta.go b/vendor/github.com/vbauerster/mpb/decor/eta.go
index 44a1f03ea..e8dc979b4 100644
--- a/vendor/github.com/vbauerster/mpb/decor/eta.go
+++ b/vendor/github.com/vbauerster/mpb/decor/eta.go
@@ -6,7 +6,6 @@ import (
"time"
"github.com/VividCortex/ewma"
- "github.com/vbauerster/mpb/internal"
)
type TimeNormalizer func(time.Duration) time.Duration
@@ -18,7 +17,7 @@ type TimeNormalizer func(time.Duration) time.Duration
// `age` is the previous N samples to average over.
//
// `wcc` optional WC config
-func EwmaETA(style int, age float64, wcc ...WC) Decorator {
+func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
return MovingAverageETA(style, ewma.NewMovingAverage(age), NopNormalizer(), wcc...)
}
@@ -31,7 +30,7 @@ func EwmaETA(style int, age float64, wcc ...WC) Decorator {
// `normalizer` available implementations are [NopNormalizer|FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
//
// `wcc` optional WC config
-func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
+func MovingAverageETA(style TimeStyle, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@@ -48,7 +47,7 @@ func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalize
type movingAverageETA struct {
WC
- style int
+ style TimeStyle
average ewma.MovingAverage
completeMsg *string
normalizer TimeNormalizer
@@ -59,7 +58,7 @@ func (d *movingAverageETA) Decor(st *Statistics) string {
return d.FormatMsg(*d.completeMsg)
}
- v := internal.Round(d.average.Value())
+ v := math.Round(d.average.Value())
remaining := d.normalizer(time.Duration((st.Total - st.Current) * int64(v)))
hours := int64((remaining / time.Hour) % 60)
minutes := int64((remaining / time.Minute) % 60)
@@ -105,7 +104,7 @@ func (d *movingAverageETA) OnCompleteMessage(msg string) {
// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
//
// `wcc` optional WC config
-func AverageETA(style int, wcc ...WC) Decorator {
+func AverageETA(style TimeStyle, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@@ -121,7 +120,7 @@ func AverageETA(style int, wcc ...WC) Decorator {
type averageETA struct {
WC
- style int
+ style TimeStyle
startTime time.Time
completeMsg *string
}
@@ -133,7 +132,7 @@ func (d *averageETA) Decor(st *Statistics) string {
var str string
timeElapsed := time.Since(d.startTime)
- v := internal.Round(float64(timeElapsed) / float64(st.Current))
+ v := math.Round(float64(timeElapsed) / float64(st.Current))
if math.IsInf(v, 0) || math.IsNaN(v) {
v = 0
}
diff --git a/vendor/github.com/vbauerster/mpb/decor/moving-average.go b/vendor/github.com/vbauerster/mpb/decor/moving-average.go
index f9596a27f..fcd268923 100644
--- a/vendor/github.com/vbauerster/mpb/decor/moving-average.go
+++ b/vendor/github.com/vbauerster/mpb/decor/moving-average.go
@@ -6,9 +6,9 @@ import (
"github.com/VividCortex/ewma"
)
-// MovingAverage is the interface that computes a moving average over a time-
-// series stream of numbers. The average may be over a window or exponentially
-// decaying.
+// MovingAverage is the interface that computes a moving average over
+// a time-series stream of numbers. The average may be over a window
+// or exponentially decaying.
type MovingAverage interface {
Add(float64)
Value() float64
@@ -57,7 +57,8 @@ func (s *medianEwma) Add(v float64) {
s.count++
}
-// NewMedianEwma is ewma based MovingAverage, which gets its values from median MovingAverage.
+// NewMedianEwma is ewma based MovingAverage, which gets its values
+// from median MovingAverage.
func NewMedianEwma(age ...float64) MovingAverage {
return &medianEwma{
MovingAverage: ewma.NewMovingAverage(age...),
diff --git a/vendor/github.com/vbauerster/mpb/decor/speed.go b/vendor/github.com/vbauerster/mpb/decor/speed.go
index 395e5d04d..74658ce41 100644
--- a/vendor/github.com/vbauerster/mpb/decor/speed.go
+++ b/vendor/github.com/vbauerster/mpb/decor/speed.go
@@ -137,7 +137,8 @@ func EwmaSpeed(unit int, unitFormat string, age float64, wcc ...WC) Decorator {
return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...)
}
-// MovingAverageSpeed decorator relies on MovingAverage implementation to calculate its average.
+// MovingAverageSpeed decorator relies on MovingAverage implementation
+// to calculate its average.
//
// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
//
diff --git a/vendor/github.com/vbauerster/mpb/internal/percentage.go b/vendor/github.com/vbauerster/mpb/internal/percentage.go
index 3c8defb7d..0483d2598 100644
--- a/vendor/github.com/vbauerster/mpb/internal/percentage.go
+++ b/vendor/github.com/vbauerster/mpb/internal/percentage.go
@@ -1,10 +1,12 @@
package internal
+import "math"
+
// Percentage is a helper function, to calculate percentage.
func Percentage(total, current, width int64) int64 {
if total <= 0 {
return 0
}
p := float64(width*current) / float64(total)
- return int64(Round(p))
+ return int64(math.Round(p))
}
diff --git a/vendor/github.com/vbauerster/mpb/internal/round.go b/vendor/github.com/vbauerster/mpb/internal/round.go
deleted file mode 100644
index c54a789d2..000000000
--- a/vendor/github.com/vbauerster/mpb/internal/round.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package internal
-
-import "math"
-
-const (
- uvone = 0x3FF0000000000000
- mask = 0x7FF
- shift = 64 - 11 - 1
- bias = 1023
- signMask = 1 << 63
- fracMask = 1<<shift - 1
-)
-
-// Round returns the nearest integer, rounding half away from zero.
-//
-// Special cases are:
-// Round(±0) = ±0
-// Round(±Inf) = ±Inf
-// Round(NaN) = NaN
-func Round(x float64) float64 {
- // Round is a faster implementation of:
- //
- // func Round(x float64) float64 {
- // t := Trunc(x)
- // if Abs(x-t) >= 0.5 {
- // return t + Copysign(1, x)
- // }
- // return t
- // }
- bits := math.Float64bits(x)
- e := uint(bits>>shift) & mask
- if e < bias {
- // Round abs(x) < 1 including denormals.
- bits &= signMask // +-0
- if e == bias-1 {
- bits |= uvone // +-1
- }
- } else if e < bias+shift {
- // Round any abs(x) >= 1 containing a fractional component [0,1).
- //
- // Numbers with larger exponents are returned unchanged since they
- // must be either an integer, infinity, or NaN.
- const half = 1 << (shift - 1)
- e -= bias
- bits += half >> e
- bits &^= fracMask >> e
- }
- return math.Float64frombits(bits)
-}
diff --git a/vendor/github.com/vbauerster/mpb/options.go b/vendor/github.com/vbauerster/mpb/options.go
index 05d2ecf1f..44a6ee3f3 100644
--- a/vendor/github.com/vbauerster/mpb/options.go
+++ b/vendor/github.com/vbauerster/mpb/options.go
@@ -1,29 +1,30 @@
package mpb
import (
+ "context"
"io"
"sync"
"time"
- "unicode/utf8"
"github.com/vbauerster/mpb/cwriter"
)
-// ProgressOption is a function option which changes the default behavior of
-// progress pool, if passed to mpb.New(...ProgressOption)
+// ProgressOption is a function option which changes the default
+// behavior of progress pool, if passed to mpb.New(...ProgressOption).
type ProgressOption 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.
+// 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) ProgressOption {
return func(s *pState) {
s.uwg = wg
}
}
-// WithWidth overrides default width 80
+// WithWidth sets container width. Default is 80. Bars inherit this
+// width, as long as no BarWidth is applied.
func WithWidth(w int) ProgressOption {
return func(s *pState) {
if w >= 0 {
@@ -32,16 +33,7 @@ func WithWidth(w int) ProgressOption {
}
}
-// WithFormat overrides default bar format "[=>-]"
-func WithFormat(format string) ProgressOption {
- return func(s *pState) {
- if utf8.RuneCountInString(format) == formatLen {
- s.format = format
- }
- }
-}
-
-// WithRefreshRate overrides default 120ms refresh rate
+// WithRefreshRate overrides default 120ms refresh rate.
func WithRefreshRate(d time.Duration) ProgressOption {
return func(s *pState) {
if d < 10*time.Millisecond {
@@ -59,22 +51,25 @@ func WithManualRefresh(ch <-chan time.Time) ProgressOption {
}
}
-// WithCancel provide your cancel channel,
-// which you plan to close at some point.
-func WithCancel(ch <-chan struct{}) ProgressOption {
+// WithContext provided context will be used for cancellation purposes.
+func WithContext(ctx context.Context) ProgressOption {
return func(s *pState) {
- s.cancel = ch
+ if ctx == nil {
+ return
+ }
+ s.ctx = ctx
}
}
-// WithShutdownNotifier provided chanel will be closed, after all bars have been rendered.
+// WithShutdownNotifier provided chanel will be closed, after all bars
+// have been rendered.
func WithShutdownNotifier(ch chan struct{}) ProgressOption {
return func(s *pState) {
s.shutdownNotifier = ch
}
}
-// WithOutput overrides default output os.Stdout
+// WithOutput overrides default output os.Stdout.
func WithOutput(w io.Writer) ProgressOption {
return func(s *pState) {
if w == nil {
diff --git a/vendor/github.com/vbauerster/mpb/options_go1.7.go b/vendor/github.com/vbauerster/mpb/options_go1.7.go
deleted file mode 100644
index ca9a5bad8..000000000
--- a/vendor/github.com/vbauerster/mpb/options_go1.7.go
+++ /dev/null
@@ -1,15 +0,0 @@
-//+build go1.7
-
-package mpb
-
-import "context"
-
-// WithContext provided context will be used for cancellation purposes
-func WithContext(ctx context.Context) ProgressOption {
- return func(s *pState) {
- if ctx == nil {
- panic("ctx must not be nil")
- }
- s.cancel = ctx.Done()
- }
-}
diff --git a/vendor/github.com/vbauerster/mpb/progress.go b/vendor/github.com/vbauerster/mpb/progress.go
index d95fe45b7..f9e25af79 100644
--- a/vendor/github.com/vbauerster/mpb/progress.go
+++ b/vendor/github.com/vbauerster/mpb/progress.go
@@ -2,6 +2,7 @@ package mpb
import (
"container/heap"
+ "context"
"fmt"
"io"
"io/ioutil"
@@ -17,8 +18,6 @@ const (
prr = 120 * time.Millisecond
// default width
pwidth = 80
- // default format
- pformat = "[=>-]"
)
// Progress represents the container that renders Progress bars
@@ -42,24 +41,24 @@ type pState struct {
pMatrix map[int][]chan int
aMatrix map[int][]chan int
- // following are provided by user
+ // following are provided/overrided by user
+ ctx context.Context
uwg *sync.WaitGroup
manualRefreshCh <-chan time.Time
- cancel <-chan struct{}
shutdownNotifier chan struct{}
waitBars map[*Bar]*Bar
debugOut io.Writer
}
-// New creates new Progress instance, which orchestrates bars rendering process.
-// Accepts mpb.ProgressOption funcs for customization.
+// New creates new Progress instance, which orchestrates bars rendering
+// process. Accepts mpb.ProgressOption funcs for customization.
func New(options ...ProgressOption) *Progress {
pq := make(priorityQueue, 0)
heap.Init(&pq)
s := &pState{
+ ctx: context.Background(),
bHeap: &pq,
width: pwidth,
- format: pformat,
cw: cwriter.New(os.Stdout),
rr: prr,
waitBars: make(map[*Bar]*Bar),
@@ -84,12 +83,28 @@ func New(options ...ProgressOption) *Progress {
// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
+ return p.Add(total, newDefaultBarFiller(), options...)
+}
+
+// AddSpinner creates a new spinner bar and adds to the container.
+func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
+ filler := &spinnerFiller{
+ frames: defaultSpinnerStyle,
+ alignment: alignment,
+ }
+ return p.Add(total, filler, options...)
+}
+
+// Add creates a bar which renders itself by provided filler.
+func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar {
+ if filler == nil {
+ filler = newDefaultBarFiller()
+ }
p.wg.Add(1)
result := make(chan *Bar)
select {
case p.operateState <- func(s *pState) {
- options = append(options, barWidth(s.width), barFormat(s.format))
- b := newBar(p.wg, s.idCounter, total, s.cancel, options...)
+ b := newBar(s.ctx, p.wg, filler, s.idCounter, s.width, total, options...)
if b.runningBar != nil {
s.waitBars[b.runningBar] = b
} else {
@@ -106,10 +121,10 @@ func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
}
}
-// Abort is only effective while bar progress is running,
-// it means remove bar now without waiting for its completion.
-// If bar is already completed, there is nothing to abort.
-// If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
+// Abort is only effective while bar progress is running, it means
+// remove bar now without waiting for its completion. If bar is already
+// completed, there is nothing to abort. If you need to remove bar
+// after completion, use BarRemoveOnComplete BarOption.
func (p *Progress) Abort(b *Bar, remove bool) {
select {
case p.operateState <- func(s *pState) {
@@ -145,9 +160,10 @@ func (p *Progress) BarCount() int {
}
}
-// Wait first waits for user provided *sync.WaitGroup, if any,
-// then waits far all bars to complete and finally shutdowns master goroutine.
-// After this method has been called, there is no way to reuse *Progress instance.
+// Wait first waits for user provided *sync.WaitGroup, if any, then
+// waits far all bars to complete and finally shutdowns master goroutine.
+// After this method has been called, there is no way to reuse *Progress
+// instance.
func (p *Progress) Wait() {
if p.uwg != nil {
p.uwg.Wait()
@@ -205,8 +221,8 @@ func (s *pState) flush(lineCount int) error {
defer func() {
if frameReader.toShutdown {
// shutdown at next flush, in other words decrement underlying WaitGroup
- // only after the bar with completed state has been flushed.
- // this ensures no bar ends up with less than 100% rendered.
+ // only after the bar with completed state has been flushed. this
+ // ensures no bar ends up with less than 100% rendered.
s.shutdownPending = append(s.shutdownPending, bar)
if replacementBar, ok := s.waitBars[bar]; ok {
heap.Push(s.bHeap, replacementBar)
diff --git a/vendor/github.com/vbauerster/mpb/spinner_filler.go b/vendor/github.com/vbauerster/mpb/spinner_filler.go
new file mode 100644
index 000000000..36299fef0
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/spinner_filler.go
@@ -0,0 +1,48 @@
+package mpb
+
+import (
+ "io"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/vbauerster/mpb/decor"
+)
+
+// SpinnerAlignment enum.
+type SpinnerAlignment int
+
+// SpinnerAlignment kinds.
+const (
+ SpinnerOnLeft SpinnerAlignment = iota
+ SpinnerOnMiddle
+ SpinnerOnRight
+)
+
+var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
+
+type spinnerFiller struct {
+ frames []string
+ count uint
+ alignment SpinnerAlignment
+}
+
+func (s *spinnerFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
+
+ frame := s.frames[s.count%uint(len(s.frames))]
+ frameWidth := utf8.RuneCountInString(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++
+}