croc/vendor/github.com/gosuri/uiprogress/bar.go

238 lines
5.8 KiB
Go

package uiprogress
import (
"bytes"
"errors"
"fmt"
"sync"
"time"
"github.com/gosuri/uiprogress/util/strutil"
)
var (
// Fill is the default character representing completed progress
Fill byte = '='
// Head is the default character that moves when progress is updated
Head byte = '>'
// Empty is the default character that represents the empty progress
Empty byte = '-'
// LeftEnd is the default character in the left most part of the progress indicator
LeftEnd byte = '['
// RightEnd is the default character in the right most part of the progress indicator
RightEnd byte = ']'
// Width is the default width of the progress bar
Width = 70
// ErrMaxCurrentReached is error when trying to set current value that exceeds the total value
ErrMaxCurrentReached = errors.New("errors: current value is greater total value")
)
// Bar represents a progress bar
type Bar struct {
// Total of the total for the progress bar
Total int
// LeftEnd is character in the left most part of the progress indicator. Defaults to '['
LeftEnd byte
// RightEnd is character in the right most part of the progress indicator. Defaults to ']'
RightEnd byte
// Fill is the character representing completed progress. Defaults to '='
Fill byte
// Head is the character that moves when progress is updated. Defaults to '>'
Head byte
// Empty is the character that represents the empty progress. Default is '-'
Empty byte
// TimeStated is time progress began
TimeStarted time.Time
// Width is the width of the progress bar
Width int
// timeElased is the time elapsed for the progress
timeElapsed time.Duration
current int
mtx *sync.RWMutex
appendFuncs []DecoratorFunc
prependFuncs []DecoratorFunc
}
// DecoratorFunc is a function that can be prepended and appended to the progress bar
type DecoratorFunc func(b *Bar) string
// NewBar returns a new progress bar
func NewBar(total int) *Bar {
return &Bar{
Total: total,
Width: Width,
LeftEnd: LeftEnd,
RightEnd: RightEnd,
Head: Head,
Fill: Fill,
Empty: Empty,
mtx: &sync.RWMutex{},
}
}
// Set the current count of the bar. It returns ErrMaxCurrentReached when trying n exceeds the total value. This is atomic operation and concurancy safe.
func (b *Bar) Set(n int) error {
b.mtx.Lock()
defer b.mtx.Unlock()
if n > b.Total {
return ErrMaxCurrentReached
}
b.current = n
return nil
}
// Incr increments the current value by 1, time elapsed to current time and returns true. It returns false if the cursor has reached or exceeds total value.
func (b *Bar) Incr() bool {
b.mtx.Lock()
defer b.mtx.Unlock()
n := b.current + 1
if n > b.Total {
return false
}
var t time.Time
if b.TimeStarted == t {
b.TimeStarted = time.Now()
}
b.timeElapsed = time.Since(b.TimeStarted)
b.current = n
return true
}
// Current returns the current progress of the bar
func (b *Bar) Current() int {
b.mtx.RLock()
defer b.mtx.RUnlock()
return b.current
}
// AppendFunc runs the decorator function and renders the output on the right of the progress bar
func (b *Bar) AppendFunc(f DecoratorFunc) *Bar {
b.mtx.Lock()
defer b.mtx.Unlock()
b.appendFuncs = append(b.appendFuncs, f)
return b
}
// AppendCompleted appends the completion percent to the progress bar
func (b *Bar) AppendCompleted() *Bar {
b.AppendFunc(func(b *Bar) string {
return b.CompletedPercentString()
})
return b
}
// AppendElapsed appends the time elapsed the be progress bar
func (b *Bar) AppendElapsed() *Bar {
b.AppendFunc(func(b *Bar) string {
return strutil.PadLeft(b.TimeElapsedString(), 5, ' ')
})
return b
}
// PrependFunc runs decorator function and render the output left the progress bar
func (b *Bar) PrependFunc(f DecoratorFunc) *Bar {
b.mtx.Lock()
defer b.mtx.Unlock()
b.prependFuncs = append(b.prependFuncs, f)
return b
}
// PrependCompleted prepends the precent completed to the progress bar
func (b *Bar) PrependCompleted() *Bar {
b.PrependFunc(func(b *Bar) string {
return b.CompletedPercentString()
})
return b
}
// PrependElapsed prepends the time elapsed to the begining of the bar
func (b *Bar) PrependElapsed() *Bar {
b.PrependFunc(func(b *Bar) string {
return strutil.PadLeft(b.TimeElapsedString(), 5, ' ')
})
return b
}
// Bytes returns the byte presentation of the progress bar
func (b *Bar) Bytes() []byte {
completedWidth := int(float64(b.Width) * (b.CompletedPercent() / 100.00))
// add fill and empty bits
var buf bytes.Buffer
for i := 0; i < completedWidth; i++ {
buf.WriteByte(b.Fill)
}
for i := 0; i < b.Width-completedWidth; i++ {
buf.WriteByte(b.Empty)
}
// set head bit
pb := buf.Bytes()
if completedWidth > 0 && completedWidth < b.Width {
pb[completedWidth-1] = b.Head
}
// set left and right ends bits
pb[0], pb[len(pb)-1] = b.LeftEnd, b.RightEnd
// render append functions to the right of the bar
for _, f := range b.appendFuncs {
pb = append(pb, ' ')
pb = append(pb, []byte(f(b))...)
}
// render prepend functions to the left of the bar
for _, f := range b.prependFuncs {
args := []byte(f(b))
args = append(args, ' ')
pb = append(args, pb...)
}
return pb
}
// String returns the string representation of the bar
func (b *Bar) String() string {
return string(b.Bytes())
}
// CompletedPercent return the percent completed
func (b *Bar) CompletedPercent() float64 {
return (float64(b.Current()) / float64(b.Total)) * 100.00
}
// CompletedPercentString returns the formatted string representation of the completed percent
func (b *Bar) CompletedPercentString() string {
return fmt.Sprintf("%3.f%%", b.CompletedPercent())
}
// TimeElapsed returns the time elapsed
func (b *Bar) TimeElapsed() time.Duration {
b.mtx.RLock()
defer b.mtx.RUnlock()
return b.timeElapsed
}
// TimeElapsedString returns the formatted string represenation of the time elapsed
func (b *Bar) TimeElapsedString() string {
return strutil.PrettyTime(b.TimeElapsed())
}