2022-05-09 21:36:18 +02:00
|
|
|
//go:build linux || darwin || freebsd || netbsd || openbsd || solaris || dragonfly || windows || plan9 || aix
|
2022-05-02 09:06:34 +02:00
|
|
|
|
|
|
|
package pb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2022-05-09 21:36:18 +02:00
|
|
|
"github.com/dmitryk-dk/pb/v3/termutil"
|
2022-05-02 09:06:34 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Create and start new pool with given bars
|
|
|
|
// You need call pool.Stop() after work
|
|
|
|
func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) {
|
|
|
|
pool = new(Pool)
|
|
|
|
if err = pool.Start(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
pool.Add(pbs...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewPool initialises a pool with progress bars, but
|
|
|
|
// doesn't start it. You need to call Start manually
|
|
|
|
func NewPool(pbs ...*ProgressBar) (pool *Pool) {
|
|
|
|
pool = new(Pool)
|
|
|
|
pool.Add(pbs...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type Pool struct {
|
|
|
|
Output io.Writer
|
|
|
|
RefreshRate time.Duration
|
|
|
|
bars []*ProgressBar
|
|
|
|
lastBarsCount int
|
|
|
|
shutdownCh chan struct{}
|
|
|
|
workerCh chan struct{}
|
|
|
|
m sync.Mutex
|
|
|
|
finishOnce sync.Once
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add progress bars.
|
|
|
|
func (p *Pool) Add(pbs ...*ProgressBar) {
|
|
|
|
p.m.Lock()
|
|
|
|
defer p.m.Unlock()
|
|
|
|
for _, bar := range pbs {
|
|
|
|
bar.Set(Static, true)
|
|
|
|
bar.Start()
|
|
|
|
p.bars = append(p.bars, bar)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pool) Start() (err error) {
|
|
|
|
p.RefreshRate = defaultRefreshRate
|
|
|
|
p.shutdownCh, err = termutil.RawModeOn()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
p.workerCh = make(chan struct{})
|
|
|
|
go p.writer()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pool) writer() {
|
|
|
|
var first = true
|
|
|
|
defer func() {
|
|
|
|
if first == false {
|
|
|
|
p.print(false)
|
|
|
|
} else {
|
|
|
|
p.print(true)
|
|
|
|
p.print(false)
|
|
|
|
}
|
|
|
|
close(p.workerCh)
|
|
|
|
}()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-time.After(p.RefreshRate):
|
|
|
|
if p.print(first) {
|
|
|
|
p.print(false)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
first = false
|
|
|
|
case <-p.shutdownCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore terminal state and close pool
|
|
|
|
func (p *Pool) Stop() error {
|
|
|
|
p.finishOnce.Do(func() {
|
|
|
|
if p.shutdownCh != nil {
|
|
|
|
close(p.shutdownCh)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// Wait for the worker to complete
|
|
|
|
select {
|
|
|
|
case <-p.workerCh:
|
|
|
|
}
|
|
|
|
|
|
|
|
return termutil.RawModeOff()
|
|
|
|
}
|