147 lines
3.0 KiB
Go
147 lines
3.0 KiB
Go
package uiprogress
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/gosuri/uilive"
|
|
)
|
|
|
|
// Out is the default writer to render progress bars to
|
|
var Out = os.Stdout
|
|
|
|
// RefreshInterval in the default time duration to wait for refreshing the output
|
|
var RefreshInterval = time.Millisecond * 10
|
|
|
|
// defaultProgress is the default progress
|
|
var defaultProgress = New()
|
|
|
|
// Progress represents the container that renders progress bars
|
|
type Progress struct {
|
|
// Out is the writer to render progress bars to
|
|
Out io.Writer
|
|
|
|
// Width is the width of the progress bars
|
|
Width int
|
|
|
|
// Bars is the collection of progress bars
|
|
Bars []*Bar
|
|
|
|
// RefreshInterval in the time duration to wait for refreshing the output
|
|
RefreshInterval time.Duration
|
|
|
|
lw *uilive.Writer
|
|
ticker *time.Ticker
|
|
tdone chan bool
|
|
mtx *sync.RWMutex
|
|
}
|
|
|
|
// New returns a new progress bar with defaults
|
|
func New() *Progress {
|
|
lw := uilive.New()
|
|
lw.Out = Out
|
|
|
|
return &Progress{
|
|
Width: Width,
|
|
Out: Out,
|
|
Bars: make([]*Bar, 0),
|
|
RefreshInterval: RefreshInterval,
|
|
|
|
tdone: make(chan bool),
|
|
lw: uilive.New(),
|
|
mtx: &sync.RWMutex{},
|
|
}
|
|
}
|
|
|
|
// AddBar creates a new progress bar and adds it to the default progress container
|
|
func AddBar(total int) *Bar {
|
|
return defaultProgress.AddBar(total)
|
|
}
|
|
|
|
// Start starts the rendering the progress of progress bars using the DefaultProgress. It listens for updates using `bar.Set(n)` and new bars when added using `AddBar`
|
|
func Start() {
|
|
defaultProgress.Start()
|
|
}
|
|
|
|
// Stop stops listening
|
|
func Stop() {
|
|
defaultProgress.Stop()
|
|
}
|
|
|
|
// Listen listens for updates and renders the progress bars
|
|
func Listen() {
|
|
defaultProgress.Listen()
|
|
}
|
|
|
|
func (p *Progress) SetOut(o io.Writer) {
|
|
p.mtx.Lock()
|
|
defer p.mtx.Unlock()
|
|
|
|
p.Out = o
|
|
p.lw.Out = o
|
|
}
|
|
|
|
func (p *Progress) SetRefreshInterval(interval time.Duration) {
|
|
p.mtx.Lock()
|
|
defer p.mtx.Unlock()
|
|
p.RefreshInterval = interval
|
|
}
|
|
|
|
// AddBar creates a new progress bar and adds to the container
|
|
func (p *Progress) AddBar(total int) *Bar {
|
|
p.mtx.Lock()
|
|
defer p.mtx.Unlock()
|
|
|
|
bar := NewBar(total)
|
|
bar.Width = p.Width
|
|
p.Bars = append(p.Bars, bar)
|
|
return bar
|
|
}
|
|
|
|
// Listen listens for updates and renders the progress bars
|
|
func (p *Progress) Listen() {
|
|
for {
|
|
|
|
p.mtx.Lock()
|
|
interval := p.RefreshInterval
|
|
p.mtx.Unlock()
|
|
|
|
select {
|
|
case <-time.After(interval):
|
|
p.print()
|
|
case <-p.tdone:
|
|
p.print()
|
|
close(p.tdone)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Progress) print() {
|
|
p.mtx.Lock()
|
|
defer p.mtx.Unlock()
|
|
for _, bar := range p.Bars {
|
|
fmt.Fprintln(p.lw, bar.String())
|
|
}
|
|
p.lw.Flush()
|
|
}
|
|
|
|
// Start starts the rendering the progress of progress bars. It listens for updates using `bar.Set(n)` and new bars when added using `AddBar`
|
|
func (p *Progress) Start() {
|
|
go p.Listen()
|
|
}
|
|
|
|
// Stop stops listening
|
|
func (p *Progress) Stop() {
|
|
p.tdone <- true
|
|
<-p.tdone
|
|
}
|
|
|
|
// Bypass returns a writer which allows non-buffered data to be written to the underlying output
|
|
func (p *Progress) Bypass() io.Writer {
|
|
return p.lw.Bypass()
|
|
}
|