deps: update osbuild/images to 246b718310ea

Current main.
246b718310
This commit is contained in:
Achilleas Koutsou 2023-07-19 17:22:28 +02:00 committed by Ondřej Budai
parent 326f0cfa2f
commit 5c292c61c6
1437 changed files with 208886 additions and 87131 deletions

View file

@ -1,84 +0,0 @@
package cwriter
import (
"bytes"
"errors"
"io"
"os"
"strconv"
)
// ErrNotTTY not a TeleTYpewriter error.
var ErrNotTTY = errors.New("not a terminal")
// 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
}

View file

@ -1,26 +0,0 @@
// +build !windows
package cwriter
import (
"golang.org/x/sys/unix"
)
func (w *Writer) clearLines() error {
return w.ansiCuuAndEd()
}
// GetSize returns the dimensions of the given terminal.
func GetSize(fd int) (width, height int, err error) {
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
if err != nil {
return -1, -1, err
}
return int(ws.Col), int(ws.Row), nil
}
// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
return err == nil
}

View file

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

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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)
}

View file

@ -1,8 +1,8 @@
# Multi Progress Bar
[![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
[![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v8)
[![Test status](https://github.com/vbauerster/mpb/actions/workflows/test.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
[![Donate with PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/vbauerster)
[![Lint status](https://github.com/vbauerster/mpb/actions/workflows/golangci-lint.yml/badge.svg)](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))
}
}()
}

View file

@ -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(' ')
}

View file

@ -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()
}

View file

@ -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
}

View file

@ -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
})
})
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -1,4 +1,4 @@
// +build darwin dragonfly freebsd netbsd openbsd
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
package cwriter

View file

@ -1,4 +1,4 @@
// +build aix linux
//go:build aix || linux
package cwriter

View file

@ -1,4 +1,4 @@
// +build solaris
//go:build solaris
package cwriter

View file

@ -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
View 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
}

View 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
}

View file

@ -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

View file

@ -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}
}

View file

@ -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...)
}

View file

@ -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))
}

View file

@ -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:
//

View file

@ -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)

View file

@ -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()
}
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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...)
}

View file

@ -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
}

View file

@ -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
}

View 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
}
}

View file

@ -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"

View file

@ -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())
}

View file

@ -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
View 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
}
}

View file

@ -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
View 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
View 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
View 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)
}