summaryrefslogtreecommitdiff
path: root/vendor/github.com/vbauerster/mpb/v4/bar.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/vbauerster/mpb/v4/bar.go')
-rw-r--r--vendor/github.com/vbauerster/mpb/v4/bar.go477
1 files changed, 477 insertions, 0 deletions
diff --git a/vendor/github.com/vbauerster/mpb/v4/bar.go b/vendor/github.com/vbauerster/mpb/v4/bar.go
new file mode 100644
index 000000000..c362da739
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/v4/bar.go
@@ -0,0 +1,477 @@
+package mpb
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "strings"
+ "time"
+ "unicode/utf8"
+
+ "github.com/vbauerster/mpb/v4/decor"
+)
+
+// Filler interface.
+// Bar renders by calling Filler's Fill method. You can literally have
+// any bar kind, by implementing this interface and passing it to the
+// *Progress.Add method.
+type Filler interface {
+ Fill(w io.Writer, width int, stat *decor.Statistics)
+}
+
+// FillerFunc is function type adapter to convert function into Filler.
+type FillerFunc func(w io.Writer, width int, stat *decor.Statistics)
+
+func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
+ f(w, width, stat)
+}
+
+// Wrapper interface.
+// If you're implementing custom Filler by wrapping a built-in one,
+// it is necessary to implement this interface to retain functionality
+// of built-in Filler.
+type Wrapper interface {
+ Base() Filler
+}
+
+// Bar represents a progress Bar.
+type Bar struct {
+ priority int // used by heap
+ index int // used by heap
+
+ extendedLines int
+ toShutdown bool
+ toDrop bool
+ noPop bool
+ operateState chan func(*bState)
+ frameCh chan io.Reader
+ syncTableCh chan [][]chan int
+ completed chan bool
+
+ // cancel is called either by user or on complete event
+ cancel func()
+ // done is closed after cacheState is assigned
+ done chan struct{}
+ // cacheState is populated, right after close(shutdown)
+ cacheState *bState
+
+ container *Progress
+ dlogger *log.Logger
+ recoveredPanic interface{}
+}
+
+type extFunc func(in io.Reader, tw int, st *decor.Statistics) (out io.Reader, lines int)
+
+type bState struct {
+ baseF Filler
+ filler Filler
+ id int
+ width int
+ total int64
+ current int64
+ trimSpace bool
+ toComplete bool
+ completeFlushed bool
+ noPop bool
+ aDecorators []decor.Decorator
+ pDecorators []decor.Decorator
+ amountReceivers []decor.AmountReceiver
+ shutdownListeners []decor.ShutdownListener
+ averageAdjusters []decor.AverageAdjuster
+ bufP, bufB, bufA *bytes.Buffer
+ extender extFunc
+
+ // priority overrides *Bar's priority, if set
+ priority int
+ // dropOnComplete propagates to *Bar
+ dropOnComplete bool
+ // runningBar is a key for *pState.parkedBars
+ runningBar *Bar
+
+ debugOut io.Writer
+}
+
+func newBar(container *Progress, bs *bState) *Bar {
+ logPrefix := fmt.Sprintf("%sbar#%02d ", container.dlogger.Prefix(), bs.id)
+ ctx, cancel := context.WithCancel(container.ctx)
+
+ bar := &Bar{
+ container: container,
+ priority: bs.priority,
+ toDrop: bs.dropOnComplete,
+ noPop: bs.noPop,
+ operateState: make(chan func(*bState)),
+ frameCh: make(chan io.Reader, 1),
+ syncTableCh: make(chan [][]chan int),
+ completed: make(chan bool, 1),
+ done: make(chan struct{}),
+ cancel: cancel,
+ dlogger: log.New(bs.debugOut, logPrefix, log.Lshortfile),
+ }
+
+ go bar.serve(ctx, bs)
+ return bar
+}
+
+// RemoveAllPrependers removes all prepend functions.
+func (b *Bar) RemoveAllPrependers() {
+ select {
+ case b.operateState <- func(s *bState) { s.pDecorators = nil }:
+ case <-b.done:
+ }
+}
+
+// RemoveAllAppenders removes all append functions.
+func (b *Bar) RemoveAllAppenders() {
+ select {
+ case b.operateState <- func(s *bState) { s.aDecorators = nil }:
+ case <-b.done:
+ }
+}
+
+// ProxyReader wraps r with metrics required for progress tracking.
+func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
+ if r == nil {
+ return nil
+ }
+ rc, ok := r.(io.ReadCloser)
+ if !ok {
+ rc = ioutil.NopCloser(r)
+ }
+ prox := &proxyReader{rc, b, time.Now()}
+ if wt, ok := r.(io.WriterTo); ok {
+ return &proxyWriterTo{prox, wt}
+ }
+ return prox
+}
+
+// ID returs id of the bar.
+func (b *Bar) ID() int {
+ result := make(chan int)
+ select {
+ case b.operateState <- func(s *bState) { result <- s.id }:
+ return <-result
+ case <-b.done:
+ return b.cacheState.id
+ }
+}
+
+// Current returns bar's current number, in other words sum of all increments.
+func (b *Bar) Current() int64 {
+ result := make(chan int64)
+ select {
+ case b.operateState <- func(s *bState) { result <- s.current }:
+ return <-result
+ case <-b.done:
+ return b.cacheState.current
+ }
+}
+
+// SetRefill sets refill, if supported by underlying Filler.
+// Useful for resume-able tasks.
+func (b *Bar) SetRefill(amount int64) {
+ type refiller interface {
+ SetRefill(int64)
+ }
+ b.operateState <- func(s *bState) {
+ if f, ok := s.baseF.(refiller); ok {
+ f.SetRefill(amount)
+ }
+ }
+}
+
+// AdjustAverageDecorators updates start time of all average decorators.
+// Useful for resume-able tasks.
+func (b *Bar) AdjustAverageDecorators(startTime time.Time) {
+ b.operateState <- func(s *bState) {
+ for _, adjuster := range s.averageAdjusters {
+ adjuster.AverageAdjust(startTime)
+ }
+ }
+}
+
+// TraverseDecorators traverses all available decorators and calls cb func on each.
+func (b *Bar) TraverseDecorators(cb decor.CBFunc) {
+ b.operateState <- func(s *bState) {
+ for _, decorators := range [...][]decor.Decorator{
+ s.pDecorators,
+ s.aDecorators,
+ } {
+ for _, d := range decorators {
+ cb(extractBaseDecorator(d))
+ }
+ }
+ }
+}
+
+// SetTotal sets total dynamically.
+// Set complete to true, to trigger bar complete event now.
+func (b *Bar) SetTotal(total int64, complete bool) {
+ select {
+ case b.operateState <- func(s *bState) {
+ if total <= 0 {
+ s.total = s.current
+ } else {
+ s.total = total
+ }
+ if complete && !s.toComplete {
+ s.current = s.total
+ s.toComplete = true
+ go b.refreshTillShutdown()
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// SetCurrent sets progress' current to arbitrary amount.
+func (b *Bar) SetCurrent(current int64, wdd ...time.Duration) {
+ select {
+ case b.operateState <- func(s *bState) {
+ for _, ar := range s.amountReceivers {
+ ar.NextAmount(current-s.current, wdd...)
+ }
+ s.current = current
+ if s.total > 0 && s.current >= s.total {
+ s.current = s.total
+ s.toComplete = true
+ go b.refreshTillShutdown()
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// Increment is a shorthand for b.IncrInt64(1, wdd...).
+func (b *Bar) Increment(wdd ...time.Duration) {
+ b.IncrInt64(1, wdd...)
+}
+
+// IncrBy is a shorthand for b.IncrInt64(int64(n), wdd...).
+func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
+ b.IncrInt64(int64(n), wdd...)
+}
+
+// IncrInt64 increments progress bar by amount of n. wdd is an optional
+// work duration i.e. time.Since(start), which expected to be passed,
+// if any ewma based decorator is used.
+func (b *Bar) IncrInt64(n int64, wdd ...time.Duration) {
+ select {
+ case b.operateState <- func(s *bState) {
+ for _, ar := range s.amountReceivers {
+ ar.NextAmount(n, wdd...)
+ }
+ s.current += n
+ if s.total > 0 && s.current >= s.total {
+ s.current = s.total
+ s.toComplete = true
+ go b.refreshTillShutdown()
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// SetPriority changes bar's order among multiple bars. Zero is highest
+// priority, i.e. bar will be on top. If you don't need to set priority
+// dynamically, better use BarPriority option.
+func (b *Bar) SetPriority(priority int) {
+ select {
+ case <-b.done:
+ default:
+ b.container.setBarPriority(b, priority)
+ }
+}
+
+// Abort interrupts bar's running goroutine. Call this, if you'd like
+// to stop/remove bar before completion event. It has no effect after
+// completion event. If drop is true bar will be removed as well.
+func (b *Bar) Abort(drop bool) {
+ select {
+ case <-b.done:
+ default:
+ if drop {
+ b.container.dropBar(b)
+ }
+ b.cancel()
+ }
+}
+
+// Completed reports whether the bar is in completed state.
+func (b *Bar) Completed() bool {
+ select {
+ case b.operateState <- func(s *bState) { b.completed <- s.toComplete }:
+ return <-b.completed
+ case <-b.done:
+ return true
+ }
+}
+
+func (b *Bar) serve(ctx context.Context, s *bState) {
+ defer b.container.bwg.Done()
+ for {
+ select {
+ case op := <-b.operateState:
+ op(s)
+ case <-ctx.Done():
+ b.cacheState = s
+ close(b.done)
+ // Notifying decorators about shutdown event
+ for _, sl := range s.shutdownListeners {
+ sl.Shutdown()
+ }
+ return
+ }
+ }
+}
+
+func (b *Bar) render(tw int) {
+ if b.recoveredPanic != nil {
+ b.toShutdown = false
+ b.frameCh <- b.panicToFrame(tw)
+ return
+ }
+ select {
+ case b.operateState <- func(s *bState) {
+ defer func() {
+ // recovering if user defined decorator panics for example
+ if p := recover(); p != nil {
+ b.dlogger.Println(p)
+ b.recoveredPanic = p
+ b.toShutdown = !s.completeFlushed
+ b.frameCh <- b.panicToFrame(tw)
+ }
+ }()
+
+ st := newStatistics(s)
+ frame := s.draw(tw, st)
+ frame, b.extendedLines = s.extender(frame, tw, st)
+
+ b.toShutdown = s.toComplete && !s.completeFlushed
+ s.completeFlushed = s.toComplete
+ b.frameCh <- frame
+ }:
+ case <-b.done:
+ s := b.cacheState
+ st := newStatistics(s)
+ frame := s.draw(tw, st)
+ frame, b.extendedLines = s.extender(frame, tw, st)
+ b.frameCh <- frame
+ }
+}
+
+func (b *Bar) panicToFrame(termWidth int) io.Reader {
+ return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%dv\n", termWidth), b.recoveredPanic))
+}
+
+func (b *Bar) subscribeDecorators() {
+ var amountReceivers []decor.AmountReceiver
+ var shutdownListeners []decor.ShutdownListener
+ var averageAdjusters []decor.AverageAdjuster
+ b.TraverseDecorators(func(d decor.Decorator) {
+ if d, ok := d.(decor.AmountReceiver); ok {
+ amountReceivers = append(amountReceivers, d)
+ }
+ if d, ok := d.(decor.ShutdownListener); ok {
+ shutdownListeners = append(shutdownListeners, d)
+ }
+ if d, ok := d.(decor.AverageAdjuster); ok {
+ averageAdjusters = append(averageAdjusters, d)
+ }
+ })
+ b.operateState <- func(s *bState) {
+ s.amountReceivers = amountReceivers
+ s.shutdownListeners = shutdownListeners
+ s.averageAdjusters = averageAdjusters
+ }
+}
+
+func (b *Bar) refreshTillShutdown() {
+ for {
+ select {
+ case b.container.refreshCh <- time.Now():
+ case <-b.done:
+ return
+ }
+ }
+}
+
+func (b *Bar) wSyncTable() [][]chan int {
+ select {
+ case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
+ return <-b.syncTableCh
+ case <-b.done:
+ return b.cacheState.wSyncTable()
+ }
+}
+
+func (s *bState) draw(termWidth int, stat *decor.Statistics) io.Reader {
+ for _, d := range s.pDecorators {
+ s.bufP.WriteString(d.Decor(stat))
+ }
+
+ for _, d := range s.aDecorators {
+ s.bufA.WriteString(d.Decor(stat))
+ }
+
+ s.bufA.WriteByte('\n')
+
+ prependCount := utf8.RuneCount(s.bufP.Bytes())
+ appendCount := utf8.RuneCount(s.bufA.Bytes()) - 1
+
+ if fitWidth := s.width; termWidth > 1 {
+ if !s.trimSpace {
+ // reserve space for edge spaces
+ termWidth -= 2
+ s.bufB.WriteByte(' ')
+ defer s.bufB.WriteByte(' ')
+ }
+ if prependCount+s.width+appendCount > termWidth {
+ fitWidth = termWidth - prependCount - appendCount
+ }
+ s.filler.Fill(s.bufB, fitWidth, stat)
+ }
+
+ return io.MultiReader(s.bufP, s.bufB, s.bufA)
+}
+
+func (s *bState) wSyncTable() [][]chan int {
+ columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
+ var pCount int
+ for _, d := range s.pDecorators {
+ if ch, ok := d.Sync(); ok {
+ columns = append(columns, ch)
+ pCount++
+ }
+ }
+ var aCount int
+ for _, d := range s.aDecorators {
+ if ch, ok := d.Sync(); ok {
+ columns = append(columns, ch)
+ aCount++
+ }
+ }
+ table := make([][]chan int, 2)
+ table[0] = columns[0:pCount]
+ table[1] = columns[pCount : pCount+aCount : pCount+aCount]
+ return table
+}
+
+func newStatistics(s *bState) *decor.Statistics {
+ return &decor.Statistics{
+ ID: s.id,
+ Completed: s.completeFlushed,
+ Total: s.total,
+ Current: s.current,
+ }
+}
+
+func extractBaseDecorator(d decor.Decorator) decor.Decorator {
+ if d, ok := d.(decor.Wrapper); ok {
+ return extractBaseDecorator(d.Base())
+ }
+ return d
+}