diff --git a/_examples/barExtender/main.go b/_examples/barExtender/main.go new file mode 100644 index 0000000..9e44cab --- /dev/null +++ b/_examples/barExtender/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + "io" + "math/rand" + "sync" + "time" + + "github.com/vbauerster/mpb/v4" + "github.com/vbauerster/mpb/v4/decor" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func main() { + var wg sync.WaitGroup + p := mpb.New(mpb.WithWaitGroup(&wg)) + total, numBars := 100, 3 + wg.Add(numBars) + + for i := 0; i < numBars; i++ { + name := fmt.Sprintf("Bar#%d:", i) + efn := func(w io.Writer, width int, s *decor.Statistics) { + if s.Completed { + fmt.Fprintf(w, "Bar id: %d has been completed\n", s.ID) + } + } + bar := p.AddBar(int64(total), mpb.BarExtender(mpb.FillerFunc(efn)), + mpb.PrependDecorators( + // simple name decorator + decor.Name(name), + // decor.DSyncWidth bit enables column width synchronization + decor.Percentage(decor.WCSyncSpace), + ), + 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), "done", + ), + ), + ) + // simulating some work + go func() { + defer wg.Done() + max := 100 * time.Millisecond + for i := 0; i < total; i++ { + start := time.Now() + time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) + // ewma based decorators require work duration measurement + bar.IncrBy(1, time.Since(start)) + } + }() + } + // wait for all bars to complete and flush + p.Wait() +} diff --git a/_examples/barNewLineExtend/main.go b/_examples/barNewLineExtend/main.go deleted file mode 100644 index 7b4b1c8..0000000 --- a/_examples/barNewLineExtend/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "fmt" - "io" - "math/rand" - "sync" - "time" - - "github.com/vbauerster/mpb/v4" - "github.com/vbauerster/mpb/v4/decor" -) - -func init() { - rand.Seed(time.Now().UnixNano()) -} - -func main() { - var wg sync.WaitGroup - p := mpb.New(mpb.WithWaitGroup(&wg)) - total, numBars := 100, 3 - wg.Add(numBars) - - for i := 0; i < numBars; i++ { - name := fmt.Sprintf("Bar#%d:", i) - efn := func(w io.Writer, s *decor.Statistics) { - if s.Completed { - fmt.Fprintf(w, "Bar id: %d has been completed\n", s.ID) - } - } - bar := p.AddBar(int64(total), mpb.BarNewLineExtend(efn), - mpb.PrependDecorators( - // simple name decorator - decor.Name(name), - // decor.DSyncWidth bit enables column width synchronization - decor.Percentage(decor.WCSyncSpace), - ), - 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), "done", - ), - ), - ) - // simulating some work - go func() { - defer wg.Done() - max := 100 * time.Millisecond - for i := 0; i < total; i++ { - start := time.Now() - time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10) - // ewma based decorators require work duration measurement - bar.IncrBy(1, time.Since(start)) - } - }() - } - // wait for all bars to complete and flush - p.Wait() -} diff --git a/bar.go b/bar.go index 5671873..c6cecc0 100644 --- a/bar.go +++ b/bar.go @@ -14,7 +14,22 @@ "github.com/vbauerster/mpb/v4/decor" ) -// Bar represents a progress Bar +// Filler interface. +// Bar renders by calling Filler's Fill method. You can literally have +// any bar kind, by implementing this interface and passing it to the +// Add method. +type Filler interface { + Fill(w io.Writer, termWidth int, stat *decor.Statistics) +} + +// FillerFunc is function type adapter to convert function into Filler. +type FillerFunc func(w io.Writer, termWidth int, stat *decor.Statistics) + +func (f FillerFunc) Fill(w io.Writer, termWidth int, stat *decor.Statistics) { + f(w, termWidth, stat) +} + +// Bar represents a progress Bar. type Bar struct { priority int index int @@ -33,35 +48,26 @@ shutdown chan struct{} } -// Filler interface. -// Bar renders by calling Filler's Fill method. You can literally have -// any bar kind, by implementing this interface and passing it to the -// Add method. -type Filler interface { - Fill(w io.Writer, width int, s *decor.Statistics) -} - type ( bState struct { - filler Filler - id int - width int - alignment int - total int64 - current int64 - trimSpace bool - toComplete bool - removeOnComplete bool - barClearOnComplete bool - completeFlushed bool - aDecorators []decor.Decorator - pDecorators []decor.Decorator - amountReceivers []decor.AmountReceiver - shutdownListeners []decor.ShutdownListener - bufP, bufB, bufA *bytes.Buffer - bufNL *bytes.Buffer - panicMsg string - newLineExtendFn func(io.Writer, *decor.Statistics) + filler Filler + extender Filler + id int + width int + alignment int + total int64 + current int64 + trimSpace bool + toComplete bool + removeOnComplete bool + barClearOnComplete bool + completeFlushed bool + aDecorators []decor.Decorator + pDecorators []decor.Decorator + amountReceivers []decor.AmountReceiver + shutdownListeners []decor.ShutdownListener + bufP, bufB, bufA, bufE *bytes.Buffer + panicMsg string // following options are assigned to the *Bar priority int @@ -104,8 +110,8 @@ s.bufP = bytes.NewBuffer(make([]byte, 0, width)) s.bufB = bytes.NewBuffer(make([]byte, 0, width)) s.bufA = bytes.NewBuffer(make([]byte, 0, width)) - if s.newLineExtendFn != nil { - s.bufNL = bytes.NewBuffer(make([]byte, 0, width)) + if s.extender != nil { + s.bufE = bytes.NewBuffer(make([]byte, 0, width)) } b := &Bar{ @@ -284,11 +290,10 @@ }() r := s.draw(tw) var extendedLines int - if s.newLineExtendFn != nil { - s.bufNL.Reset() - s.newLineExtendFn(s.bufNL, newStatistics(s)) - extendedLines = countLines(s.bufNL.Bytes()) - r = io.MultiReader(r, s.bufNL) + if s.extender != nil { + s.extender.Fill(s.bufE, tw, newStatistics(s)) + extendedLines = countLines(s.bufE.Bytes()) + r = io.MultiReader(r, s.bufE) } b.frameReaderCh <- &frameReader{ Reader: r, @@ -302,11 +307,10 @@ s := b.cacheState r := s.draw(tw) var extendedLines int - if s.newLineExtendFn != nil { - s.bufNL.Reset() - s.newLineExtendFn(s.bufNL, newStatistics(s)) - extendedLines = countLines(s.bufNL.Bytes()) - r = io.MultiReader(r, s.bufNL) + if s.extender != nil { + s.extender.Fill(s.bufE, tw, newStatistics(s)) + extendedLines = countLines(s.bufE.Bytes()) + r = io.MultiReader(r, s.bufE) } b.frameReaderCh <- &frameReader{ Reader: r, diff --git a/bar_option.go b/bar_option.go index 68cfef9..5f17c81 100644 --- a/bar_option.go +++ b/bar_option.go @@ -1,7 +1,6 @@ package mpb import ( - "io" "unicode/utf8" "github.com/vbauerster/mpb/v4/decor" @@ -94,9 +93,9 @@ // BarNewLineExtend takes user defined efn, which gets called each // render cycle. Any write to provided writer of efn, will appear on // new line of respective bar. -func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption { +func BarExtender(extender Filler) BarOption { return func(s *bState) { - s.newLineExtendFn = efn + s.extender = extender } }