parent
326f0cfa2f
commit
5c292c61c6
1437 changed files with 208886 additions and 87131 deletions
84
vendor/github.com/vbauerster/mpb/v7/cwriter/writer.go
generated
vendored
84
vendor/github.com/vbauerster/mpb/v7/cwriter/writer.go
generated
vendored
|
|
@ -1,84 +0,0 @@
|
|||
package cwriter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ErrNotTTY not a TeleTYpewriter error.
|
||||
var ErrNotTTY = errors.New("not a terminal")
|
||||
|
||||
// https://github.com/dylanaraps/pure-sh-bible#cursor-movement
|
||||
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
|
||||
lines 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(lines int) (err error) {
|
||||
// some terminals interpret 'cursor up 0' as 'cursor up 1'
|
||||
if w.lines > 0 {
|
||||
err = w.clearLines()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
w.lines = lines
|
||||
_, 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() error {
|
||||
buf := make([]byte, 8)
|
||||
buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lines), 10)
|
||||
_, err := w.out.Write(append(buf, cuuAndEd...))
|
||||
return err
|
||||
}
|
||||
26
vendor/github.com/vbauerster/mpb/v7/cwriter/writer_posix.go
generated
vendored
26
vendor/github.com/vbauerster/mpb/v7/cwriter/writer_posix.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
27
vendor/github.com/vbauerster/mpb/v7/decor/on_condition.go
generated
vendored
27
vendor/github.com/vbauerster/mpb/v7/decor/on_condition.go
generated
vendored
|
|
@ -1,27 +0,0 @@
|
|||
package decor
|
||||
|
||||
// OnPredicate returns decorator if predicate evaluates to true.
|
||||
//
|
||||
// `decorator` Decorator
|
||||
//
|
||||
// `predicate` func() bool
|
||||
//
|
||||
func OnPredicate(decorator Decorator, predicate func() bool) Decorator {
|
||||
if predicate() {
|
||||
return decorator
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnCondition returns decorator if condition is true.
|
||||
//
|
||||
// `decorator` Decorator
|
||||
//
|
||||
// `cond` bool
|
||||
//
|
||||
func OnCondition(decorator Decorator, cond bool) Decorator {
|
||||
if cond {
|
||||
return decorator
|
||||
}
|
||||
return nil
|
||||
}
|
||||
10
vendor/github.com/vbauerster/mpb/v7/decor/optimistic_string_writer.go
generated
vendored
10
vendor/github.com/vbauerster/mpb/v7/decor/optimistic_string_writer.go
generated
vendored
|
|
@ -1,10 +0,0 @@
|
|||
package decor
|
||||
|
||||
import "io"
|
||||
|
||||
func mustWriteString(w io.Writer, s string) {
|
||||
_, err := io.WriteString(w, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
409
vendor/github.com/vbauerster/mpb/v7/progress.go
generated
vendored
409
vendor/github.com/vbauerster/mpb/v7/progress.go
generated
vendored
|
|
@ -1,409 +0,0 @@
|
|||
package mpb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/heap"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/vbauerster/mpb/v7/cwriter"
|
||||
"github.com/vbauerster/mpb/v7/decor"
|
||||
)
|
||||
|
||||
const (
|
||||
prr = 150 * time.Millisecond // default RefreshRate
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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{}
|
||||
queueBars 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,
|
||||
queueBars: make(map[*Bar]*Bar),
|
||||
output: os.Stdout,
|
||||
}
|
||||
|
||||
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{}),
|
||||
}
|
||||
|
||||
p.cwg.Add(1)
|
||||
go p.serve(s, cwriter.New(s.output))
|
||||
return p
|
||||
}
|
||||
|
||||
// AddBar creates a bar with default bar filler.
|
||||
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
|
||||
return p.New(total, BarStyle(), options...)
|
||||
}
|
||||
|
||||
// AddSpinner creates a bar with default spinner filler.
|
||||
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
|
||||
return p.New(total, SpinnerStyle(), options...)
|
||||
}
|
||||
|
||||
// New creates a bar with provided BarFillerBuilder.
|
||||
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
|
||||
return p.Add(total, builder.Build(), options...)
|
||||
}
|
||||
|
||||
// Add creates a bar which renders itself by provided filler.
|
||||
// If `total <= 0` triggering complete event by increment methods is disabled.
|
||||
// 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 = NopStyle().Build()
|
||||
}
|
||||
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.afterBar != nil {
|
||||
ps.queueBars[bs.afterBar] = bar
|
||||
} else {
|
||||
heap.Push(&ps.bHeap, bar)
|
||||
ps.heapUpdated = true
|
||||
}
|
||||
ps.idCount++
|
||||
result <- bar
|
||||
}:
|
||||
bar := <-result
|
||||
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) traverseBars(cb func(b *Bar) bool) {
|
||||
sync := make(chan struct{})
|
||||
select {
|
||||
case p.operateState <- func(s *pState) {
|
||||
for i := 0; i < s.bHeap.Len(); i++ {
|
||||
bar := s.bHeap[i]
|
||||
if !cb(bar) {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(sync)
|
||||
}:
|
||||
<-sync
|
||||
case <-p.done:
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateBarPriority same as *Bar.SetPriority(int).
|
||||
func (p *Progress) UpdateBarPriority(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:
|
||||
}
|
||||
}
|
||||
|
||||
// BarCount returns bars count.
|
||||
func (p *Progress) BarCount() int {
|
||||
result := make(chan int)
|
||||
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() {
|
||||
// wait for user wg, if any
|
||||
if p.uwg != nil {
|
||||
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 {
|
||||
if s.debugOut != nil {
|
||||
_, e := fmt.Fprintln(s.debugOut, err)
|
||||
if e != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
case <-s.shutdownNotifier:
|
||||
for s.heapUpdated {
|
||||
if err := s.render(cw); err != nil {
|
||||
if s.debugOut != nil {
|
||||
_, e := fmt.Fprintln(s.debugOut, err)
|
||||
if e != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 lines int
|
||||
pool := make([]*Bar, 0, s.bHeap.Len())
|
||||
for s.bHeap.Len() > 0 {
|
||||
b := heap.Pop(&s.bHeap).(*Bar)
|
||||
frame := <-b.frameCh
|
||||
lines += frame.lines
|
||||
_, err := cw.ReadFrom(frame.reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if frame.shutdown {
|
||||
b.Wait() // waiting for b.done, so it's safe to read b.bs
|
||||
var toDrop bool
|
||||
if qb, ok := s.queueBars[b]; ok {
|
||||
delete(s.queueBars, b)
|
||||
qb.priority = b.priority
|
||||
pool = append(pool, qb)
|
||||
toDrop = true
|
||||
} else if s.popCompleted && !b.bs.noPop {
|
||||
lines -= frame.lines
|
||||
toDrop = true
|
||||
}
|
||||
if toDrop || b.bs.dropOnComplete {
|
||||
s.heapUpdated = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
pool = append(pool, b)
|
||||
}
|
||||
|
||||
for _, b := range pool {
|
||||
heap.Push(&s.bHeap, b)
|
||||
}
|
||||
|
||||
return cw.Flush(lines)
|
||||
}
|
||||
|
||||
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) 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)
|
||||
}
|
||||
|
||||
for i := 0; i < len(bs.buffers); i++ {
|
||||
bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
|
||||
}
|
||||
|
||||
bs.subscribeDecorators()
|
||||
|
||||
return bs
|
||||
}
|
||||
|
||||
func syncWidth(matrix map[int][]chan int) {
|
||||
for _, column := range matrix {
|
||||
go maxWidthDistributor(column)
|
||||
}
|
||||
}
|
||||
|
||||
func maxWidthDistributor(column []chan int) {
|
||||
var maxWidth int
|
||||
for _, ch := range column {
|
||||
if w := <-ch; w > maxWidth {
|
||||
maxWidth = w
|
||||
}
|
||||
}
|
||||
for _, ch := range column {
|
||||
ch <- maxWidth
|
||||
}
|
||||
}
|
||||
80
vendor/github.com/vbauerster/mpb/v7/proxyreader.go
generated
vendored
80
vendor/github.com/vbauerster/mpb/v7/proxyreader.go
generated
vendored
|
|
@ -1,80 +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)
|
||||
return n, err
|
||||
}
|
||||
|
||||
type proxyWriterTo struct {
|
||||
proxyReader
|
||||
wt io.WriterTo
|
||||
}
|
||||
|
||||
func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := x.wt.WriteTo(w)
|
||||
x.bar.IncrInt64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
type ewmaProxyReader struct {
|
||||
proxyReader
|
||||
}
|
||||
|
||||
func (x ewmaProxyReader) Read(p []byte) (int, error) {
|
||||
start := time.Now()
|
||||
n, err := x.proxyReader.Read(p)
|
||||
if n > 0 {
|
||||
x.bar.DecoratorEwmaUpdate(time.Since(start))
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
type ewmaProxyWriterTo struct {
|
||||
ewmaProxyReader
|
||||
wt proxyWriterTo
|
||||
}
|
||||
|
||||
func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
|
||||
start := time.Now()
|
||||
n, err := x.wt.WriteTo(w)
|
||||
if n > 0 {
|
||||
x.bar.DecoratorEwmaUpdate(time.Since(start))
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (b *Bar) newProxyReader(r io.Reader) (rc io.ReadCloser) {
|
||||
pr := proxyReader{toReadCloser(r), b}
|
||||
if wt, ok := r.(io.WriterTo); ok {
|
||||
pw := proxyWriterTo{pr, wt}
|
||||
if b.hasEwma {
|
||||
rc = ewmaProxyWriterTo{ewmaProxyReader{pr}, pw}
|
||||
} else {
|
||||
rc = pw
|
||||
}
|
||||
} else if b.hasEwma {
|
||||
rc = ewmaProxyReader{pr}
|
||||
} else {
|
||||
rc = pr
|
||||
}
|
||||
return rc
|
||||
}
|
||||
|
||||
func toReadCloser(r io.Reader) io.ReadCloser {
|
||||
if rc, ok := r.(io.ReadCloser); ok {
|
||||
return rc
|
||||
}
|
||||
return ioutil.NopCloser(r)
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# Multi Progress Bar
|
||||
|
||||
[](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
|
||||
[](https://pkg.go.dev/github.com/vbauerster/mpb/v8)
|
||||
[](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
|
||||
[](https://www.paypal.me/vbauerster)
|
||||
[](https://github.com/vbauerster/mpb/actions/workflows/golangci-lint.yml)
|
||||
|
||||
**mpb** is a Go lib for rendering progress bars in terminal applications.
|
||||
|
||||
|
|
@ -26,8 +26,8 @@ import (
|
|||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/vbauerster/mpb/v7"
|
||||
"github.com/vbauerster/mpb/v7/decor"
|
||||
"github.com/vbauerster/mpb/v8"
|
||||
"github.com/vbauerster/mpb/v8/decor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -82,8 +82,8 @@ func main() {
|
|||
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, decor.WCSyncWidth), "done",
|
||||
// ETA decorator with ewma age of 30
|
||||
decor.EwmaETA(decor.ET_STYLE_GO, 30, decor.WCSyncWidth), "done",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -97,9 +97,8 @@ func main() {
|
|||
// 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))
|
||||
// we need to call EwmaIncrement to fulfill ewma decorator's contract
|
||||
bar.EwmaIncrement(time.Since(start))
|
||||
}
|
||||
}()
|
||||
}
|
||||
488
vendor/github.com/vbauerster/mpb/v7/bar.go → vendor/github.com/vbauerster/mpb/v8/bar.go
generated
vendored
488
vendor/github.com/vbauerster/mpb/v7/bar.go → vendor/github.com/vbauerster/mpb/v8/bar.go
generated
vendored
|
|
@ -3,49 +3,47 @@ package mpb
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/acarl005/stripansi"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/vbauerster/mpb/v7/decor"
|
||||
"github.com/vbauerster/mpb/v8/decor"
|
||||
)
|
||||
|
||||
// Bar represents a progress bar.
|
||||
type Bar struct {
|
||||
index int // used by heap
|
||||
priority int // used by heap
|
||||
hasEwma bool
|
||||
frameCh chan *renderFrame
|
||||
operateState chan func(*bState)
|
||||
done chan struct{}
|
||||
container *Progress
|
||||
bs *bState
|
||||
cancel func()
|
||||
recoveredPanic interface{}
|
||||
index int // used by heap
|
||||
priority int // used by heap
|
||||
frameCh chan *renderFrame
|
||||
operateState chan func(*bState)
|
||||
done chan struct{}
|
||||
container *Progress
|
||||
bs *bState
|
||||
cancel func()
|
||||
}
|
||||
|
||||
type extenderFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int)
|
||||
type syncTable [2][]chan int
|
||||
type extenderFunc func([]io.Reader, decor.Statistics) ([]io.Reader, error)
|
||||
|
||||
// bState is actual bar's state.
|
||||
type bState struct {
|
||||
id int
|
||||
priority int
|
||||
reqWidth int
|
||||
shutdown int
|
||||
total int64
|
||||
current int64
|
||||
refill int64
|
||||
lastIncrement int64
|
||||
trimSpace bool
|
||||
completed bool
|
||||
aborted bool
|
||||
triggerComplete bool
|
||||
dropOnComplete bool
|
||||
rmOnComplete bool
|
||||
noPop bool
|
||||
autoRefresh bool
|
||||
aDecorators []decor.Decorator
|
||||
pDecorators []decor.Decorator
|
||||
averageDecorators []decor.AverageDecorator
|
||||
|
|
@ -53,26 +51,25 @@ type bState struct {
|
|||
shutdownListeners []decor.ShutdownListener
|
||||
buffers [3]*bytes.Buffer
|
||||
filler BarFiller
|
||||
middleware func(BarFiller) BarFiller
|
||||
extender extenderFunc
|
||||
debugOut io.Writer
|
||||
|
||||
afterBar *Bar // key for (*pState).queueBars
|
||||
sync bool
|
||||
renderReq chan<- time.Time
|
||||
waitBar *Bar // key for (*pState).queueBars
|
||||
}
|
||||
|
||||
type renderFrame struct {
|
||||
reader io.Reader
|
||||
lines int
|
||||
shutdown bool
|
||||
rows []io.Reader
|
||||
shutdown int
|
||||
rmOnComplete bool
|
||||
noPop bool
|
||||
done bool
|
||||
err error
|
||||
}
|
||||
|
||||
func newBar(container *Progress, bs *bState) *Bar {
|
||||
ctx, cancel := context.WithCancel(container.ctx)
|
||||
func newBar(ctx context.Context, container *Progress, bs *bState) *Bar {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
bar := &Bar{
|
||||
priority: bs.priority,
|
||||
hasEwma: len(bs.ewmaDecorators) != 0,
|
||||
frameCh: make(chan *renderFrame, 1),
|
||||
operateState: make(chan func(*bState)),
|
||||
done: make(chan struct{}),
|
||||
|
|
@ -80,24 +77,42 @@ func newBar(container *Progress, bs *bState) *Bar {
|
|||
cancel: cancel,
|
||||
}
|
||||
|
||||
container.bwg.Add(1)
|
||||
go bar.serve(ctx, bs)
|
||||
return bar
|
||||
}
|
||||
|
||||
// ProxyReader wraps r with metrics required for progress tracking.
|
||||
// If r is 'unknown total/size' reader it's mandatory to call
|
||||
// (*Bar).SetTotal(-1, true) method after (Reader).Read returns io.EOF.
|
||||
// Panics if r is nil. If bar is already completed or aborted, returns
|
||||
// nil.
|
||||
// ProxyReader wraps io.Reader with metrics required for progress tracking.
|
||||
// If `r` is 'unknown total/size' reader it's mandatory to call
|
||||
// (*Bar).SetTotal(-1, true) method after (io.Reader).Read returns io.EOF.
|
||||
// If bar is already completed or aborted, returns nil.
|
||||
// Panics if `r` is nil.
|
||||
func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
|
||||
if r == nil {
|
||||
panic("expected non nil io.Reader")
|
||||
}
|
||||
result := make(chan bool)
|
||||
select {
|
||||
case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }:
|
||||
return newProxyReader(r, b, <-result)
|
||||
case <-b.done:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ProxyWriter wraps io.Writer with metrics required for progress tracking.
|
||||
// If bar is already completed or aborted, returns nil.
|
||||
// Panics if `w` is nil.
|
||||
func (b *Bar) ProxyWriter(w io.Writer) io.WriteCloser {
|
||||
if w == nil {
|
||||
panic("expected non nil io.Writer")
|
||||
}
|
||||
result := make(chan bool)
|
||||
select {
|
||||
case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }:
|
||||
return newProxyWriter(w, b, <-result)
|
||||
case <-b.done:
|
||||
return nil
|
||||
default:
|
||||
return b.newProxyReader(r)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,35 +144,35 @@ func (b *Bar) Current() int64 {
|
|||
// operation for example.
|
||||
func (b *Bar) SetRefill(amount int64) {
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
s.refill = amount
|
||||
}:
|
||||
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)) {
|
||||
sync := make(chan struct{})
|
||||
iter := make(chan decor.Decorator)
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
for _, decorators := range [...][]decor.Decorator{
|
||||
for _, decorators := range [][]decor.Decorator{
|
||||
s.pDecorators,
|
||||
s.aDecorators,
|
||||
} {
|
||||
for _, d := range decorators {
|
||||
cb(extractBaseDecorator(d))
|
||||
iter <- d
|
||||
}
|
||||
}
|
||||
close(sync)
|
||||
close(iter)
|
||||
}:
|
||||
<-sync
|
||||
for d := range iter {
|
||||
cb(unwrap(d))
|
||||
}
|
||||
case <-b.done:
|
||||
}
|
||||
}
|
||||
|
||||
// EnableTriggerComplete enables triggering complete event. It's
|
||||
// effective only for bar which was constructed with `total <= 0` and
|
||||
// effective only for bars which were constructed with `total <= 0` and
|
||||
// after total has been set with (*Bar).SetTotal(int64, false). If bar
|
||||
// has been incremented to the total, complete event is triggered right
|
||||
// away.
|
||||
|
|
@ -170,7 +185,7 @@ func (b *Bar) EnableTriggerComplete() {
|
|||
if s.current >= s.total {
|
||||
s.current = s.total
|
||||
s.completed = true
|
||||
go b.forceRefresh()
|
||||
b.triggerCompletion(s)
|
||||
} else {
|
||||
s.triggerComplete = true
|
||||
}
|
||||
|
|
@ -181,10 +196,10 @@ func (b *Bar) EnableTriggerComplete() {
|
|||
|
||||
// SetTotal sets total to an arbitrary value. It's effective only for
|
||||
// bar which was constructed with `total <= 0`. Setting total to negative
|
||||
// value is equivalent to (*Bar).SetTotal((*Bar).Current(), bool).
|
||||
// If triggerCompleteNow is true, total value is set to current and
|
||||
// value is equivalent to (*Bar).SetTotal((*Bar).Current(), bool) but faster.
|
||||
// If triggerCompletion is true, total value is set to current and
|
||||
// complete event is triggered right away.
|
||||
func (b *Bar) SetTotal(total int64, triggerCompleteNow bool) {
|
||||
func (b *Bar) SetTotal(total int64, triggerCompletion bool) {
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
if s.triggerComplete {
|
||||
|
|
@ -195,10 +210,10 @@ func (b *Bar) SetTotal(total int64, triggerCompleteNow bool) {
|
|||
} else {
|
||||
s.total = total
|
||||
}
|
||||
if triggerCompleteNow {
|
||||
if triggerCompletion {
|
||||
s.current = s.total
|
||||
s.completed = true
|
||||
go b.forceRefresh()
|
||||
b.triggerCompletion(s)
|
||||
}
|
||||
}:
|
||||
case <-b.done:
|
||||
|
|
@ -206,16 +221,39 @@ func (b *Bar) SetTotal(total int64, triggerCompleteNow bool) {
|
|||
}
|
||||
|
||||
// SetCurrent sets progress' current to an arbitrary value.
|
||||
// Setting a negative value will cause a panic.
|
||||
func (b *Bar) SetCurrent(current int64) {
|
||||
if current < 0 {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
s.lastIncrement = current - s.current
|
||||
s.current = current
|
||||
if s.triggerComplete && s.current >= s.total {
|
||||
s.current = s.total
|
||||
s.completed = true
|
||||
go b.forceRefresh()
|
||||
b.triggerCompletion(s)
|
||||
}
|
||||
}:
|
||||
case <-b.done:
|
||||
}
|
||||
}
|
||||
|
||||
// EwmaSetCurrent sets progress' current to an arbitrary value and updates
|
||||
// EWMA based decorators by dur of a single iteration.
|
||||
func (b *Bar) EwmaSetCurrent(current int64, iterDur time.Duration) {
|
||||
if current < 0 {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
if n := current - s.current; n > 0 {
|
||||
s.decoratorEwmaUpdate(n, iterDur)
|
||||
}
|
||||
s.current = current
|
||||
if s.triggerComplete && s.current >= s.total {
|
||||
s.current = s.total
|
||||
s.completed = true
|
||||
b.triggerCompletion(s)
|
||||
}
|
||||
}:
|
||||
case <-b.done:
|
||||
|
|
@ -239,37 +277,44 @@ func (b *Bar) IncrInt64(n int64) {
|
|||
}
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
s.lastIncrement = n
|
||||
s.current += n
|
||||
if s.triggerComplete && s.current >= s.total {
|
||||
s.current = s.total
|
||||
s.completed = true
|
||||
go b.forceRefresh()
|
||||
b.triggerCompletion(s)
|
||||
}
|
||||
}:
|
||||
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) {
|
||||
// EwmaIncrement is a shorthand for b.EwmaIncrInt64(1, iterDur).
|
||||
func (b *Bar) EwmaIncrement(iterDur time.Duration) {
|
||||
b.EwmaIncrInt64(1, iterDur)
|
||||
}
|
||||
|
||||
// EwmaIncrBy is a shorthand for b.EwmaIncrInt64(int64(n), iterDur).
|
||||
func (b *Bar) EwmaIncrBy(n int, iterDur time.Duration) {
|
||||
b.EwmaIncrInt64(int64(n), iterDur)
|
||||
}
|
||||
|
||||
// EwmaIncrInt64 increments progress by amount of n and updates EWMA based
|
||||
// decorators by dur of a single iteration.
|
||||
func (b *Bar) EwmaIncrInt64(n int64, iterDur time.Duration) {
|
||||
if n <= 0 {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
if s.lastIncrement > 0 {
|
||||
s.decoratorEwmaUpdate(dur)
|
||||
s.lastIncrement = 0
|
||||
} else {
|
||||
panic("increment required before ewma iteration update")
|
||||
s.decoratorEwmaUpdate(n, iterDur)
|
||||
s.current += n
|
||||
if s.triggerComplete && s.current >= s.total {
|
||||
s.current = s.total
|
||||
s.completed = true
|
||||
b.triggerCompletion(s)
|
||||
}
|
||||
}:
|
||||
case <-b.done:
|
||||
if b.bs.lastIncrement > 0 {
|
||||
b.bs.decoratorEwmaUpdate(dur)
|
||||
b.bs.lastIncrement = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,9 +323,7 @@ func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
|
|||
// or after progress resume.
|
||||
func (b *Bar) DecoratorAverageAdjust(start time.Time) {
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
s.decoratorAverageAdjust(start)
|
||||
}:
|
||||
case b.operateState <- func(s *bState) { s.decoratorAverageAdjust(start) }:
|
||||
case <-b.done:
|
||||
}
|
||||
}
|
||||
|
|
@ -303,8 +346,8 @@ func (b *Bar) Abort(drop bool) {
|
|||
return
|
||||
}
|
||||
s.aborted = true
|
||||
s.dropOnComplete = drop
|
||||
go b.forceRefresh()
|
||||
s.rmOnComplete = drop
|
||||
b.triggerCompletion(s)
|
||||
}:
|
||||
case <-b.done:
|
||||
}
|
||||
|
|
@ -332,6 +375,18 @@ func (b *Bar) Completed() bool {
|
|||
}
|
||||
}
|
||||
|
||||
// IsRunning reports whether the bar is running, i.e. not yet completed
|
||||
// and not yet aborted.
|
||||
func (b *Bar) IsRunning() bool {
|
||||
result := make(chan bool)
|
||||
select {
|
||||
case b.operateState <- func(s *bState) { result <- !s.completed && !s.aborted }:
|
||||
return <-result
|
||||
case <-b.done:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Wait blocks until bar is completed or aborted.
|
||||
func (b *Bar) Wait() {
|
||||
<-b.done
|
||||
|
|
@ -339,9 +394,6 @@ func (b *Bar) Wait() {
|
|||
|
||||
func (b *Bar) serve(ctx context.Context, bs *bState) {
|
||||
defer b.container.bwg.Done()
|
||||
if bs.afterBar != nil && bs.sync {
|
||||
bs.afterBar.Wait()
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case op := <-b.operateState:
|
||||
|
|
@ -357,64 +409,65 @@ func (b *Bar) serve(ctx context.Context, bs *bState) {
|
|||
}
|
||||
|
||||
func (b *Bar) render(tw int) {
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
var reader io.Reader
|
||||
var lines int
|
||||
var done bool
|
||||
fn := func(s *bState) {
|
||||
var rows []io.Reader
|
||||
stat := newStatistics(tw, s)
|
||||
defer func() {
|
||||
// recovering if user defined decorator panics for example
|
||||
if p := recover(); p != nil {
|
||||
if s.debugOut != nil {
|
||||
fmt.Fprintln(s.debugOut, p)
|
||||
_, _ = s.debugOut.Write(debug.Stack())
|
||||
}
|
||||
s.aborted = !s.completed
|
||||
s.extender = makePanicExtender(p)
|
||||
reader, lines = s.extender(nil, s.reqWidth, stat)
|
||||
b.recoveredPanic = p
|
||||
}
|
||||
frame := renderFrame{
|
||||
reader: reader,
|
||||
lines: lines + 1,
|
||||
shutdown: s.completed || s.aborted,
|
||||
}
|
||||
if frame.shutdown {
|
||||
b.cancel()
|
||||
}
|
||||
b.frameCh <- &frame
|
||||
}()
|
||||
if b.recoveredPanic == nil {
|
||||
reader = s.draw(stat)
|
||||
r, err := s.draw(stat)
|
||||
if err != nil {
|
||||
b.frameCh <- &renderFrame{err: err}
|
||||
return
|
||||
}
|
||||
reader, lines = s.extender(reader, s.reqWidth, stat)
|
||||
}:
|
||||
rows = append(rows, r)
|
||||
if s.extender != nil {
|
||||
rows, err = s.extender(rows, stat)
|
||||
if err != nil {
|
||||
b.frameCh <- &renderFrame{err: err}
|
||||
return
|
||||
}
|
||||
}
|
||||
frame := &renderFrame{
|
||||
rows: rows,
|
||||
shutdown: s.shutdown,
|
||||
rmOnComplete: s.rmOnComplete,
|
||||
noPop: s.noPop,
|
||||
done: done,
|
||||
}
|
||||
if s.completed || s.aborted {
|
||||
// post increment makes sure OnComplete decorators are rendered
|
||||
s.shutdown++
|
||||
}
|
||||
b.frameCh <- frame
|
||||
}
|
||||
select {
|
||||
case b.operateState <- fn:
|
||||
case <-b.done:
|
||||
var reader io.Reader
|
||||
var lines int
|
||||
stat, s := newStatistics(tw, b.bs), b.bs
|
||||
if b.recoveredPanic == nil {
|
||||
reader = s.draw(stat)
|
||||
}
|
||||
reader, lines = s.extender(reader, s.reqWidth, stat)
|
||||
b.frameCh <- &renderFrame{
|
||||
reader: reader,
|
||||
lines: lines + 1,
|
||||
}
|
||||
done = true
|
||||
fn(b.bs)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bar) forceRefresh() {
|
||||
func (b *Bar) triggerCompletion(s *bState) {
|
||||
if s.autoRefresh {
|
||||
// Technically this call isn't required, but if refresh rate is set to
|
||||
// one hour for example and bar completes within a few minutes p.Wait()
|
||||
// will wait for one hour. This call helps to avoid unnecessary waiting.
|
||||
go b.tryEarlyRefresh(s.renderReq)
|
||||
} else {
|
||||
b.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bar) tryEarlyRefresh(renderReq chan<- time.Time) {
|
||||
var anyOtherRunning bool
|
||||
b.container.traverseBars(func(bar *Bar) bool {
|
||||
anyOtherRunning = b != bar && bar.isRunning()
|
||||
return !anyOtherRunning
|
||||
anyOtherRunning = b != bar && bar.IsRunning()
|
||||
return anyOtherRunning
|
||||
})
|
||||
if !anyOtherRunning {
|
||||
for {
|
||||
select {
|
||||
case b.container.refreshCh <- time.Now():
|
||||
time.Sleep(prr)
|
||||
case renderReq <- time.Now():
|
||||
case <-b.done:
|
||||
return
|
||||
}
|
||||
|
|
@ -422,20 +475,8 @@ func (b *Bar) forceRefresh() {
|
|||
}
|
||||
}
|
||||
|
||||
func (b *Bar) isRunning() bool {
|
||||
result := make(chan bool)
|
||||
select {
|
||||
case b.operateState <- func(s *bState) {
|
||||
result <- !s.completed && !s.aborted
|
||||
}:
|
||||
return <-result
|
||||
case <-b.done:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bar) wSyncTable() [][]chan int {
|
||||
result := make(chan [][]chan int)
|
||||
func (b *Bar) wSyncTable() syncTable {
|
||||
result := make(chan syncTable)
|
||||
select {
|
||||
case b.operateState <- func(s *bState) { result <- s.wSyncTable() }:
|
||||
return <-result
|
||||
|
|
@ -444,96 +485,119 @@ func (b *Bar) wSyncTable() [][]chan int {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *bState) draw(stat decor.Statistics) io.Reader {
|
||||
func (s *bState) draw(stat decor.Statistics) (io.Reader, error) {
|
||||
r, err := s.drawImpl(stat)
|
||||
if err != nil {
|
||||
for _, b := range s.buffers {
|
||||
b.Reset()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return io.MultiReader(r, strings.NewReader("\n")), nil
|
||||
}
|
||||
|
||||
func (s *bState) drawImpl(stat decor.Statistics) (io.Reader, error) {
|
||||
decorFiller := func(buf *bytes.Buffer, decorators []decor.Decorator) (res struct {
|
||||
width int
|
||||
truncate bool
|
||||
err error
|
||||
}) {
|
||||
res.width = stat.AvailableWidth
|
||||
for _, d := range decorators {
|
||||
str := d.Decor(stat)
|
||||
if stat.AvailableWidth > 0 {
|
||||
stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
|
||||
if res.err == nil {
|
||||
_, res.err = buf.WriteString(str)
|
||||
}
|
||||
}
|
||||
}
|
||||
res.truncate = stat.AvailableWidth < 0
|
||||
return res
|
||||
}
|
||||
|
||||
bufP, bufB, bufA := s.buffers[0], s.buffers[1], s.buffers[2]
|
||||
nlr := strings.NewReader("\n")
|
||||
tw := stat.AvailableWidth
|
||||
for _, d := range s.pDecorators {
|
||||
str := d.Decor(stat)
|
||||
stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
|
||||
bufP.WriteString(str)
|
||||
|
||||
resP := decorFiller(bufP, s.pDecorators)
|
||||
resA := decorFiller(bufA, s.aDecorators)
|
||||
|
||||
for _, err := range []error{resP.err, resA.err} {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if stat.AvailableWidth < 1 {
|
||||
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufP.String()), tw, "…"))
|
||||
|
||||
if resP.truncate {
|
||||
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufP.String()), resP.width, "…"))
|
||||
bufP.Reset()
|
||||
return io.MultiReader(trunc, nlr)
|
||||
}
|
||||
|
||||
if !s.trimSpace && stat.AvailableWidth > 1 {
|
||||
stat.AvailableWidth -= 2
|
||||
bufB.WriteByte(' ')
|
||||
defer bufB.WriteByte(' ')
|
||||
}
|
||||
|
||||
tw = stat.AvailableWidth
|
||||
for _, d := range s.aDecorators {
|
||||
str := d.Decor(stat)
|
||||
stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
|
||||
bufA.WriteString(str)
|
||||
}
|
||||
if stat.AvailableWidth < 1 {
|
||||
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufA.String()), tw, "…"))
|
||||
bufA.Reset()
|
||||
return io.MultiReader(bufP, bufB, trunc, nlr)
|
||||
return trunc, nil
|
||||
}
|
||||
|
||||
s.filler.Fill(bufB, s.reqWidth, stat)
|
||||
if resA.truncate {
|
||||
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufA.String()), resA.width, "…"))
|
||||
bufA.Reset()
|
||||
return io.MultiReader(bufP, trunc), nil
|
||||
}
|
||||
|
||||
return io.MultiReader(bufP, bufB, 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++
|
||||
if !s.trimSpace && stat.AvailableWidth >= 2 {
|
||||
stat.AvailableWidth -= 2
|
||||
writeFiller := func(buf *bytes.Buffer) error {
|
||||
return s.filler.Fill(buf, stat)
|
||||
}
|
||||
for _, fn := range []func(*bytes.Buffer) error{
|
||||
writeSpace,
|
||||
writeFiller,
|
||||
writeSpace,
|
||||
} {
|
||||
if err := fn(bufB); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err := s.filler.Fill(bufB, stat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
return io.MultiReader(bufP, bufB, bufA), nil
|
||||
}
|
||||
|
||||
func (s *bState) subscribeDecorators() {
|
||||
for _, decorators := range [...][]decor.Decorator{
|
||||
func (s *bState) wSyncTable() (table syncTable) {
|
||||
var count int
|
||||
var row []chan int
|
||||
|
||||
for i, decorators := range [][]decor.Decorator{
|
||||
s.pDecorators,
|
||||
s.aDecorators,
|
||||
} {
|
||||
for _, d := range decorators {
|
||||
d = extractBaseDecorator(d)
|
||||
if d, ok := d.(decor.AverageDecorator); ok {
|
||||
s.averageDecorators = append(s.averageDecorators, d)
|
||||
}
|
||||
if d, ok := d.(decor.EwmaDecorator); ok {
|
||||
s.ewmaDecorators = append(s.ewmaDecorators, d)
|
||||
}
|
||||
if d, ok := d.(decor.ShutdownListener); ok {
|
||||
s.shutdownListeners = append(s.shutdownListeners, d)
|
||||
if ch, ok := d.Sync(); ok {
|
||||
row = append(row, ch)
|
||||
count++
|
||||
}
|
||||
}
|
||||
switch i {
|
||||
case 0:
|
||||
table[i] = row[0:count]
|
||||
default:
|
||||
table[i] = row[len(table[i-1]):count]
|
||||
}
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func (s bState) decoratorEwmaUpdate(dur time.Duration) {
|
||||
wg := new(sync.WaitGroup)
|
||||
func (s bState) decoratorEwmaUpdate(n int64, dur time.Duration) {
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < len(s.ewmaDecorators); i++ {
|
||||
switch d := s.ewmaDecorators[i]; i {
|
||||
case len(s.ewmaDecorators) - 1:
|
||||
d.EwmaUpdate(s.lastIncrement, dur)
|
||||
d.EwmaUpdate(n, dur)
|
||||
default:
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
d.EwmaUpdate(s.lastIncrement, dur)
|
||||
d.EwmaUpdate(n, dur)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
|
@ -542,7 +606,7 @@ func (s bState) decoratorEwmaUpdate(dur time.Duration) {
|
|||
}
|
||||
|
||||
func (s bState) decoratorAverageAdjust(start time.Time) {
|
||||
wg := new(sync.WaitGroup)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < len(s.averageDecorators); i++ {
|
||||
switch d := s.averageDecorators[i]; i {
|
||||
case len(s.averageDecorators) - 1:
|
||||
|
|
@ -559,15 +623,15 @@ func (s bState) decoratorAverageAdjust(start time.Time) {
|
|||
}
|
||||
|
||||
func (s bState) decoratorShutdownNotify() {
|
||||
wg := new(sync.WaitGroup)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < len(s.shutdownListeners); i++ {
|
||||
switch d := s.shutdownListeners[i]; i {
|
||||
case len(s.shutdownListeners) - 1:
|
||||
d.Shutdown()
|
||||
d.OnShutdown()
|
||||
default:
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
d.Shutdown()
|
||||
d.OnShutdown()
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
|
@ -578,6 +642,7 @@ func (s bState) decoratorShutdownNotify() {
|
|||
func newStatistics(tw int, s *bState) decor.Statistics {
|
||||
return decor.Statistics{
|
||||
AvailableWidth: tw,
|
||||
RequestedWidth: s.reqWidth,
|
||||
ID: s.id,
|
||||
Total: s.total,
|
||||
Current: s.current,
|
||||
|
|
@ -587,20 +652,13 @@ func newStatistics(tw int, s *bState) decor.Statistics {
|
|||
}
|
||||
}
|
||||
|
||||
func extractBaseDecorator(d decor.Decorator) decor.Decorator {
|
||||
func unwrap(d decor.Decorator) decor.Decorator {
|
||||
if d, ok := d.(decor.Wrapper); ok {
|
||||
return extractBaseDecorator(d.Base())
|
||||
return unwrap(d.Unwrap())
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func makePanicExtender(p interface{}) extenderFunc {
|
||||
pstr := fmt.Sprint(p)
|
||||
return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) {
|
||||
mr := io.MultiReader(
|
||||
strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")),
|
||||
strings.NewReader("\n"),
|
||||
)
|
||||
return mr, 0
|
||||
}
|
||||
func writeSpace(buf *bytes.Buffer) error {
|
||||
return buf.WriteByte(' ')
|
||||
}
|
||||
|
|
@ -3,17 +3,13 @@ package mpb
|
|||
import (
|
||||
"io"
|
||||
|
||||
"github.com/vbauerster/mpb/v7/decor"
|
||||
"github.com/vbauerster/mpb/v8/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.
|
||||
//
|
||||
type BarFiller interface {
|
||||
Fill(w io.Writer, reqWidth int, stat decor.Statistics)
|
||||
Fill(io.Writer, decor.Statistics) error
|
||||
}
|
||||
|
||||
// BarFillerBuilder interface.
|
||||
|
|
@ -22,17 +18,16 @@ type BarFiller interface {
|
|||
// BarStyle()
|
||||
// SpinnerStyle()
|
||||
// NopStyle()
|
||||
//
|
||||
type BarFillerBuilder interface {
|
||||
Build() BarFiller
|
||||
}
|
||||
|
||||
// BarFillerFunc is function type adapter to convert compatible function
|
||||
// into BarFiller interface.
|
||||
type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
|
||||
type BarFillerFunc func(io.Writer, decor.Statistics) error
|
||||
|
||||
func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
|
||||
f(w, reqWidth, stat)
|
||||
func (f BarFillerFunc) Fill(w io.Writer, stat decor.Statistics) error {
|
||||
return f(w, stat)
|
||||
}
|
||||
|
||||
// BarFillerBuilderFunc is function type adapter to convert compatible
|
||||
|
|
@ -42,9 +37,3 @@ type BarFillerBuilderFunc func() BarFiller
|
|||
func (f BarFillerBuilderFunc) Build() BarFiller {
|
||||
return f()
|
||||
}
|
||||
|
||||
// NewBarFiller constructs a BarFiller from provided BarFillerBuilder.
|
||||
// Deprecated. Prefer using `*Progress.New(...)` directly.
|
||||
func NewBarFiller(b BarFillerBuilder) BarFiller {
|
||||
return b.Build()
|
||||
}
|
||||
|
|
@ -5,8 +5,8 @@ import (
|
|||
|
||||
"github.com/acarl005/stripansi"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/vbauerster/mpb/v7/decor"
|
||||
"github.com/vbauerster/mpb/v7/internal"
|
||||
"github.com/vbauerster/mpb/v8/decor"
|
||||
"github.com/vbauerster/mpb/v8/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -36,8 +36,8 @@ type bFiller struct {
|
|||
components [components]*component
|
||||
tip struct {
|
||||
count uint
|
||||
onComplete *component
|
||||
frames []*component
|
||||
onComplete *component
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,20 +148,22 @@ func (s *barStyle) Build() BarFiller {
|
|||
return bf
|
||||
}
|
||||
|
||||
func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
|
||||
width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
|
||||
brackets := s.components[iLbound].width + s.components[iRbound].width
|
||||
func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) (err error) {
|
||||
width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
|
||||
// don't count brackets as progress
|
||||
width -= brackets
|
||||
width -= (s.components[iLbound].width + s.components[iRbound].width)
|
||||
if width < 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
mustWrite(w, s.components[iLbound].bytes)
|
||||
defer mustWrite(w, s.components[iRbound].bytes)
|
||||
_, err = w.Write(s.components[iLbound].bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if width == 0 {
|
||||
return
|
||||
_, err = w.Write(s.components[iRbound].bytes)
|
||||
return err
|
||||
}
|
||||
|
||||
var filling [][]byte
|
||||
|
|
@ -171,7 +173,7 @@ func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
|
|||
var refWidth int
|
||||
curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width)))
|
||||
|
||||
if stat.Current >= stat.Total {
|
||||
if stat.Completed {
|
||||
tip = s.tip.onComplete
|
||||
} else {
|
||||
tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
|
||||
|
|
@ -230,24 +232,28 @@ func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
|
|||
}
|
||||
|
||||
if s.rev {
|
||||
flush(w, padding, filling)
|
||||
} else {
|
||||
flush(w, filling, padding)
|
||||
filling, padding = padding, filling
|
||||
}
|
||||
err = flush(w, filling, padding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(s.components[iRbound].bytes)
|
||||
return err
|
||||
}
|
||||
|
||||
func flush(w io.Writer, filling, padding [][]byte) {
|
||||
func flush(w io.Writer, filling, padding [][]byte) error {
|
||||
for i := len(filling) - 1; i >= 0; i-- {
|
||||
mustWrite(w, filling[i])
|
||||
_, err := w.Write(filling[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(padding); i++ {
|
||||
mustWrite(w, padding[i])
|
||||
}
|
||||
}
|
||||
|
||||
func mustWrite(w io.Writer, p []byte) {
|
||||
_, err := w.Write(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
_, err := w.Write(padding[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -3,12 +3,14 @@ package mpb
|
|||
import (
|
||||
"io"
|
||||
|
||||
"github.com/vbauerster/mpb/v7/decor"
|
||||
"github.com/vbauerster/mpb/v8/decor"
|
||||
)
|
||||
|
||||
// NopStyle provides BarFillerBuilder which builds NOP BarFiller.
|
||||
func NopStyle() BarFillerBuilder {
|
||||
return BarFillerBuilderFunc(func() BarFiller {
|
||||
return BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
|
||||
return BarFillerFunc(func(io.Writer, decor.Statistics) error {
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -6,8 +6,8 @@ import (
|
|||
|
||||
"github.com/acarl005/stripansi"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/vbauerster/mpb/v7/decor"
|
||||
"github.com/vbauerster/mpb/v7/internal"
|
||||
"github.com/vbauerster/mpb/v8/decor"
|
||||
"github.com/vbauerster/mpb/v8/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -63,17 +63,16 @@ func (s *spinnerStyle) Build() BarFiller {
|
|||
return sf
|
||||
}
|
||||
|
||||
func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
|
||||
width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
|
||||
func (s *sFiller) Fill(w io.Writer, stat decor.Statistics) (err error) {
|
||||
width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
|
||||
|
||||
frame := s.frames[s.count%uint(len(s.frames))]
|
||||
frameWidth := runewidth.StringWidth(stripansi.Strip(frame))
|
||||
|
||||
if width < frameWidth {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
rest := width - frameWidth
|
||||
switch s.position {
|
||||
case positionLeft:
|
||||
|
|
@ -84,8 +83,6 @@ func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
|
|||
str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
|
||||
_, err = io.WriteString(w, str)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.count++
|
||||
return err
|
||||
}
|
||||
|
|
@ -4,44 +4,40 @@ import (
|
|||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/vbauerster/mpb/v7/decor"
|
||||
"github.com/vbauerster/mpb/v8/decor"
|
||||
)
|
||||
|
||||
// BarOption is a func option to alter default behavior of a bar.
|
||||
type BarOption func(*bState)
|
||||
|
||||
func skipNil(decorators []decor.Decorator) (filtered []decor.Decorator) {
|
||||
for _, d := range decorators {
|
||||
if d != nil {
|
||||
filtered = append(filtered, d)
|
||||
func inspect(decorators []decor.Decorator) (dest []decor.Decorator) {
|
||||
for _, decorator := range decorators {
|
||||
if decorator == nil {
|
||||
continue
|
||||
}
|
||||
if d, ok := decorator.(interface {
|
||||
PlaceHolders() []decor.Decorator
|
||||
}); ok {
|
||||
dest = append(dest, d.PlaceHolders()...)
|
||||
}
|
||||
dest = append(dest, decorator)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
decorators = inspect(decorators)
|
||||
return func(s *bState) {
|
||||
s.addDecorators(&s.aDecorators, skipNil(decorators)...)
|
||||
s.aDecorators = decorators
|
||||
}
|
||||
}
|
||||
|
||||
// PrependDecorators let you inject decorators to the bar's left side.
|
||||
func PrependDecorators(decorators ...decor.Decorator) BarOption {
|
||||
decorators = inspect(decorators)
|
||||
return func(s *bState) {
|
||||
s.addDecorators(&s.pDecorators, skipNil(decorators)...)
|
||||
s.pDecorators = decorators
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,16 +56,11 @@ func BarWidth(width int) BarOption {
|
|||
}
|
||||
|
||||
// BarQueueAfter puts this (being constructed) bar into the queue.
|
||||
// BarPriority will be inherited from the argument bar.
|
||||
// When argument bar completes or aborts queued bar replaces its place.
|
||||
// If sync is true queued bar is suspended until argument bar completes
|
||||
// or aborts.
|
||||
func BarQueueAfter(bar *Bar, sync bool) BarOption {
|
||||
if bar == nil {
|
||||
return nil
|
||||
}
|
||||
func BarQueueAfter(bar *Bar) BarOption {
|
||||
return func(s *bState) {
|
||||
s.afterBar = bar
|
||||
s.sync = sync
|
||||
s.waitBar = bar
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +68,7 @@ func BarQueueAfter(bar *Bar, sync bool) BarOption {
|
|||
// on complete event.
|
||||
func BarRemoveOnComplete() BarOption {
|
||||
return func(s *bState) {
|
||||
s.dropOnComplete = true
|
||||
s.rmOnComplete = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,15 +81,12 @@ func BarFillerClearOnComplete() BarOption {
|
|||
// 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) {
|
||||
return BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
|
||||
if st.Completed {
|
||||
_, err := io.WriteString(w, message)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
base.Fill(w, reqWidth, st)
|
||||
return err
|
||||
}
|
||||
return base.Fill(w, st)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -106,34 +94,66 @@ func BarFillerOnComplete(message string) BarOption {
|
|||
// BarFillerMiddleware provides a way to augment the underlying BarFiller.
|
||||
func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption {
|
||||
return func(s *bState) {
|
||||
s.middleware = middle
|
||||
if middle == nil {
|
||||
return
|
||||
}
|
||||
s.filler = middle(s.filler)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// will be on top. This option isn't effective with `BarQueueAfter` option.
|
||||
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 {
|
||||
// BarExtender extends bar with arbitrary lines. Provided BarFiller will be
|
||||
// called at each render/flush cycle. Any lines written to the underlying
|
||||
// io.Writer will extend the bar either in above (rev = true) or below
|
||||
// (rev = false) direction.
|
||||
func BarExtender(filler BarFiller, rev bool) BarOption {
|
||||
if filler == nil {
|
||||
return nil
|
||||
}
|
||||
fn := makeExtenderFunc(filler, rev)
|
||||
return func(s *bState) {
|
||||
s.extender = makeExtenderFunc(filler)
|
||||
s.extender = fn
|
||||
}
|
||||
}
|
||||
|
||||
func makeExtenderFunc(filler BarFiller) extenderFunc {
|
||||
func makeExtenderFunc(filler BarFiller, rev bool) 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"))
|
||||
base := func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
|
||||
err := filler.Fill(buf, stat)
|
||||
if err != nil {
|
||||
buf.Reset()
|
||||
return rows, err
|
||||
}
|
||||
for {
|
||||
b, err := buf.ReadBytes('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
rows = append(rows, bytes.NewReader(b))
|
||||
}
|
||||
buf.Reset()
|
||||
return rows, err
|
||||
}
|
||||
|
||||
if !rev {
|
||||
return base
|
||||
}
|
||||
return func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
|
||||
rows, err := base(rows, stat)
|
||||
if err != nil {
|
||||
return rows, err
|
||||
}
|
||||
for left, right := 0, len(rows)-1; left < right; left, right = left+1, right-1 {
|
||||
rows[left], rows[right] = rows[right], rows[left]
|
||||
}
|
||||
return rows, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +172,7 @@ func BarNoPop() BarOption {
|
|||
}
|
||||
}
|
||||
|
||||
// BarOptional will invoke provided option only when cond is true.
|
||||
// BarOptional will return provided option only when cond is true.
|
||||
func BarOptional(option BarOption, cond bool) BarOption {
|
||||
if cond {
|
||||
return option
|
||||
|
|
@ -160,11 +180,26 @@ func BarOptional(option BarOption, cond bool) BarOption {
|
|||
return nil
|
||||
}
|
||||
|
||||
// BarOptOn will invoke provided option only when higher order predicate
|
||||
// evaluates to true.
|
||||
// BarOptOn will return provided option only when predicate evaluates to true.
|
||||
func BarOptOn(option BarOption, predicate func() bool) BarOption {
|
||||
if predicate() {
|
||||
return option
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BarFuncOptional will call option and return its value only when cond is true.
|
||||
func BarFuncOptional(option func() BarOption, cond bool) BarOption {
|
||||
if cond {
|
||||
return option()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BarFuncOptOn will call option and return its value only when predicate evaluates to true.
|
||||
func BarFuncOptOn(option func() BarOption, predicate func() bool) BarOption {
|
||||
if predicate() {
|
||||
return option()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package mpb
|
|||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -31,10 +30,10 @@ func WithWidth(width int) ContainerOption {
|
|||
}
|
||||
}
|
||||
|
||||
// WithRefreshRate overrides default 120ms refresh rate.
|
||||
// WithRefreshRate overrides default 150ms refresh rate.
|
||||
func WithRefreshRate(d time.Duration) ContainerOption {
|
||||
return func(s *pState) {
|
||||
s.rr = d
|
||||
s.refreshRate = d
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +41,7 @@ func WithRefreshRate(d time.Duration) ContainerOption {
|
|||
// Refresh will occur upon receive value from provided ch.
|
||||
func WithManualRefresh(ch <-chan interface{}) ContainerOption {
|
||||
return func(s *pState) {
|
||||
s.externalRefresh = ch
|
||||
s.manualRC = ch
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,32 +51,26 @@ func WithManualRefresh(ch <-chan interface{}) ContainerOption {
|
|||
// rendering will start as soon as provided chan is closed.
|
||||
func WithRenderDelay(ch <-chan struct{}) ContainerOption {
|
||||
return func(s *pState) {
|
||||
s.renderDelay = ch
|
||||
s.delayRC = ch
|
||||
}
|
||||
}
|
||||
|
||||
// WithShutdownNotifier provided chanel will be closed, after all bars
|
||||
// have been rendered.
|
||||
func WithShutdownNotifier(ch chan struct{}) ContainerOption {
|
||||
// WithShutdownNotifier value of type `[]*mpb.Bar` will be send into provided
|
||||
// channel upon container shutdown.
|
||||
func WithShutdownNotifier(ch chan<- interface{}) ContainerOption {
|
||||
return func(s *pState) {
|
||||
select {
|
||||
case <-ch:
|
||||
default:
|
||||
s.shutdownNotifier = ch
|
||||
}
|
||||
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.
|
||||
// WithOutput overrides default os.Stdout output. If underlying io.Writer
|
||||
// is not a terminal then auto refresh is disabled unless WithAutoRefresh
|
||||
// option is set.
|
||||
func WithOutput(w io.Writer) ContainerOption {
|
||||
if w == nil {
|
||||
w = io.Discard
|
||||
}
|
||||
return func(s *pState) {
|
||||
if w == nil {
|
||||
s.output = ioutil.Discard
|
||||
s.outputDiscarded = true
|
||||
return
|
||||
}
|
||||
s.output = w
|
||||
}
|
||||
}
|
||||
|
|
@ -85,21 +78,31 @@ func WithOutput(w io.Writer) ContainerOption {
|
|||
// WithDebugOutput sets debug output.
|
||||
func WithDebugOutput(w io.Writer) ContainerOption {
|
||||
if w == nil {
|
||||
return nil
|
||||
w = io.Discard
|
||||
}
|
||||
return func(s *pState) {
|
||||
s.debugOut = w
|
||||
}
|
||||
}
|
||||
|
||||
// PopCompletedMode will pop and stop rendering completed bars.
|
||||
// WithAutoRefresh force auto refresh regardless of what output is set to.
|
||||
// Applicable only if not WithManualRefresh set.
|
||||
func WithAutoRefresh() ContainerOption {
|
||||
return func(s *pState) {
|
||||
s.autoRefresh = true
|
||||
}
|
||||
}
|
||||
|
||||
// PopCompletedMode will pop completed bars to the top.
|
||||
// To stop rendering bar after it has been popped, use
|
||||
// mpb.BarRemoveOnComplete() option on that bar.
|
||||
func PopCompletedMode() ContainerOption {
|
||||
return func(s *pState) {
|
||||
s.popCompleted = true
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerOptional will invoke provided option only when cond is true.
|
||||
// ContainerOptional will return provided option only when cond is true.
|
||||
func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
|
||||
if cond {
|
||||
return option
|
||||
|
|
@ -107,11 +110,26 @@ func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ContainerOptOn will invoke provided option only when higher order
|
||||
// predicate evaluates to true.
|
||||
// ContainerOptOn will return provided option only when predicate evaluates to true.
|
||||
func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption {
|
||||
if predicate() {
|
||||
return option
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainerFuncOptional will call option and return its value only when cond is true.
|
||||
func ContainerFuncOptional(option func() ContainerOption, cond bool) ContainerOption {
|
||||
if cond {
|
||||
return option()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainerFuncOptOn will call option and return its value only when predicate evaluates to true.
|
||||
func ContainerFuncOptOn(option func() ContainerOption, predicate func() bool) ContainerOption {
|
||||
if predicate() {
|
||||
return option()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
|
||||
|
||||
package cwriter
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// +build aix linux
|
||||
//go:build aix || linux
|
||||
|
||||
package cwriter
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// +build solaris
|
||||
//go:build solaris
|
||||
|
||||
package cwriter
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// +build zos
|
||||
//go:build zos
|
||||
|
||||
package cwriter
|
||||
|
||||
59
vendor/github.com/vbauerster/mpb/v8/cwriter/writer.go
generated
vendored
Normal file
59
vendor/github.com/vbauerster/mpb/v8/cwriter/writer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package cwriter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// https://github.com/dylanaraps/pure-sh-bible#cursor-movement
|
||||
const (
|
||||
escOpen = "\x1b["
|
||||
cuuAndEd = "A\x1b[J"
|
||||
)
|
||||
|
||||
// ErrNotTTY not a TeleTYpewriter error.
|
||||
var ErrNotTTY = errors.New("not a terminal")
|
||||
|
||||
// New returns a new Writer with defaults.
|
||||
func New(out io.Writer) *Writer {
|
||||
w := &Writer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
out: out,
|
||||
termSize: func(_ int) (int, int, error) {
|
||||
return -1, -1, ErrNotTTY
|
||||
},
|
||||
}
|
||||
if f, ok := out.(*os.File); ok {
|
||||
w.fd = int(f.Fd())
|
||||
if IsTerminal(w.fd) {
|
||||
w.terminal = true
|
||||
w.termSize = func(fd int) (int, int, error) {
|
||||
return GetSize(fd)
|
||||
}
|
||||
}
|
||||
}
|
||||
bb := make([]byte, 16)
|
||||
w.ew = escWriter(bb[:copy(bb, []byte(escOpen))])
|
||||
return w
|
||||
}
|
||||
|
||||
// IsTerminal tells whether underlying io.Writer is terminal.
|
||||
func (w *Writer) IsTerminal() bool {
|
||||
return w.terminal
|
||||
}
|
||||
|
||||
// GetTermSize returns WxH of underlying terminal.
|
||||
func (w *Writer) GetTermSize() (width, height int, err error) {
|
||||
return w.termSize(w.fd)
|
||||
}
|
||||
|
||||
type escWriter []byte
|
||||
|
||||
func (b escWriter) ansiCuuAndEd(out io.Writer, n int) error {
|
||||
b = strconv.AppendInt(b, int64(n), 10)
|
||||
_, err := out.Write(append(b, []byte(cuuAndEd)...))
|
||||
return err
|
||||
}
|
||||
48
vendor/github.com/vbauerster/mpb/v8/cwriter/writer_posix.go
generated
vendored
Normal file
48
vendor/github.com/vbauerster/mpb/v8/cwriter/writer_posix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//go:build !windows
|
||||
|
||||
package cwriter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Writer is a buffered terminal writer, which moves cursor N lines up
|
||||
// on each flush except the first one, where N is a number of lines of
|
||||
// a previous flush.
|
||||
type Writer struct {
|
||||
*bytes.Buffer
|
||||
out io.Writer
|
||||
ew escWriter
|
||||
fd int
|
||||
terminal bool
|
||||
termSize func(int) (int, int, error)
|
||||
}
|
||||
|
||||
// Flush flushes the underlying buffer.
|
||||
// It's caller's responsibility to pass correct number of lines.
|
||||
func (w *Writer) Flush(lines int) error {
|
||||
_, err := w.WriteTo(w.out)
|
||||
// some terminals interpret 'cursor up 0' as 'cursor up 1'
|
||||
if err == nil && lines > 0 {
|
||||
err = w.ew.ansiCuuAndEd(w, lines)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
// +build windows
|
||||
//go:build windows
|
||||
|
||||
package cwriter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
|
@ -15,10 +17,37 @@ var (
|
|||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||
)
|
||||
|
||||
func (w *Writer) clearLines() error {
|
||||
if !w.isTerminal {
|
||||
// Writer is a buffered terminal writer, which moves cursor N lines up
|
||||
// on each flush except the first one, where N is a number of lines of
|
||||
// a previous flush.
|
||||
type Writer struct {
|
||||
*bytes.Buffer
|
||||
out io.Writer
|
||||
ew escWriter
|
||||
lines int
|
||||
fd int
|
||||
terminal bool
|
||||
termSize func(int) (int, int, error)
|
||||
}
|
||||
|
||||
// Flush flushes the underlying buffer.
|
||||
// It's caller's responsibility to pass correct number of lines.
|
||||
func (w *Writer) Flush(lines int) error {
|
||||
if w.lines > 0 {
|
||||
err := w.clearLines(w.lines)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.lines = lines
|
||||
_, err := w.WriteTo(w.out)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Writer) clearLines(n int) error {
|
||||
if !w.terminal {
|
||||
// hope it's cygwin or similar
|
||||
return w.ansiCuuAndEd()
|
||||
return w.ew.ansiCuuAndEd(w.out, n)
|
||||
}
|
||||
|
||||
var info windows.ConsoleScreenBufferInfo
|
||||
|
|
@ -26,7 +55,7 @@ func (w *Writer) clearLines() error {
|
|||
return err
|
||||
}
|
||||
|
||||
info.CursorPosition.Y -= int16(w.lines)
|
||||
info.CursorPosition.Y -= int16(n)
|
||||
if info.CursorPosition.Y < 0 {
|
||||
info.CursorPosition.Y = 0
|
||||
}
|
||||
|
|
@ -40,7 +69,7 @@ func (w *Writer) clearLines() error {
|
|||
X: info.Window.Left,
|
||||
Y: info.CursorPosition.Y,
|
||||
}
|
||||
count := uint32(info.Size.X) * uint32(w.lines)
|
||||
count := uint32(info.Size.X) * uint32(n)
|
||||
_, _, _ = procFillConsoleOutputCharacter.Call(
|
||||
uintptr(w.fd),
|
||||
uintptr(' '),
|
||||
|
|
@ -52,7 +81,6 @@ func (w *Writer) clearLines() error {
|
|||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
package decor
|
||||
|
||||
var _ Decorator = (*any)(nil)
|
||||
|
||||
// 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}
|
||||
}
|
||||
|
|
@ -2,13 +2,6 @@ package decor
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
UnitKiB
|
||||
UnitKB
|
||||
)
|
||||
|
||||
// CountersNoUnit is a wrapper around Counters with no unit param.
|
||||
|
|
@ -17,55 +10,60 @@ func CountersNoUnit(pairFmt string, wcc ...WC) Decorator {
|
|||
}
|
||||
|
||||
// CountersKibiByte is a wrapper around Counters with predefined unit
|
||||
// UnitKiB (bytes/1024).
|
||||
// as SizeB1024(0).
|
||||
func CountersKibiByte(pairFmt string, wcc ...WC) Decorator {
|
||||
return Counters(UnitKiB, pairFmt, wcc...)
|
||||
return Counters(SizeB1024(0), pairFmt, wcc...)
|
||||
}
|
||||
|
||||
// CountersKiloByte is a wrapper around Counters with predefined unit
|
||||
// UnitKB (bytes/1000).
|
||||
// as SizeB1000(0).
|
||||
func CountersKiloByte(pairFmt string, wcc ...WC) Decorator {
|
||||
return Counters(UnitKB, pairFmt, wcc...)
|
||||
return Counters(SizeB1000(0), pairFmt, wcc...)
|
||||
}
|
||||
|
||||
// Counters decorator with dynamic unit measure adjustment.
|
||||
//
|
||||
// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
|
||||
// `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
|
||||
//
|
||||
// `pairFmt` printf compatible verbs for current and total pair
|
||||
// `pairFmt` printf compatible verbs for current and total
|
||||
//
|
||||
// `wcc` optional WC config
|
||||
//
|
||||
// pairFmt example if unit=UnitKB:
|
||||
// pairFmt example if unit=SizeB1000(0):
|
||||
//
|
||||
// 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:
|
||||
// pairFmt="%.1f / %.1f" output: "1.0MB / 12.0MB"
|
||||
// pairFmt="% .1f / % .1f" output: "1.0 MB / 12.0 MB"
|
||||
// pairFmt="%f / %f" output: "1.000000MB / 12.000000MB"
|
||||
// pairFmt="% f / % f" output: "1.000000 MB / 12.000000 MB"
|
||||
func Counters(unit interface{}, pairFmt string, wcc ...WC) Decorator {
|
||||
producer := func() DecorFunc {
|
||||
switch unit.(type) {
|
||||
case SizeB1024:
|
||||
if pairFmt == "" {
|
||||
pairFmt = "% d / % d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(pairFmt, SizeB1024(s.Current), SizeB1024(s.Total))
|
||||
}
|
||||
case UnitKB:
|
||||
case SizeB1000:
|
||||
if pairFmt == "" {
|
||||
pairFmt = "% d / % d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(pairFmt, SizeB1000(s.Current), SizeB1000(s.Total))
|
||||
}
|
||||
default:
|
||||
if pairFmt == "" {
|
||||
pairFmt = "%d / %d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(pairFmt, s.Current, s.Total)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Any(producer(unit, pairFmt), wcc...)
|
||||
return Any(producer(), wcc...)
|
||||
}
|
||||
|
||||
// TotalNoUnit is a wrapper around Total with no unit param.
|
||||
|
|
@ -74,56 +72,60 @@ func TotalNoUnit(format string, wcc ...WC) Decorator {
|
|||
}
|
||||
|
||||
// TotalKibiByte is a wrapper around Total with predefined unit
|
||||
// UnitKiB (bytes/1024).
|
||||
// as SizeB1024(0).
|
||||
func TotalKibiByte(format string, wcc ...WC) Decorator {
|
||||
return Total(UnitKiB, format, wcc...)
|
||||
return Total(SizeB1024(0), format, wcc...)
|
||||
}
|
||||
|
||||
// TotalKiloByte is a wrapper around Total with predefined unit
|
||||
// UnitKB (bytes/1000).
|
||||
// as SizeB1000(0).
|
||||
func TotalKiloByte(format string, wcc ...WC) Decorator {
|
||||
return Total(UnitKB, format, wcc...)
|
||||
return Total(SizeB1000(0), format, wcc...)
|
||||
}
|
||||
|
||||
// Total decorator with dynamic unit measure adjustment.
|
||||
//
|
||||
// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
|
||||
// `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
|
||||
//
|
||||
// `format` printf compatible verb for Total
|
||||
//
|
||||
// `wcc` optional WC config
|
||||
//
|
||||
// format example if unit=UnitKiB:
|
||||
// format example if unit=SizeB1024(0):
|
||||
//
|
||||
// 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:
|
||||
// format="%.1f" output: "12.0MiB"
|
||||
// format="% .1f" output: "12.0 MiB"
|
||||
// format="%f" output: "12.000000MiB"
|
||||
// format="% f" output: "12.000000 MiB"
|
||||
func Total(unit interface{}, format string, wcc ...WC) Decorator {
|
||||
producer := func() DecorFunc {
|
||||
switch unit.(type) {
|
||||
case SizeB1024:
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, SizeB1024(s.Total))
|
||||
}
|
||||
case UnitKB:
|
||||
case SizeB1000:
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, SizeB1000(s.Total))
|
||||
}
|
||||
default:
|
||||
if format == "" {
|
||||
format = "%d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, s.Total)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Any(producer(unit, format), wcc...)
|
||||
return Any(producer(), wcc...)
|
||||
}
|
||||
|
||||
// CurrentNoUnit is a wrapper around Current with no unit param.
|
||||
|
|
@ -132,56 +134,60 @@ func CurrentNoUnit(format string, wcc ...WC) Decorator {
|
|||
}
|
||||
|
||||
// CurrentKibiByte is a wrapper around Current with predefined unit
|
||||
// UnitKiB (bytes/1024).
|
||||
// as SizeB1024(0).
|
||||
func CurrentKibiByte(format string, wcc ...WC) Decorator {
|
||||
return Current(UnitKiB, format, wcc...)
|
||||
return Current(SizeB1024(0), format, wcc...)
|
||||
}
|
||||
|
||||
// CurrentKiloByte is a wrapper around Current with predefined unit
|
||||
// UnitKB (bytes/1000).
|
||||
// as SizeB1000(0).
|
||||
func CurrentKiloByte(format string, wcc ...WC) Decorator {
|
||||
return Current(UnitKB, format, wcc...)
|
||||
return Current(SizeB1000(0), format, wcc...)
|
||||
}
|
||||
|
||||
// Current decorator with dynamic unit measure adjustment.
|
||||
//
|
||||
// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
|
||||
// `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
|
||||
//
|
||||
// `format` printf compatible verb for Current
|
||||
//
|
||||
// `wcc` optional WC config
|
||||
//
|
||||
// format example if unit=UnitKiB:
|
||||
// format example if unit=SizeB1024(0):
|
||||
//
|
||||
// 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:
|
||||
// format="%.1f" output: "12.0MiB"
|
||||
// format="% .1f" output: "12.0 MiB"
|
||||
// format="%f" output: "12.000000MiB"
|
||||
// format="% f" output: "12.000000 MiB"
|
||||
func Current(unit interface{}, format string, wcc ...WC) Decorator {
|
||||
producer := func() DecorFunc {
|
||||
switch unit.(type) {
|
||||
case SizeB1024:
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, SizeB1024(s.Current))
|
||||
}
|
||||
case UnitKB:
|
||||
case SizeB1000:
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, SizeB1000(s.Current))
|
||||
}
|
||||
default:
|
||||
if format == "" {
|
||||
format = "%d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, s.Current)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Any(producer(unit, format), wcc...)
|
||||
return Any(producer(), wcc...)
|
||||
}
|
||||
|
||||
// InvertedCurrentNoUnit is a wrapper around InvertedCurrent with no unit param.
|
||||
|
|
@ -190,54 +196,58 @@ func InvertedCurrentNoUnit(format string, wcc ...WC) Decorator {
|
|||
}
|
||||
|
||||
// InvertedCurrentKibiByte is a wrapper around InvertedCurrent with predefined unit
|
||||
// UnitKiB (bytes/1024).
|
||||
// as SizeB1024(0).
|
||||
func InvertedCurrentKibiByte(format string, wcc ...WC) Decorator {
|
||||
return InvertedCurrent(UnitKiB, format, wcc...)
|
||||
return InvertedCurrent(SizeB1024(0), format, wcc...)
|
||||
}
|
||||
|
||||
// InvertedCurrentKiloByte is a wrapper around InvertedCurrent with predefined unit
|
||||
// UnitKB (bytes/1000).
|
||||
// as SizeB1000(0).
|
||||
func InvertedCurrentKiloByte(format string, wcc ...WC) Decorator {
|
||||
return InvertedCurrent(UnitKB, format, wcc...)
|
||||
return InvertedCurrent(SizeB1000(0), format, wcc...)
|
||||
}
|
||||
|
||||
// InvertedCurrent decorator with dynamic unit measure adjustment.
|
||||
//
|
||||
// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
|
||||
// `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
|
||||
//
|
||||
// `format` printf compatible verb for InvertedCurrent
|
||||
//
|
||||
// `wcc` optional WC config
|
||||
//
|
||||
// format example if unit=UnitKiB:
|
||||
// format example if unit=SizeB1024(0):
|
||||
//
|
||||
// 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:
|
||||
// format="%.1f" output: "12.0MiB"
|
||||
// format="% .1f" output: "12.0 MiB"
|
||||
// format="%f" output: "12.000000MiB"
|
||||
// format="% f" output: "12.000000 MiB"
|
||||
func InvertedCurrent(unit interface{}, format string, wcc ...WC) Decorator {
|
||||
producer := func() DecorFunc {
|
||||
switch unit.(type) {
|
||||
case SizeB1024:
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, SizeB1024(s.Total-s.Current))
|
||||
}
|
||||
case UnitKB:
|
||||
case SizeB1000:
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, SizeB1000(s.Total-s.Current))
|
||||
}
|
||||
default:
|
||||
if format == "" {
|
||||
format = "%d"
|
||||
}
|
||||
return func(s Statistics) string {
|
||||
return fmt.Sprintf(format, s.Total-s.Current)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Any(producer(unit, format), wcc...)
|
||||
return Any(producer(), wcc...)
|
||||
}
|
||||
|
|
@ -44,10 +44,11 @@ const (
|
|||
ET_STYLE_MMSS
|
||||
)
|
||||
|
||||
// Statistics consists of progress related statistics, that Decorator
|
||||
// may need.
|
||||
// Statistics contains fields which are necessary for implementing
|
||||
// `decor.Decorator` and `mpb.BarFiller` interfaces.
|
||||
type Statistics struct {
|
||||
AvailableWidth int
|
||||
AvailableWidth int // calculated width initially equal to terminal width
|
||||
RequestedWidth int // width set by `mpb.WithWidth`
|
||||
ID int
|
||||
Total int64
|
||||
Current int64
|
||||
|
|
@ -92,7 +93,7 @@ type Configurator interface {
|
|||
// it is necessary to implement this interface to retain functionality
|
||||
// of built-in Decorator.
|
||||
type Wrapper interface {
|
||||
Base() Decorator
|
||||
Unwrap() Decorator
|
||||
}
|
||||
|
||||
// EwmaDecorator interface.
|
||||
|
|
@ -112,7 +113,7 @@ type AverageDecorator interface {
|
|||
// If decorator needs to be notified once upon bar shutdown event, so
|
||||
// this is the right interface to implement.
|
||||
type ShutdownListener interface {
|
||||
Shutdown()
|
||||
OnShutdown()
|
||||
}
|
||||
|
||||
// Global convenience instances of WC with sync width bit set.
|
||||
|
|
@ -136,26 +137,27 @@ type WC struct {
|
|||
|
||||
// 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 {
|
||||
func (wc WC) FormatMsg(msg string) string {
|
||||
pureWidth := runewidth.StringWidth(msg)
|
||||
stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
|
||||
maxCell := wc.W
|
||||
viewWidth := runewidth.StringWidth(stripansi.Strip(msg))
|
||||
max := wc.W
|
||||
if (wc.C & DSyncWidth) != 0 {
|
||||
cellCount := stripWidth
|
||||
viewWidth := viewWidth
|
||||
if (wc.C & DextraSpace) != 0 {
|
||||
cellCount++
|
||||
viewWidth++
|
||||
}
|
||||
wc.wsync <- cellCount
|
||||
maxCell = <-wc.wsync
|
||||
wc.wsync <- viewWidth
|
||||
max = <-wc.wsync
|
||||
}
|
||||
return wc.fill(msg, maxCell+(pureWidth-stripWidth))
|
||||
return wc.fill(msg, max-viewWidth+pureWidth)
|
||||
}
|
||||
|
||||
// Init initializes width related config.
|
||||
func (wc *WC) Init() WC {
|
||||
wc.fill = runewidth.FillLeft
|
||||
if (wc.C & DidentRight) != 0 {
|
||||
wc.fill = runewidth.FillRight
|
||||
} else {
|
||||
wc.fill = runewidth.FillLeft
|
||||
}
|
||||
if (wc.C & DSyncWidth) != 0 {
|
||||
// it's deliberate choice to override wsync on each Init() call,
|
||||
|
|
@ -166,7 +168,7 @@ func (wc *WC) Init() WC {
|
|||
}
|
||||
|
||||
// Sync is implementation of Synchronizer interface.
|
||||
func (wc *WC) Sync() (chan int, bool) {
|
||||
func (wc WC) Sync() (chan int, bool) {
|
||||
if (wc.C&DSyncWidth) != 0 && wc.wsync == nil {
|
||||
panic(fmt.Sprintf("%T is not initialized", wc))
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Package decor provides common decorators for "github.com/vbauerster/mpb/v7" module.
|
||||
// Package decor provides common decorators for "github.com/vbauerster/mpb/v8" 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
|
||||
|
|
@ -6,10 +6,10 @@
|
|||
//
|
||||
// Don't:
|
||||
//
|
||||
// p := mpb.New()
|
||||
// name := decor.Name("bar")
|
||||
// p.AddBar(100, mpb.AppendDecorators(name))
|
||||
// p.AddBar(100, mpb.AppendDecorators(name))
|
||||
// p := mpb.New()
|
||||
// name := decor.Name("bar")
|
||||
// p.AddBar(100, mpb.AppendDecorators(name))
|
||||
// p.AddBar(100, mpb.AppendDecorators(name))
|
||||
//
|
||||
// Do:
|
||||
//
|
||||
|
|
@ -9,7 +9,6 @@ import (
|
|||
// `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...)
|
||||
}
|
||||
|
|
@ -21,7 +20,6 @@ func Elapsed(style TimeStyle, wcc ...WC) Decorator {
|
|||
// `startTime` start time
|
||||
//
|
||||
// `wcc` optional WC config
|
||||
//
|
||||
func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
|
||||
var msg string
|
||||
producer := chooseTimeProducer(style)
|
||||
|
|
@ -8,6 +8,13 @@ import (
|
|||
"github.com/VividCortex/ewma"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Decorator = (*movingAverageETA)(nil)
|
||||
_ EwmaDecorator = (*movingAverageETA)(nil)
|
||||
_ Decorator = (*averageETA)(nil)
|
||||
_ AverageDecorator = (*averageETA)(nil)
|
||||
)
|
||||
|
||||
// TimeNormalizer interface. Implementors could be passed into
|
||||
// MovingAverageETA, in order to affect i.e. normalize its output.
|
||||
type TimeNormalizer interface {
|
||||
|
|
@ -22,10 +29,9 @@ 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.
|
||||
// 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 one of the (*Bar).EwmaIncr... family methods.
|
||||
func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
|
||||
var average ewma.MovingAverage
|
||||
if age == 0 {
|
||||
|
|
@ -45,7 +51,6 @@ func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
|
|||
// `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...),
|
||||
|
|
@ -85,7 +90,6 @@ func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) {
|
|||
// `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...)
|
||||
}
|
||||
|
|
@ -99,7 +103,6 @@ func AverageETA(style TimeStyle, wcc ...WC) Decorator {
|
|||
// `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...),
|
||||
|
|
@ -196,8 +199,7 @@ func chooseTimeProducer(style TimeStyle) func(time.Duration) string {
|
|||
}
|
||||
default:
|
||||
return func(remaining time.Duration) string {
|
||||
// strip off nanoseconds
|
||||
return ((remaining / time.Second) * time.Second).String()
|
||||
return remaining.Truncate(time.Second).String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,15 +7,20 @@ import (
|
|||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Decorator = (*mergeDecorator)(nil)
|
||||
_ Wrapper = (*mergeDecorator)(nil)
|
||||
_ Decorator = (*placeHolderDecorator)(nil)
|
||||
)
|
||||
|
||||
// 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 |
|
||||
// +----+--------+---------+--------+
|
||||
//
|
||||
// +----+--------+---------+--------+
|
||||
// | B1 | MERGE(D, P1, Pn) |
|
||||
// +----+--------+---------+--------+
|
||||
// | B2 | D0 | D1 | Dn |
|
||||
// +----+--------+---------+--------+
|
||||
func Merge(decorator Decorator, placeholders ...WC) Decorator {
|
||||
if decorator == nil {
|
||||
return nil
|
||||
|
|
@ -26,7 +31,7 @@ func Merge(decorator Decorator, placeholders ...WC) Decorator {
|
|||
md := &mergeDecorator{
|
||||
Decorator: decorator,
|
||||
wc: decorator.GetConf(),
|
||||
placeHolders: make([]*placeHolderDecorator, len(placeholders)),
|
||||
placeHolders: make([]Decorator, len(placeholders)),
|
||||
}
|
||||
decorator.SetConf(WC{})
|
||||
for i, wc := range placeholders {
|
||||
|
|
@ -41,7 +46,7 @@ func Merge(decorator Decorator, placeholders ...WC) Decorator {
|
|||
type mergeDecorator struct {
|
||||
Decorator
|
||||
wc WC
|
||||
placeHolders []*placeHolderDecorator
|
||||
placeHolders []Decorator
|
||||
}
|
||||
|
||||
func (d *mergeDecorator) GetConf() WC {
|
||||
|
|
@ -52,19 +57,15 @@ 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) PlaceHolders() []Decorator {
|
||||
return d.placeHolders
|
||||
}
|
||||
|
||||
func (d *mergeDecorator) Sync() (chan int, bool) {
|
||||
return d.wc.Sync()
|
||||
}
|
||||
|
||||
func (d *mergeDecorator) Base() Decorator {
|
||||
func (d *mergeDecorator) Unwrap() Decorator {
|
||||
return d.Decorator
|
||||
}
|
||||
|
||||
|
|
@ -77,21 +78,21 @@ func (d *mergeDecorator) Decor(s Statistics) string {
|
|||
cellCount++
|
||||
}
|
||||
|
||||
total := runewidth.StringWidth(d.placeHolders[0].FormatMsg(""))
|
||||
total := runewidth.StringWidth(d.placeHolders[0].GetConf().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]
|
||||
wc := d.placeHolders[i].GetConf()
|
||||
width := pw - diff
|
||||
if (ph.WC.C & DextraSpace) != 0 {
|
||||
if (wc.C & DextraSpace) != 0 {
|
||||
width--
|
||||
if width < 0 {
|
||||
width = 0
|
||||
}
|
||||
}
|
||||
max := runewidth.StringWidth(ph.FormatMsg(strings.Repeat(" ", width)))
|
||||
max := runewidth.StringWidth(wc.FormatMsg(strings.Repeat(" ", width)))
|
||||
total += max
|
||||
diff = max - pw
|
||||
}
|
||||
|
|
@ -7,6 +7,12 @@ import (
|
|||
"github.com/VividCortex/ewma"
|
||||
)
|
||||
|
||||
var (
|
||||
_ ewma.MovingAverage = (*threadSafeMovingAverage)(nil)
|
||||
_ ewma.MovingAverage = (*medianWindow)(nil)
|
||||
_ sort.Interface = (*medianWindow)(nil)
|
||||
)
|
||||
|
||||
type threadSafeMovingAverage struct {
|
||||
ewma.MovingAverage
|
||||
mu sync.Mutex
|
||||
|
|
@ -6,7 +6,6 @@ package decor
|
|||
// `str` string to display
|
||||
//
|
||||
// `wcc` optional WC config
|
||||
//
|
||||
func Name(str string, wcc ...WC) Decorator {
|
||||
return Any(func(Statistics) string { return str }, wcc...)
|
||||
}
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
package decor
|
||||
|
||||
var (
|
||||
_ Decorator = (*onAbortWrapper)(nil)
|
||||
_ Wrapper = (*onAbortWrapper)(nil)
|
||||
)
|
||||
|
||||
// OnAbort returns decorator, which wraps provided decorator with sole
|
||||
// purpose to display provided message on abort event. It has no effect
|
||||
// if bar.Abort(drop bool) is called with true argument.
|
||||
|
|
@ -7,7 +12,6 @@ package decor
|
|||
// `decorator` Decorator to wrap
|
||||
//
|
||||
// `message` message to display on abort event
|
||||
//
|
||||
func OnAbort(decorator Decorator, message string) Decorator {
|
||||
if decorator == nil {
|
||||
return nil
|
||||
|
|
@ -30,12 +34,11 @@ type onAbortWrapper struct {
|
|||
|
||||
func (d *onAbortWrapper) Decor(s Statistics) string {
|
||||
if s.Aborted {
|
||||
wc := d.GetConf()
|
||||
return wc.FormatMsg(d.msg)
|
||||
return d.GetConf().FormatMsg(d.msg)
|
||||
}
|
||||
return d.Decorator.Decor(s)
|
||||
}
|
||||
|
||||
func (d *onAbortWrapper) Base() Decorator {
|
||||
func (d *onAbortWrapper) Unwrap() Decorator {
|
||||
return d.Decorator
|
||||
}
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
package decor
|
||||
|
||||
var (
|
||||
_ Decorator = (*onCompleteWrapper)(nil)
|
||||
_ Wrapper = (*onCompleteWrapper)(nil)
|
||||
)
|
||||
|
||||
// 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 {
|
||||
if decorator == nil {
|
||||
return nil
|
||||
|
|
@ -29,12 +33,11 @@ type onCompleteWrapper struct {
|
|||
|
||||
func (d *onCompleteWrapper) Decor(s Statistics) string {
|
||||
if s.Completed {
|
||||
wc := d.GetConf()
|
||||
return wc.FormatMsg(d.msg)
|
||||
return d.GetConf().FormatMsg(d.msg)
|
||||
}
|
||||
return d.Decorator.Decor(s)
|
||||
}
|
||||
|
||||
func (d *onCompleteWrapper) Base() Decorator {
|
||||
func (d *onCompleteWrapper) Unwrap() Decorator {
|
||||
return d.Decorator
|
||||
}
|
||||
51
vendor/github.com/vbauerster/mpb/v8/decor/on_condition.go
generated
vendored
Normal file
51
vendor/github.com/vbauerster/mpb/v8/decor/on_condition.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package decor
|
||||
|
||||
// OnCondition applies decorator only if a condition is true.
|
||||
//
|
||||
// `decorator` Decorator
|
||||
//
|
||||
// `cond` bool
|
||||
func OnCondition(decorator Decorator, cond bool) Decorator {
|
||||
return Conditional(cond, decorator, nil)
|
||||
}
|
||||
|
||||
// OnPredicate applies decorator only if a predicate evaluates to true.
|
||||
//
|
||||
// `decorator` Decorator
|
||||
//
|
||||
// `predicate` func() bool
|
||||
func OnPredicate(decorator Decorator, predicate func() bool) Decorator {
|
||||
return Predicative(predicate, decorator, nil)
|
||||
}
|
||||
|
||||
// Conditional returns decorator `a` if condition is true, otherwise
|
||||
// decorator `b`.
|
||||
//
|
||||
// `cond` bool
|
||||
//
|
||||
// `a` Decorator
|
||||
//
|
||||
// `b` Decorator
|
||||
func Conditional(cond bool, a, b Decorator) Decorator {
|
||||
if cond {
|
||||
return a
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
// Predicative returns decorator `a` if predicate evaluates to true,
|
||||
// otherwise decorator `b`.
|
||||
//
|
||||
// `predicate` func() bool
|
||||
//
|
||||
// `a` Decorator
|
||||
//
|
||||
// `b` Decorator
|
||||
func Predicative(predicate func() bool, a, b Decorator) Decorator {
|
||||
if predicate() {
|
||||
return a
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
|
@ -4,30 +4,37 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/vbauerster/mpb/v7/internal"
|
||||
"github.com/vbauerster/mpb/v8/internal"
|
||||
)
|
||||
|
||||
var _ fmt.Formatter = percentageType(0)
|
||||
|
||||
type percentageType float64
|
||||
|
||||
func (s percentageType) Format(st fmt.State, verb rune) {
|
||||
var prec int
|
||||
prec := -1
|
||||
switch verb {
|
||||
case 'd':
|
||||
case 's':
|
||||
prec = -1
|
||||
default:
|
||||
case 'f', 'e', 'E':
|
||||
prec = 6 // default prec of fmt.Printf("%f|%e|%E")
|
||||
fallthrough
|
||||
case 'b', 'g', 'G', 'x', 'X':
|
||||
if p, ok := st.Precision(); ok {
|
||||
prec = p
|
||||
} else {
|
||||
prec = 6
|
||||
}
|
||||
default:
|
||||
verb, prec = 'f', 0
|
||||
}
|
||||
|
||||
mustWriteString(st, strconv.FormatFloat(float64(s), 'f', prec, 64))
|
||||
b := strconv.AppendFloat(make([]byte, 0, 16), float64(s), byte(verb), prec, 64)
|
||||
if st.Flag(' ') {
|
||||
mustWriteString(st, " ")
|
||||
b = append(b, ' ', '%')
|
||||
} else {
|
||||
b = append(b, '%')
|
||||
}
|
||||
_, err := st.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mustWriteString(st, "%")
|
||||
}
|
||||
|
||||
// Percentage returns percentage decorator. It's a wrapper of NewPercentage.
|
||||
|
|
@ -37,13 +44,18 @@ func Percentage(wcc ...WC) Decorator {
|
|||
|
||||
// NewPercentage percentage decorator with custom format string.
|
||||
//
|
||||
// `format` printf compatible verb
|
||||
//
|
||||
// `wcc` optional WC config
|
||||
//
|
||||
// format examples:
|
||||
//
|
||||
// format="%.1f" output: "1.0%"
|
||||
// format="% .1f" output: "1.0 %"
|
||||
// format="%d" output: "1%"
|
||||
// format="% d" output: "1 %"
|
||||
//
|
||||
// format="%.1f" output: "1.0%"
|
||||
// format="% .1f" output: "1.0 %"
|
||||
// format="%f" output: "1.000000%"
|
||||
// format="% f" output: "1.000000 %"
|
||||
func NewPercentage(format string, wcc ...WC) Decorator {
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
|
|
@ -8,6 +8,13 @@ import (
|
|||
//go:generate stringer -type=SizeB1024 -trimprefix=_i
|
||||
//go:generate stringer -type=SizeB1000 -trimprefix=_
|
||||
|
||||
var (
|
||||
_ fmt.Formatter = SizeB1024(0)
|
||||
_ fmt.Stringer = SizeB1024(0)
|
||||
_ fmt.Formatter = SizeB1000(0)
|
||||
_ fmt.Stringer = SizeB1000(0)
|
||||
)
|
||||
|
||||
const (
|
||||
_ib SizeB1024 = iota + 1
|
||||
_iKiB SizeB1024 = 1 << (iota * 10)
|
||||
|
|
@ -22,17 +29,17 @@ const (
|
|||
type SizeB1024 int64
|
||||
|
||||
func (self SizeB1024) Format(st fmt.State, verb rune) {
|
||||
var prec int
|
||||
prec := -1
|
||||
switch verb {
|
||||
case 'd':
|
||||
case 's':
|
||||
prec = -1
|
||||
default:
|
||||
case 'f', 'e', 'E':
|
||||
prec = 6 // default prec of fmt.Printf("%f|%e|%E")
|
||||
fallthrough
|
||||
case 'b', 'g', 'G', 'x', 'X':
|
||||
if p, ok := st.Precision(); ok {
|
||||
prec = p
|
||||
} else {
|
||||
prec = 6
|
||||
}
|
||||
default:
|
||||
verb, prec = 'f', 0
|
||||
}
|
||||
|
||||
var unit SizeB1024
|
||||
|
|
@ -49,11 +56,15 @@ func (self SizeB1024) Format(st fmt.State, verb rune) {
|
|||
unit = _iTiB
|
||||
}
|
||||
|
||||
mustWriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
|
||||
b := strconv.AppendFloat(make([]byte, 0, 24), float64(self)/float64(unit), byte(verb), prec, 64)
|
||||
if st.Flag(' ') {
|
||||
mustWriteString(st, " ")
|
||||
b = append(b, ' ')
|
||||
}
|
||||
b = append(b, []byte(unit.String())...)
|
||||
_, err := st.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mustWriteString(st, unit.String())
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -70,17 +81,17 @@ const (
|
|||
type SizeB1000 int64
|
||||
|
||||
func (self SizeB1000) Format(st fmt.State, verb rune) {
|
||||
var prec int
|
||||
prec := -1
|
||||
switch verb {
|
||||
case 'd':
|
||||
case 's':
|
||||
prec = -1
|
||||
default:
|
||||
case 'f', 'e', 'E':
|
||||
prec = 6 // default prec of fmt.Printf("%f|%e|%E")
|
||||
fallthrough
|
||||
case 'b', 'g', 'G', 'x', 'X':
|
||||
if p, ok := st.Precision(); ok {
|
||||
prec = p
|
||||
} else {
|
||||
prec = 6
|
||||
}
|
||||
default:
|
||||
verb, prec = 'f', 0
|
||||
}
|
||||
|
||||
var unit SizeB1000
|
||||
|
|
@ -97,9 +108,13 @@ func (self SizeB1000) Format(st fmt.State, verb rune) {
|
|||
unit = _TB
|
||||
}
|
||||
|
||||
mustWriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
|
||||
b := strconv.AppendFloat(make([]byte, 0, 24), float64(self)/float64(unit), byte(verb), prec, 64)
|
||||
if st.Flag(' ') {
|
||||
mustWriteString(st, " ")
|
||||
b = append(b, ' ')
|
||||
}
|
||||
b = append(b, []byte(unit.String())...)
|
||||
_, err := st.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mustWriteString(st, unit.String())
|
||||
}
|
||||
|
|
@ -2,35 +2,44 @@ package decor
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/VividCortex/ewma"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Decorator = (*movingAverageSpeed)(nil)
|
||||
_ EwmaDecorator = (*movingAverageSpeed)(nil)
|
||||
_ Decorator = (*averageSpeed)(nil)
|
||||
_ AverageDecorator = (*averageSpeed)(nil)
|
||||
)
|
||||
|
||||
// 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}
|
||||
return speedFormatter{input}
|
||||
}
|
||||
|
||||
type speedFormatter struct {
|
||||
fmt.Formatter
|
||||
}
|
||||
|
||||
func (self *speedFormatter) Format(st fmt.State, verb rune) {
|
||||
func (self speedFormatter) Format(st fmt.State, verb rune) {
|
||||
self.Formatter.Format(st, verb)
|
||||
mustWriteString(st, "/s")
|
||||
_, err := io.WriteString(st, "/s")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// For this decorator to work correctly you have to measure each iteration's
|
||||
// duration and pass it to one of the (*Bar).EwmaIncr... family methods.
|
||||
func EwmaSpeed(unit interface{}, format string, age float64, wcc ...WC) Decorator {
|
||||
var average ewma.MovingAverage
|
||||
if age == 0 {
|
||||
average = ewma.NewMovingAverage()
|
||||
|
|
@ -43,7 +52,7 @@ func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
|
|||
// MovingAverageSpeed decorator relies on MovingAverage implementation
|
||||
// to calculate its average.
|
||||
//
|
||||
// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
|
||||
// `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
|
||||
//
|
||||
// `format` printf compatible verb for value, like "%f" or "%d"
|
||||
//
|
||||
|
|
@ -53,15 +62,11 @@ func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
|
|||
//
|
||||
// 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"
|
||||
}
|
||||
// unit=SizeB1024(0), format="%.1f" output: "1.0MiB/s"
|
||||
// unit=SizeB1024(0), format="% .1f" output: "1.0 MiB/s"
|
||||
// unit=SizeB1000(0), format="%.1f" output: "1.0MB/s"
|
||||
// unit=SizeB1000(0), format="% .1f" output: "1.0 MB/s"
|
||||
func MovingAverageSpeed(unit interface{}, format string, average ewma.MovingAverage, wcc ...WC) Decorator {
|
||||
d := &movingAverageSpeed{
|
||||
WC: initWC(wcc...),
|
||||
average: average,
|
||||
|
|
@ -98,14 +103,14 @@ func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) {
|
|||
|
||||
// AverageSpeed decorator with dynamic unit measure adjustment. It's
|
||||
// a wrapper of NewAverageSpeed.
|
||||
func AverageSpeed(unit int, format string, wcc ...WC) Decorator {
|
||||
func AverageSpeed(unit interface{}, 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
|
||||
// `unit` one of [0|SizeB1024(0)|SizeB1000(0)]
|
||||
//
|
||||
// `format` printf compatible verb for value, like "%f" or "%d"
|
||||
//
|
||||
|
|
@ -115,15 +120,11 @@ func AverageSpeed(unit int, format string, wcc ...WC) Decorator {
|
|||
//
|
||||
// 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"
|
||||
}
|
||||
// unit=SizeB1024(0), format="%.1f" output: "1.0MiB/s"
|
||||
// unit=SizeB1024(0), format="% .1f" output: "1.0 MiB/s"
|
||||
// unit=SizeB1000(0), format="%.1f" output: "1.0MB/s"
|
||||
// unit=SizeB1000(0), format="% .1f" output: "1.0 MB/s"
|
||||
func NewAverageSpeed(unit interface{}, format string, startTime time.Time, wcc ...WC) Decorator {
|
||||
d := &averageSpeed{
|
||||
WC: initWC(wcc...),
|
||||
startTime: startTime,
|
||||
|
|
@ -144,7 +145,6 @@ func (d *averageSpeed) Decor(s Statistics) string {
|
|||
speed := float64(s.Current) / float64(time.Since(d.startTime))
|
||||
d.msg = d.producer(speed * 1e9)
|
||||
}
|
||||
|
||||
return d.FormatMsg(d.msg)
|
||||
}
|
||||
|
||||
|
|
@ -152,17 +152,26 @@ func (d *averageSpeed) AverageAdjust(startTime time.Time) {
|
|||
d.startTime = startTime
|
||||
}
|
||||
|
||||
func chooseSpeedProducer(unit int, format string) func(float64) string {
|
||||
switch unit {
|
||||
case UnitKiB:
|
||||
func chooseSpeedProducer(unit interface{}, format string) func(float64) string {
|
||||
switch unit.(type) {
|
||||
case SizeB1024:
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
}
|
||||
return func(speed float64) string {
|
||||
return fmt.Sprintf(format, FmtAsSpeed(SizeB1024(math.Round(speed))))
|
||||
}
|
||||
case UnitKB:
|
||||
case SizeB1000:
|
||||
if format == "" {
|
||||
format = "% d"
|
||||
}
|
||||
return func(speed float64) string {
|
||||
return fmt.Sprintf(format, FmtAsSpeed(SizeB1000(math.Round(speed))))
|
||||
}
|
||||
default:
|
||||
if format == "" {
|
||||
format = "%f"
|
||||
}
|
||||
return func(speed float64) string {
|
||||
return fmt.Sprintf(format, speed)
|
||||
}
|
||||
174
vendor/github.com/vbauerster/mpb/v8/heap_manager.go
generated
vendored
Normal file
174
vendor/github.com/vbauerster/mpb/v8/heap_manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package mpb
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
)
|
||||
|
||||
type heapManager chan heapRequest
|
||||
|
||||
type heapCmd int
|
||||
|
||||
const (
|
||||
h_sync heapCmd = iota
|
||||
h_push
|
||||
h_iter
|
||||
h_drain
|
||||
h_fix
|
||||
h_state
|
||||
h_end
|
||||
)
|
||||
|
||||
type heapRequest struct {
|
||||
cmd heapCmd
|
||||
data interface{}
|
||||
}
|
||||
|
||||
type iterData struct {
|
||||
iter chan<- *Bar
|
||||
drop <-chan struct{}
|
||||
}
|
||||
|
||||
type pushData struct {
|
||||
bar *Bar
|
||||
sync bool
|
||||
}
|
||||
|
||||
type fixData struct {
|
||||
bar *Bar
|
||||
priority int
|
||||
}
|
||||
|
||||
func (m heapManager) run() {
|
||||
var bHeap priorityQueue
|
||||
var pMatrix, aMatrix map[int][]chan int
|
||||
|
||||
var l int
|
||||
var sync bool
|
||||
|
||||
for req := range m {
|
||||
next:
|
||||
switch req.cmd {
|
||||
case h_push:
|
||||
data := req.data.(pushData)
|
||||
heap.Push(&bHeap, data.bar)
|
||||
if !sync {
|
||||
sync = data.sync
|
||||
}
|
||||
case h_sync:
|
||||
if sync || l != bHeap.Len() {
|
||||
pMatrix = make(map[int][]chan int)
|
||||
aMatrix = make(map[int][]chan int)
|
||||
for _, b := range bHeap {
|
||||
table := b.wSyncTable()
|
||||
for i, ch := range table[0] {
|
||||
pMatrix[i] = append(pMatrix[i], ch)
|
||||
}
|
||||
for i, ch := range table[1] {
|
||||
aMatrix[i] = append(aMatrix[i], ch)
|
||||
}
|
||||
}
|
||||
sync = false
|
||||
l = bHeap.Len()
|
||||
}
|
||||
drop := req.data.(<-chan struct{})
|
||||
syncWidth(pMatrix, drop)
|
||||
syncWidth(aMatrix, drop)
|
||||
case h_iter:
|
||||
data := req.data.(iterData)
|
||||
for _, b := range bHeap {
|
||||
select {
|
||||
case data.iter <- b:
|
||||
case <-data.drop:
|
||||
close(data.iter)
|
||||
break next
|
||||
}
|
||||
}
|
||||
close(data.iter)
|
||||
case h_drain:
|
||||
data := req.data.(iterData)
|
||||
for bHeap.Len() != 0 {
|
||||
select {
|
||||
case data.iter <- heap.Pop(&bHeap).(*Bar):
|
||||
case <-data.drop:
|
||||
close(data.iter)
|
||||
break next
|
||||
}
|
||||
}
|
||||
close(data.iter)
|
||||
case h_fix:
|
||||
data := req.data.(fixData)
|
||||
bar, priority := data.bar, data.priority
|
||||
if bar.index < 0 {
|
||||
break
|
||||
}
|
||||
bar.priority = priority
|
||||
heap.Fix(&bHeap, bar.index)
|
||||
case h_state:
|
||||
ch := req.data.(chan<- bool)
|
||||
ch <- sync || l != bHeap.Len()
|
||||
case h_end:
|
||||
ch := req.data.(chan<- interface{})
|
||||
if ch != nil {
|
||||
go func() {
|
||||
ch <- []*Bar(bHeap)
|
||||
}()
|
||||
}
|
||||
close(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m heapManager) sync(drop <-chan struct{}) {
|
||||
m <- heapRequest{cmd: h_sync, data: drop}
|
||||
}
|
||||
|
||||
func (m heapManager) push(b *Bar, sync bool) {
|
||||
data := pushData{b, sync}
|
||||
m <- heapRequest{cmd: h_push, data: data}
|
||||
}
|
||||
|
||||
func (m heapManager) iter(iter chan<- *Bar, drop <-chan struct{}) {
|
||||
data := iterData{iter, drop}
|
||||
m <- heapRequest{cmd: h_iter, data: data}
|
||||
}
|
||||
|
||||
func (m heapManager) drain(iter chan<- *Bar, drop <-chan struct{}) {
|
||||
data := iterData{iter, drop}
|
||||
m <- heapRequest{cmd: h_drain, data: data}
|
||||
}
|
||||
|
||||
func (m heapManager) fix(b *Bar, priority int) {
|
||||
data := fixData{b, priority}
|
||||
m <- heapRequest{cmd: h_fix, data: data}
|
||||
}
|
||||
|
||||
func (m heapManager) state(ch chan<- bool) {
|
||||
m <- heapRequest{cmd: h_state, data: ch}
|
||||
}
|
||||
|
||||
func (m heapManager) end(ch chan<- interface{}) {
|
||||
m <- heapRequest{cmd: h_end, data: ch}
|
||||
}
|
||||
|
||||
func syncWidth(matrix map[int][]chan int, drop <-chan struct{}) {
|
||||
for _, column := range matrix {
|
||||
go maxWidthDistributor(column, drop)
|
||||
}
|
||||
}
|
||||
|
||||
func maxWidthDistributor(column []chan int, drop <-chan struct{}) {
|
||||
var maxWidth int
|
||||
for _, ch := range column {
|
||||
select {
|
||||
case w := <-ch:
|
||||
if w > maxWidth {
|
||||
maxWidth = w
|
||||
}
|
||||
case <-drop:
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, ch := range column {
|
||||
ch <- maxWidth
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
package mpb
|
||||
|
||||
// A priorityQueue implements heap.Interface
|
||||
import "container/heap"
|
||||
|
||||
var _ heap.Interface = (*priorityQueue)(nil)
|
||||
|
||||
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
|
||||
// greater priority pops first
|
||||
return pq[i].priority > pq[j].priority
|
||||
}
|
||||
|
||||
func (pq priorityQueue) Swap(i, j int) {
|
||||
451
vendor/github.com/vbauerster/mpb/v8/progress.go
generated
vendored
Normal file
451
vendor/github.com/vbauerster/mpb/v8/progress.go
generated
vendored
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
package mpb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/vbauerster/mpb/v8/cwriter"
|
||||
"github.com/vbauerster/mpb/v8/decor"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRefreshRate = 150 * time.Millisecond
|
||||
)
|
||||
|
||||
// DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
|
||||
var DoneError = fmt.Errorf("%T instance can't be reused after it's done", (*Progress)(nil))
|
||||
|
||||
// Progress represents a container that renders one or more progress bars.
|
||||
type Progress struct {
|
||||
uwg *sync.WaitGroup
|
||||
pwg, bwg sync.WaitGroup
|
||||
operateState chan func(*pState)
|
||||
interceptIO chan func(io.Writer)
|
||||
done <-chan struct{}
|
||||
cancel func()
|
||||
}
|
||||
|
||||
// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
|
||||
type pState struct {
|
||||
ctx context.Context
|
||||
hm heapManager
|
||||
dropS, dropD chan struct{}
|
||||
renderReq chan time.Time
|
||||
idCount int
|
||||
popPriority int
|
||||
|
||||
// following are provided/overrided by user
|
||||
refreshRate time.Duration
|
||||
reqWidth int
|
||||
popCompleted bool
|
||||
autoRefresh bool
|
||||
delayRC <-chan struct{}
|
||||
manualRC <-chan interface{}
|
||||
shutdownNotifier chan<- interface{}
|
||||
queueBars map[*Bar]*Bar
|
||||
output io.Writer
|
||||
debugOut io.Writer
|
||||
uwg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
s := &pState{
|
||||
ctx: ctx,
|
||||
hm: make(heapManager),
|
||||
dropS: make(chan struct{}),
|
||||
dropD: make(chan struct{}),
|
||||
renderReq: make(chan time.Time),
|
||||
refreshRate: defaultRefreshRate,
|
||||
popPriority: math.MinInt32,
|
||||
queueBars: make(map[*Bar]*Bar),
|
||||
output: os.Stdout,
|
||||
debugOut: io.Discard,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
if opt != nil {
|
||||
opt(s)
|
||||
}
|
||||
}
|
||||
|
||||
p := &Progress{
|
||||
uwg: s.uwg,
|
||||
operateState: make(chan func(*pState)),
|
||||
interceptIO: make(chan func(io.Writer)),
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
cw := cwriter.New(s.output)
|
||||
if s.manualRC != nil {
|
||||
done := make(chan struct{})
|
||||
p.done = done
|
||||
s.autoRefresh = false
|
||||
go s.manualRefreshListener(done)
|
||||
} else if cw.IsTerminal() || s.autoRefresh {
|
||||
done := make(chan struct{})
|
||||
p.done = done
|
||||
s.autoRefresh = true
|
||||
go s.autoRefreshListener(done)
|
||||
} else {
|
||||
p.done = ctx.Done()
|
||||
s.autoRefresh = false
|
||||
}
|
||||
|
||||
p.pwg.Add(1)
|
||||
go p.serve(s, cw)
|
||||
go s.hm.run()
|
||||
return p
|
||||
}
|
||||
|
||||
// AddBar creates a bar with default bar filler.
|
||||
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
|
||||
return p.New(total, BarStyle(), options...)
|
||||
}
|
||||
|
||||
// AddSpinner creates a bar with default spinner filler.
|
||||
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
|
||||
return p.New(total, SpinnerStyle(), options...)
|
||||
}
|
||||
|
||||
// New creates a bar by calling `Build` method on provided `BarFillerBuilder`.
|
||||
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
|
||||
return p.MustAdd(total, builder.Build(), options...)
|
||||
}
|
||||
|
||||
// MustAdd creates a bar which renders itself by provided BarFiller.
|
||||
// If `total <= 0` triggering complete event by increment methods is
|
||||
// disabled. Panics if *Progress instance is done, i.e. called after
|
||||
// (*Progress).Wait().
|
||||
func (p *Progress) MustAdd(total int64, filler BarFiller, options ...BarOption) *Bar {
|
||||
bar, err := p.Add(total, filler, options...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bar
|
||||
}
|
||||
|
||||
// Add creates a bar which renders itself by provided BarFiller.
|
||||
// If `total <= 0` triggering complete event by increment methods
|
||||
// is disabled. If *Progress instance is done, i.e. called after
|
||||
// (*Progress).Wait(), return error == DoneError.
|
||||
func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) (*Bar, error) {
|
||||
if filler == nil {
|
||||
filler = NopStyle().Build()
|
||||
}
|
||||
type result struct {
|
||||
bar *Bar
|
||||
bs *bState
|
||||
}
|
||||
ch := make(chan result)
|
||||
select {
|
||||
case p.operateState <- func(ps *pState) {
|
||||
bs := ps.makeBarState(total, filler, options...)
|
||||
bar := newBar(ps.ctx, p, bs)
|
||||
if bs.waitBar != nil {
|
||||
ps.queueBars[bs.waitBar] = bar
|
||||
} else {
|
||||
ps.hm.push(bar, true)
|
||||
}
|
||||
ps.idCount++
|
||||
ch <- result{bar, bs}
|
||||
}:
|
||||
res := <-ch
|
||||
bar, bs := res.bar, res.bs
|
||||
bar.TraverseDecorators(func(d decor.Decorator) {
|
||||
if d, ok := d.(decor.AverageDecorator); ok {
|
||||
bs.averageDecorators = append(bs.averageDecorators, d)
|
||||
}
|
||||
if d, ok := d.(decor.EwmaDecorator); ok {
|
||||
bs.ewmaDecorators = append(bs.ewmaDecorators, d)
|
||||
}
|
||||
if d, ok := d.(decor.ShutdownListener); ok {
|
||||
bs.shutdownListeners = append(bs.shutdownListeners, d)
|
||||
}
|
||||
})
|
||||
return bar, nil
|
||||
case <-p.done:
|
||||
return nil, DoneError
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Progress) traverseBars(cb func(b *Bar) bool) {
|
||||
iter, drop := make(chan *Bar), make(chan struct{})
|
||||
select {
|
||||
case p.operateState <- func(s *pState) { s.hm.iter(iter, drop) }:
|
||||
for b := range iter {
|
||||
if cb(b) {
|
||||
close(drop)
|
||||
break
|
||||
}
|
||||
}
|
||||
case <-p.done:
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateBarPriority same as *Bar.SetPriority(int).
|
||||
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
|
||||
select {
|
||||
case p.operateState <- func(s *pState) { s.hm.fix(b, priority) }:
|
||||
case <-p.done:
|
||||
}
|
||||
}
|
||||
|
||||
// Write is implementation of io.Writer.
|
||||
// Writing to `*mpb.Progress` will print lines above a running bar.
|
||||
// Writes aren't flushed immediately, but at next refresh cycle.
|
||||
// If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
|
||||
// is returned.
|
||||
func (p *Progress) Write(b []byte) (int, error) {
|
||||
type result struct {
|
||||
n int
|
||||
err error
|
||||
}
|
||||
ch := make(chan result)
|
||||
select {
|
||||
case p.interceptIO <- func(w io.Writer) {
|
||||
n, err := w.Write(b)
|
||||
ch <- result{n, err}
|
||||
}:
|
||||
res := <-ch
|
||||
return res.n, res.err
|
||||
case <-p.done:
|
||||
return 0, DoneError
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
// wait for user wg, if any
|
||||
if p.uwg != nil {
|
||||
p.uwg.Wait()
|
||||
}
|
||||
|
||||
p.bwg.Wait()
|
||||
p.Shutdown()
|
||||
}
|
||||
|
||||
// Shutdown cancels any running bar immediately and then shutdowns (*Progress)
|
||||
// instance. Normally this method shouldn't be called unless you know what you
|
||||
// are doing. Proper way to shutdown is to call (*Progress).Wait() instead.
|
||||
func (p *Progress) Shutdown() {
|
||||
p.cancel()
|
||||
p.pwg.Wait()
|
||||
}
|
||||
|
||||
func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
|
||||
defer p.pwg.Done()
|
||||
render := func() error { return s.render(cw) }
|
||||
var err error
|
||||
|
||||
for {
|
||||
select {
|
||||
case op := <-p.operateState:
|
||||
op(s)
|
||||
case fn := <-p.interceptIO:
|
||||
fn(cw)
|
||||
case <-s.renderReq:
|
||||
e := render()
|
||||
if e != nil {
|
||||
p.cancel() // cancel all bars
|
||||
render = func() error { return nil }
|
||||
err = e
|
||||
}
|
||||
case <-p.done:
|
||||
update := make(chan bool)
|
||||
for s.autoRefresh && err == nil {
|
||||
s.hm.state(update)
|
||||
if <-update {
|
||||
err = render()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintln(s.debugOut, err.Error())
|
||||
}
|
||||
s.hm.end(s.shutdownNotifier)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s pState) autoRefreshListener(done chan struct{}) {
|
||||
if s.delayRC != nil {
|
||||
<-s.delayRC
|
||||
}
|
||||
ticker := time.NewTicker(s.refreshRate)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case t := <-ticker.C:
|
||||
s.renderReq <- t
|
||||
case <-s.ctx.Done():
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s pState) manualRefreshListener(done chan struct{}) {
|
||||
for {
|
||||
select {
|
||||
case x := <-s.manualRC:
|
||||
if t, ok := x.(time.Time); ok {
|
||||
s.renderReq <- t
|
||||
} else {
|
||||
s.renderReq <- time.Now()
|
||||
}
|
||||
case <-s.ctx.Done():
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *pState) render(cw *cwriter.Writer) (err error) {
|
||||
s.hm.sync(s.dropS)
|
||||
iter := make(chan *Bar)
|
||||
go s.hm.iter(iter, s.dropS)
|
||||
|
||||
var width, height int
|
||||
if cw.IsTerminal() {
|
||||
width, height, err = cw.GetTermSize()
|
||||
if err != nil {
|
||||
close(s.dropS)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if s.reqWidth > 0 {
|
||||
width = s.reqWidth
|
||||
} else {
|
||||
width = 100
|
||||
}
|
||||
height = 100
|
||||
}
|
||||
|
||||
for b := range iter {
|
||||
go b.render(width)
|
||||
}
|
||||
|
||||
return s.flush(cw, height)
|
||||
}
|
||||
|
||||
func (s *pState) flush(cw *cwriter.Writer, height int) error {
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait() // waiting for all s.hm.push to complete
|
||||
|
||||
var popCount int
|
||||
var rows []io.Reader
|
||||
|
||||
iter := make(chan *Bar)
|
||||
s.hm.drain(iter, s.dropD)
|
||||
|
||||
for b := range iter {
|
||||
frame := <-b.frameCh
|
||||
if frame.err != nil {
|
||||
close(s.dropD)
|
||||
b.cancel()
|
||||
return frame.err // b.frameCh is buffered it's ok to return here
|
||||
}
|
||||
var usedRows int
|
||||
for i := len(frame.rows) - 1; i >= 0; i-- {
|
||||
if row := frame.rows[i]; len(rows) < height {
|
||||
rows = append(rows, row)
|
||||
usedRows++
|
||||
} else {
|
||||
_, _ = io.Copy(io.Discard, row)
|
||||
}
|
||||
}
|
||||
if frame.shutdown != 0 && !frame.done {
|
||||
if qb, ok := s.queueBars[b]; ok {
|
||||
b.cancel()
|
||||
delete(s.queueBars, b)
|
||||
qb.priority = b.priority
|
||||
wg.Add(1)
|
||||
go func(b *Bar) {
|
||||
s.hm.push(b, true)
|
||||
wg.Done()
|
||||
}(qb)
|
||||
continue
|
||||
}
|
||||
if s.popCompleted && !frame.noPop {
|
||||
switch frame.shutdown {
|
||||
case 1:
|
||||
b.priority = s.popPriority
|
||||
s.popPriority++
|
||||
default:
|
||||
b.cancel()
|
||||
popCount += usedRows
|
||||
continue
|
||||
}
|
||||
} else if frame.rmOnComplete {
|
||||
b.cancel()
|
||||
continue
|
||||
} else {
|
||||
b.cancel()
|
||||
}
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(b *Bar) {
|
||||
s.hm.push(b, false)
|
||||
wg.Done()
|
||||
}(b)
|
||||
}
|
||||
|
||||
for i := len(rows) - 1; i >= 0; i-- {
|
||||
_, err := cw.ReadFrom(rows[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return cw.Flush(len(rows) - popCount)
|
||||
}
|
||||
|
||||
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,
|
||||
renderReq: s.renderReq,
|
||||
autoRefresh: s.autoRefresh,
|
||||
}
|
||||
|
||||
if total > 0 {
|
||||
bs.triggerComplete = true
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
if opt != nil {
|
||||
opt(bs)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(bs.buffers); i++ {
|
||||
bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
|
||||
}
|
||||
|
||||
return bs
|
||||
}
|
||||
96
vendor/github.com/vbauerster/mpb/v8/proxyreader.go
generated
vendored
Normal file
96
vendor/github.com/vbauerster/mpb/v8/proxyreader.go
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package mpb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"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)
|
||||
return n, err
|
||||
}
|
||||
|
||||
type proxyWriterTo struct {
|
||||
proxyReader
|
||||
}
|
||||
|
||||
func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := x.ReadCloser.(io.WriterTo).WriteTo(w)
|
||||
x.bar.IncrInt64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
type ewmaProxyReader struct {
|
||||
io.ReadCloser
|
||||
bar *Bar
|
||||
}
|
||||
|
||||
func (x ewmaProxyReader) Read(p []byte) (int, error) {
|
||||
start := time.Now()
|
||||
n, err := x.ReadCloser.Read(p)
|
||||
x.bar.EwmaIncrBy(n, time.Since(start))
|
||||
return n, err
|
||||
}
|
||||
|
||||
type ewmaProxyWriterTo struct {
|
||||
ewmaProxyReader
|
||||
}
|
||||
|
||||
func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
|
||||
start := time.Now()
|
||||
n, err := x.ReadCloser.(io.WriterTo).WriteTo(w)
|
||||
x.bar.EwmaIncrInt64(n, time.Since(start))
|
||||
return n, err
|
||||
}
|
||||
|
||||
func newProxyReader(r io.Reader, b *Bar, hasEwma bool) io.ReadCloser {
|
||||
rc := toReadCloser(r)
|
||||
if hasEwma {
|
||||
epr := ewmaProxyReader{rc, b}
|
||||
if _, ok := r.(io.WriterTo); ok {
|
||||
return ewmaProxyWriterTo{epr}
|
||||
}
|
||||
return epr
|
||||
}
|
||||
pr := proxyReader{rc, b}
|
||||
if _, ok := r.(io.WriterTo); ok {
|
||||
return proxyWriterTo{pr}
|
||||
}
|
||||
return pr
|
||||
}
|
||||
|
||||
func toReadCloser(r io.Reader) io.ReadCloser {
|
||||
if rc, ok := r.(io.ReadCloser); ok {
|
||||
return rc
|
||||
}
|
||||
return toNopReadCloser(r)
|
||||
}
|
||||
|
||||
func toNopReadCloser(r io.Reader) io.ReadCloser {
|
||||
if _, ok := r.(io.WriterTo); ok {
|
||||
return nopReadCloserWriterTo{r}
|
||||
}
|
||||
return nopReadCloser{r}
|
||||
}
|
||||
|
||||
type nopReadCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (nopReadCloser) Close() error { return nil }
|
||||
|
||||
type nopReadCloserWriterTo struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (nopReadCloserWriterTo) Close() error { return nil }
|
||||
|
||||
func (c nopReadCloserWriterTo) WriteTo(w io.Writer) (int64, error) {
|
||||
return c.Reader.(io.WriterTo).WriteTo(w)
|
||||
}
|
||||
96
vendor/github.com/vbauerster/mpb/v8/proxywriter.go
generated
vendored
Normal file
96
vendor/github.com/vbauerster/mpb/v8/proxywriter.go
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package mpb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
type proxyWriter struct {
|
||||
io.WriteCloser
|
||||
bar *Bar
|
||||
}
|
||||
|
||||
func (x proxyWriter) Write(p []byte) (int, error) {
|
||||
n, err := x.WriteCloser.Write(p)
|
||||
x.bar.IncrBy(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
type proxyReaderFrom struct {
|
||||
proxyWriter
|
||||
}
|
||||
|
||||
func (x proxyReaderFrom) ReadFrom(r io.Reader) (int64, error) {
|
||||
n, err := x.WriteCloser.(io.ReaderFrom).ReadFrom(r)
|
||||
x.bar.IncrInt64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
type ewmaProxyWriter struct {
|
||||
io.WriteCloser
|
||||
bar *Bar
|
||||
}
|
||||
|
||||
func (x ewmaProxyWriter) Write(p []byte) (int, error) {
|
||||
start := time.Now()
|
||||
n, err := x.WriteCloser.Write(p)
|
||||
x.bar.EwmaIncrBy(n, time.Since(start))
|
||||
return n, err
|
||||
}
|
||||
|
||||
type ewmaProxyReaderFrom struct {
|
||||
ewmaProxyWriter
|
||||
}
|
||||
|
||||
func (x ewmaProxyReaderFrom) ReadFrom(r io.Reader) (int64, error) {
|
||||
start := time.Now()
|
||||
n, err := x.WriteCloser.(io.ReaderFrom).ReadFrom(r)
|
||||
x.bar.EwmaIncrInt64(n, time.Since(start))
|
||||
return n, err
|
||||
}
|
||||
|
||||
func newProxyWriter(w io.Writer, b *Bar, hasEwma bool) io.WriteCloser {
|
||||
wc := toWriteCloser(w)
|
||||
if hasEwma {
|
||||
epw := ewmaProxyWriter{wc, b}
|
||||
if _, ok := w.(io.ReaderFrom); ok {
|
||||
return ewmaProxyReaderFrom{epw}
|
||||
}
|
||||
return epw
|
||||
}
|
||||
pw := proxyWriter{wc, b}
|
||||
if _, ok := w.(io.ReaderFrom); ok {
|
||||
return proxyReaderFrom{pw}
|
||||
}
|
||||
return pw
|
||||
}
|
||||
|
||||
func toWriteCloser(w io.Writer) io.WriteCloser {
|
||||
if wc, ok := w.(io.WriteCloser); ok {
|
||||
return wc
|
||||
}
|
||||
return toNopWriteCloser(w)
|
||||
}
|
||||
|
||||
func toNopWriteCloser(w io.Writer) io.WriteCloser {
|
||||
if _, ok := w.(io.ReaderFrom); ok {
|
||||
return nopWriteCloserReaderFrom{w}
|
||||
}
|
||||
return nopWriteCloser{w}
|
||||
}
|
||||
|
||||
type nopWriteCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopWriteCloser) Close() error { return nil }
|
||||
|
||||
type nopWriteCloserReaderFrom struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopWriteCloserReaderFrom) Close() error { return nil }
|
||||
|
||||
func (c nopWriteCloserReaderFrom) ReadFrom(r io.Reader) (int64, error) {
|
||||
return c.Writer.(io.ReaderFrom).ReadFrom(r)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue