238 lines
5.8 KiB
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())
|
|
}
|