summaryrefslogtreecommitdiff
path: root/vendor/github.com/vbauerster/mpb/bar.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/vbauerster/mpb/bar.go')
-rw-r--r--vendor/github.com/vbauerster/mpb/bar.go455
1 files changed, 455 insertions, 0 deletions
diff --git a/vendor/github.com/vbauerster/mpb/bar.go b/vendor/github.com/vbauerster/mpb/bar.go
new file mode 100644
index 000000000..5a506fc84
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/bar.go
@@ -0,0 +1,455 @@
+package mpb
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "strings"
+ "sync"
+ "time"
+ "unicode/utf8"
+
+ "github.com/vbauerster/mpb/decor"
+ "github.com/vbauerster/mpb/internal"
+)
+
+const (
+ rLeft = iota
+ rFill
+ rTip
+ rEmpty
+ rRight
+)
+
+const formatLen = 5
+
+type barRunes [formatLen]rune
+
+// Bar represents a progress Bar
+type Bar struct {
+ priority int
+ index int
+
+ runningBar *Bar
+ cacheState *bState
+ operateState chan func(*bState)
+ int64Ch chan int64
+ boolCh chan bool
+ frameReaderCh chan *frameReader
+ syncTableCh chan [][]chan int
+
+ // done is closed by Bar's goroutine, after cacheState is written
+ done chan struct{}
+ // shutdown is closed from master Progress goroutine only
+ shutdown chan struct{}
+}
+
+type (
+ bState struct {
+ id int
+ width int
+ total int64
+ current int64
+ runes barRunes
+ trimLeftSpace bool
+ trimRightSpace bool
+ toComplete bool
+ removeOnComplete bool
+ barClearOnComplete bool
+ completeFlushed bool
+ aDecorators []decor.Decorator
+ pDecorators []decor.Decorator
+ amountReceivers []decor.AmountReceiver
+ shutdownListeners []decor.ShutdownListener
+ refill *refill
+ bufP, bufB, bufA *bytes.Buffer
+ bufNL *bytes.Buffer
+ panicMsg string
+ newLineExtendFn func(io.Writer, *decor.Statistics)
+
+ // following options are assigned to the *Bar
+ priority int
+ runningBar *Bar
+ }
+ refill struct {
+ char rune
+ till int64
+ }
+ frameReader struct {
+ io.Reader
+ extendedLines int
+ toShutdown bool
+ removeOnComplete bool
+ }
+)
+
+func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar {
+ if total <= 0 {
+ total = time.Now().Unix()
+ }
+
+ s := &bState{
+ id: id,
+ priority: id,
+ total: total,
+ }
+
+ for _, opt := range options {
+ if opt != nil {
+ opt(s)
+ }
+ }
+
+ s.bufP = bytes.NewBuffer(make([]byte, 0, s.width))
+ s.bufB = bytes.NewBuffer(make([]byte, 0, s.width))
+ s.bufA = bytes.NewBuffer(make([]byte, 0, s.width))
+
+ b := &Bar{
+ priority: s.priority,
+ runningBar: s.runningBar,
+ operateState: make(chan func(*bState)),
+ int64Ch: make(chan int64),
+ boolCh: make(chan bool),
+ frameReaderCh: make(chan *frameReader, 1),
+ syncTableCh: make(chan [][]chan int),
+ done: make(chan struct{}),
+ shutdown: make(chan struct{}),
+ }
+
+ if b.runningBar != nil {
+ b.priority = b.runningBar.priority
+ }
+
+ if s.newLineExtendFn != nil {
+ s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
+ }
+
+ go b.serve(wg, s, cancel)
+ return b
+}
+
+// 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 {
+ panic("expect io.Reader, got nil")
+ }
+ rc, ok := r.(io.ReadCloser)
+ if !ok {
+ rc = ioutil.NopCloser(r)
+ }
+ return &proxyReader{rc, b, time.Now()}
+}
+
+// ID returs id of the bar.
+func (b *Bar) ID() int {
+ select {
+ case b.operateState <- func(s *bState) { b.int64Ch <- int64(s.id) }:
+ return int(<-b.int64Ch)
+ 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 {
+ select {
+ case b.operateState <- func(s *bState) { b.int64Ch <- s.current }:
+ return <-b.int64Ch
+ case <-b.done:
+ return b.cacheState.current
+ }
+}
+
+// SetTotal sets total dynamically.
+// Set final to true, when total is known, it will trigger bar complete event.
+func (b *Bar) SetTotal(total int64, final bool) bool {
+ select {
+ case b.operateState <- func(s *bState) {
+ if total > 0 {
+ s.total = total
+ }
+ if final {
+ s.current = s.total
+ s.toComplete = true
+ }
+ }:
+ return true
+ case <-b.done:
+ return false
+ }
+}
+
+// SetRefill sets fill rune to r, up until n.
+func (b *Bar) SetRefill(n int, r rune) {
+ if n <= 0 {
+ return
+ }
+ b.operateState <- func(s *bState) {
+ s.refill = &refill{r, int64(n)}
+ }
+}
+
+// RefillBy is deprecated, use SetRefill
+func (b *Bar) RefillBy(n int, r rune) {
+ b.SetRefill(n, r)
+}
+
+// Increment is a shorthand for b.IncrBy(1).
+func (b *Bar) Increment() {
+ b.IncrBy(1)
+}
+
+// IncrBy increments progress bar by amount of n.
+// wdd is optional work duration i.e. time.Since(start),
+// which expected to be provided, if any ewma based decorator is used.
+func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
+ select {
+ case b.operateState <- func(s *bState) {
+ s.current += int64(n)
+ if s.current >= s.total {
+ s.current = s.total
+ s.toComplete = true
+ }
+ for _, ar := range s.amountReceivers {
+ ar.NextAmount(n, wdd...)
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// Completed reports whether the bar is in completed state.
+func (b *Bar) Completed() bool {
+ // omit select here, because primary usage of the method is for loop
+ // condition, like for !bar.Completed() {...}
+ // so when toComplete=true it is called once (at which time, the bar is still alive),
+ // then quits the loop and never suppose to be called afterwards.
+ return <-b.boolCh
+}
+
+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 (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) {
+ defer wg.Done()
+ for {
+ select {
+ case op := <-b.operateState:
+ op(s)
+ case b.boolCh <- s.toComplete:
+ case <-cancel:
+ s.toComplete = true
+ cancel = nil
+ case <-b.shutdown:
+ b.cacheState = s
+ close(b.done)
+ for _, sl := range s.shutdownListeners {
+ sl.Shutdown()
+ }
+ return
+ }
+ }
+}
+
+func (b *Bar) render(debugOut io.Writer, tw int) {
+ select {
+ case b.operateState <- func(s *bState) {
+ defer func() {
+ // recovering if user defined decorator panics for example
+ if p := recover(); p != nil {
+ s.panicMsg = fmt.Sprintf("panic: %v", p)
+ fmt.Fprintf(debugOut, "%s %s bar id %02d %v\n", "[mpb]", time.Now(), s.id, s.panicMsg)
+ b.frameReaderCh <- &frameReader{
+ Reader: strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg)),
+ toShutdown: true,
+ }
+ }
+ }()
+ 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)
+ }
+ b.frameReaderCh <- &frameReader{
+ Reader: r,
+ extendedLines: extendedLines,
+ toShutdown: s.toComplete && !s.completeFlushed,
+ removeOnComplete: s.removeOnComplete,
+ }
+ s.completeFlushed = s.toComplete
+ }:
+ case <-b.done:
+ 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)
+ }
+ b.frameReaderCh <- &frameReader{
+ Reader: r,
+ extendedLines: extendedLines,
+ }
+ }
+}
+
+func (s *bState) draw(termWidth int) io.Reader {
+ defer s.bufA.WriteByte('\n')
+
+ if s.panicMsg != "" {
+ return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
+ }
+
+ stat := newStatistics(s)
+
+ for _, d := range s.pDecorators {
+ s.bufP.WriteString(d.Decor(stat))
+ }
+
+ for _, d := range s.aDecorators {
+ s.bufA.WriteString(d.Decor(stat))
+ }
+
+ prependCount := utf8.RuneCount(s.bufP.Bytes())
+ appendCount := utf8.RuneCount(s.bufA.Bytes())
+
+ if s.barClearOnComplete && s.completeFlushed {
+ return io.MultiReader(s.bufP, s.bufA)
+ }
+
+ s.fillBar(s.width)
+ barCount := utf8.RuneCount(s.bufB.Bytes())
+ totalCount := prependCount + barCount + appendCount
+ if spaceCount := 0; totalCount > termWidth {
+ if !s.trimLeftSpace {
+ spaceCount++
+ }
+ if !s.trimRightSpace {
+ spaceCount++
+ }
+ s.fillBar(termWidth - prependCount - appendCount - spaceCount)
+ }
+
+ return io.MultiReader(s.bufP, s.bufB, s.bufA)
+}
+
+func (s *bState) fillBar(width int) {
+ defer func() {
+ s.bufB.WriteRune(s.runes[rRight])
+ if !s.trimRightSpace {
+ s.bufB.WriteByte(' ')
+ }
+ }()
+
+ s.bufB.Reset()
+ if !s.trimLeftSpace {
+ s.bufB.WriteByte(' ')
+ }
+ s.bufB.WriteRune(s.runes[rLeft])
+ if width <= 2 {
+ return
+ }
+
+ // bar s.width without leftEnd and rightEnd runes
+ barWidth := width - 2
+
+ completedWidth := internal.Percentage(s.total, s.current, int64(barWidth))
+
+ if s.refill != nil {
+ till := internal.Percentage(s.total, s.refill.till, int64(barWidth))
+ // append refill rune
+ var i int64
+ for i = 0; i < till; i++ {
+ s.bufB.WriteRune(s.refill.char)
+ }
+ for i = till; i < completedWidth; i++ {
+ s.bufB.WriteRune(s.runes[rFill])
+ }
+ } else {
+ var i int64
+ for i = 0; i < completedWidth; i++ {
+ s.bufB.WriteRune(s.runes[rFill])
+ }
+ }
+
+ if completedWidth < int64(barWidth) && completedWidth > 0 {
+ _, size := utf8.DecodeLastRune(s.bufB.Bytes())
+ s.bufB.Truncate(s.bufB.Len() - size)
+ s.bufB.WriteRune(s.runes[rTip])
+ }
+
+ for i := completedWidth; i < int64(barWidth); i++ {
+ s.bufB.WriteRune(s.runes[rEmpty])
+ }
+}
+
+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 ok, ch := d.Syncable(); ok {
+ columns = append(columns, ch)
+ pCount++
+ }
+ }
+ var aCount int
+ for _, d := range s.aDecorators {
+ if ok, ch := d.Syncable(); 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 strToBarRunes(format string) (array barRunes) {
+ for i, n := 0, 0; len(format) > 0; i++ {
+ array[i], n = utf8.DecodeRuneInString(format)
+ format = format[n:]
+ }
+ return
+}
+
+func countLines(b []byte) int {
+ return bytes.Count(b, []byte("\n"))
+}