diff --git a/bar.go b/bar.go index 1828e67..6a4d0bc 100644 --- a/bar.go +++ b/bar.go @@ -11,30 +11,22 @@ "time" "unicode/utf8" - "github.com/vbauerster/mpb/v4/decor" + "github.com/vbauerster/mpb/v5/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 { +// BarFiller interface. +// Bar renders itself by calling BarFiller's Fill method. You can +// literally have any bar kind, by implementing this interface and +// passing it to the *Progress.Add method. +type BarFiller 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) { +// BarFillerFunc is function type adapter to convert function into Filler. +type BarFillerFunc func(w io.Writer, width int, stat *decor.Statistics) + +func (f BarFillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) { f(w, width, stat) -} - -// WrapFiller 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 WrapFiller interface { - Base() Filler } // Bar represents a progress Bar. @@ -66,21 +58,23 @@ type extFunc func(in io.Reader, tw int, st *decor.Statistics) (out io.Reader, lines int) type bState struct { - baseF Filler - filler Filler + baseF BarFiller + filler BarFiller id int width int total int64 current int64 + lastN int64 + iterated bool trimSpace bool toComplete bool completeFlushed bool noPop bool aDecorators []decor.Decorator pDecorators []decor.Decorator - amountReceivers []decor.AmountReceiver + averageDecorators []decor.AverageDecorator + ewmaDecorators []decor.EwmaDecorator shutdownListeners []decor.ShutdownListener - averageAdjusters []decor.AverageAdjuster bufP, bufB, bufA *bytes.Buffer extender extFunc @@ -183,18 +177,8 @@ } } -// 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) { +func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) { b.operateState <- func(s *bState) { for _, decorators := range [...][]decor.Decorator{ s.pDecorators, @@ -208,7 +192,8 @@ } // SetTotal sets total dynamically. -// Set complete to true, to trigger bar complete event now. +// If total is less or equal to zero it takes progress' current value. +// If complete is true, complete event will be triggered. func (b *Bar) SetTotal(total int64, complete bool) { select { case b.operateState <- func(s *bState) { @@ -227,13 +212,12 @@ } } -// 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...) - } +// SetCurrent sets progress' current to an arbitrary value. +func (b *Bar) SetCurrent(current int64) { + select { + case b.operateState <- func(s *bState) { + s.iterated = true + s.lastN = current - s.current s.current = current if s.total > 0 && s.current >= s.total { s.current = s.total @@ -245,30 +229,55 @@ } } -// 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...) - } +// Increment is a shorthand for b.IncrInt64(1). +func (b *Bar) Increment() { + b.IncrInt64(1) +} + +// IncrBy is a shorthand for b.IncrInt64(int64(n)). +func (b *Bar) IncrBy(n int) { + b.IncrInt64(int64(n)) +} + +// IncrInt64 increments progress by amount of n. +func (b *Bar) IncrInt64(n int64) { + select { + case b.operateState <- func(s *bState) { + s.iterated = true + s.lastN = n s.current += n if s.total > 0 && s.current >= s.total { s.current = s.total s.toComplete = true go b.refreshTillShutdown() + } + }: + case <-b.done: + } +} + +// DecoratorEwmaUpdate updates all EWMA based decorators. Should be +// called on each iteration, because EWMA's unit of measure is an +// iteration's taken time. Panics if called before *Bar.Incr... family +// methods. +func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) { + select { + case b.operateState <- func(s *bState) { + ewmaIterationUpdate(false, s, dur) + }: + case <-b.done: + ewmaIterationUpdate(true, b.cacheState, dur) + } +} + +// DecoratorAverageAdjust adjusts all average based decorators. Call +// if you need to adjust start time of all average based decorators +// or after progress resume. +func (b *Bar) DecoratorAverageAdjust(start time.Time) { + select { + case b.operateState <- func(s *bState) { + for _, d := range s.averageDecorators { + d.AverageAdjust(start) } }: case <-b.done: @@ -368,24 +377,24 @@ } func (b *Bar) subscribeDecorators() { - var amountReceivers []decor.AmountReceiver + var averageDecorators []decor.AverageDecorator + var ewmaDecorators []decor.EwmaDecorator 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.AverageDecorator); ok { + averageDecorators = append(averageDecorators, d) + } + if d, ok := d.(decor.EwmaDecorator); ok { + ewmaDecorators = append(ewmaDecorators, d) } if d, ok := d.(decor.ShutdownListener); ok { shutdownListeners = append(shutdownListeners, d) } - if d, ok := d.(decor.AverageAdjuster); ok { - averageAdjusters = append(averageAdjusters, d) - } }) b.operateState <- func(s *bState) { - s.amountReceivers = amountReceivers + s.averageDecorators = averageDecorators + s.ewmaDecorators = ewmaDecorators s.shutdownListeners = shutdownListeners - s.averageAdjusters = averageAdjusters } } @@ -475,3 +484,14 @@ } return d } + +func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) { + if !done && !s.iterated { + panic("increment required before ewma iteration update") + } else { + s.iterated = false + } + for _, d := range s.ewmaDecorators { + d.EwmaUpdate(s.lastN, dur) + } +} diff --git a/bar_filler.go b/bar_filler.go index 0d751a6..a57f49b 100644 --- a/bar_filler.go +++ b/bar_filler.go @@ -4,8 +4,8 @@ "io" "unicode/utf8" - "github.com/vbauerster/mpb/v4/decor" - "github.com/vbauerster/mpb/v4/internal" + "github.com/vbauerster/mpb/v5/decor" + "github.com/vbauerster/mpb/v5/internal" ) const ( @@ -45,7 +45,7 @@ } // NewBarFiller constucts mpb.Filler, to be used with *Progress.Add method. -func NewBarFiller(style string, reverse bool) Filler { +func NewBarFiller(style string, reverse bool) BarFiller { if style == "" { style = DefaultBarStyle } diff --git a/decor/decorator.go b/decor/decorator.go index 01b6780..daa86fc 100644 --- a/decor/decorator.go +++ b/decor/decorator.go @@ -84,10 +84,17 @@ Base() Decorator } -// AmountReceiver interface. -// EWMA based decorators need to implement this one. -type AmountReceiver interface { - NextAmount(int64, ...time.Duration) +// EwmaDecorator interface. +// EWMA based decorators must to implement this one. +type EwmaDecorator interface { + IterationUpdate(int64, time.Duration) +} + +// AverageDecorator interface. +// Average decorators should implement this interface to provide start +// time adjustment facility, for resume-able tasks. +type AverageDecorator interface { + Adjust(time.Time) } // ShutdownListener interface. @@ -97,17 +104,8 @@ Shutdown() } -// AverageAdjuster interface. -// Average decorators should implement this interface to provide start -// time adjustment facility, for resume-able tasks. -type AverageAdjuster interface { - AverageAdjust(time.Time) -} - -// CBFunc convenience call back func type. -type CBFunc func(Decorator) - // Global convenience instances of WC with sync width bit set. +// To be used with multiple bars only, i.e. not effective for single bar usage. var ( WCSyncWidth = WC{C: DSyncWidth} WCSyncWidthR = WC{C: DSyncWidthR} diff --git a/progress.go b/progress.go index c9b72b0..2ddda91 100644 --- a/progress.go +++ b/progress.go @@ -12,8 +12,8 @@ "sync" "time" - "github.com/vbauerster/mpb/v4/cwriter" - "github.com/vbauerster/mpb/v4/decor" + "github.com/vbauerster/mpb/v5/cwriter" + "github.com/vbauerster/mpb/v5/decor" ) const ( @@ -111,7 +111,7 @@ // Add creates a bar which renders itself by provided filler. // Set total to 0, if you plan to update it later. // Panics if *Progress instance is done, i.e. called after *Progress.Wait(). -func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar { +func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar { if filler == nil { filler = NewBarFiller(DefaultBarStyle, false) } @@ -340,7 +340,7 @@ } } -func (s *pState) makeBarState(total int64, filler Filler, options ...BarOption) *bState { +func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState { bs := &bState{ total: total, baseF: extractBaseFiller(filler), @@ -388,8 +388,11 @@ } } -func extractBaseFiller(f Filler) Filler { - if f, ok := f.(WrapFiller); ok { +func extractBaseFiller(f BarFiller) BarFiller { + type wrapper interface { + Base() BarFiller + } + if f, ok := f.(wrapper); ok { return extractBaseFiller(f.Base()) } return f diff --git a/spinner_filler.go b/spinner_filler.go index 9f383fb..cc30151 100644 --- a/spinner_filler.go +++ b/spinner_filler.go @@ -5,7 +5,7 @@ "strings" "unicode/utf8" - "github.com/vbauerster/mpb/v4/decor" + "github.com/vbauerster/mpb/v5/decor" ) // SpinnerAlignment enum. @@ -28,7 +28,7 @@ } // NewSpinnerFiller constucts mpb.Filler, to be used with *Progress.Add method. -func NewSpinnerFiller(style []string, alignment SpinnerAlignment) Filler { +func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller { if len(style) == 0 { style = DefaultSpinnerStyle }