PKG Progress : add complex bar features, refactor & optimize, fix some errors & linter

This commit is contained in:
Nicolas JUHEL
2020-09-11 13:49:40 +02:00
parent f5f8a6367f
commit 2dda29ce48
2 changed files with 251 additions and 102 deletions

View File

@@ -26,47 +26,49 @@
package progress package progress
import ( import (
"context" "time"
"github.com/nabbar/golib/errors"
"github.com/nabbar/golib/semaphore"
"github.com/vbauerster/mpb/v5" "github.com/vbauerster/mpb/v5"
. "github.com/nabbar/golib/errors"
sem "github.com/nabbar/golib/semaphore"
) )
type bar struct { type bar struct {
u bool i time.Time
s semaphore.Sem
t int64 t int64
b *mpb.Bar b *mpb.Bar
s sem.Sem u bool
w bool
} }
type Bar interface { type Bar interface {
Current() int64 Current() int64
Completed() bool Completed() bool
Increment(n int) Reset(total, current int64)
Refill(amount int64) ResetDefined(current int64)
Done()
NewWorker() Error Increment(n int)
Increment64(n int64)
NewWorker() errors.Error
NewWorkerTry() bool NewWorkerTry() bool
DeferWorker() DeferWorker()
DeferMain(dropBar bool) DeferMain(dropBar bool)
WaitAll() Error WaitAll() errors.Error
Context() context.Context
Cancel()
GetBarMPB() *mpb.Bar GetBarMPB() *mpb.Bar
} }
func newBar(b *mpb.Bar, s sem.Sem, total int64) Bar { func newBar(b *mpb.Bar, s semaphore.Sem, total int64, isModeUnic bool) Bar {
return &bar{ return &bar{
u: total > 0, u: total > 0,
t: total, t: total,
b: b, b: b,
s: s, s: s,
w: isModeUnic,
} }
} }
@@ -83,47 +85,88 @@ func (b bar) Completed() bool {
} }
func (b *bar) Increment(n int) { func (b *bar) Increment(n int) {
if n == 0 { if n > 0 {
n = 1
}
b.b.IncrBy(n) b.b.IncrBy(n)
if b.i != semaphore.EmptyTime() {
b.b.DecoratorEwmaUpdate(time.Since(b.i))
}
}
b.i = time.Now()
} }
func (b *bar) Refill(amount int64) { func (b *bar) Increment64(n int64) {
b.b.SetRefill(amount) if n > 0 {
b.b.IncrInt64(n)
if b.i != semaphore.EmptyTime() {
b.b.DecoratorEwmaUpdate(time.Since(b.i))
}
}
b.i = time.Now()
} }
func (b *bar) NewWorker() Error { func (b *bar) ResetDefined(current int64) {
if current >= b.t {
b.b.SetTotal(b.t, true)
b.b.SetRefill(b.t)
} else {
b.b.SetTotal(b.t, false)
b.b.SetRefill(current)
}
}
func (b *bar) Reset(total, current int64) {
b.u = total > 0
b.t = total
b.ResetDefined(current)
}
func (b *bar) Done() {
b.b.SetRefill(b.t)
b.b.SetTotal(b.t, true)
}
func (b *bar) NewWorker() errors.Error {
if !b.u { if !b.u {
b.t++ b.t++
b.b.SetTotal(b.t, false) b.b.SetTotal(b.t, false)
} }
if !b.w {
return b.s.NewWorker() return b.s.NewWorker()
}
return nil
} }
func (b *bar) NewWorkerTry() bool { func (b *bar) NewWorkerTry() bool {
if !b.w {
return b.s.NewWorkerTry() return b.s.NewWorkerTry()
}
return false
} }
func (b *bar) DeferWorker() { func (b *bar) DeferWorker() {
b.b.Increment() b.Increment(1)
b.s.DeferWorker() b.s.DeferWorker()
} }
func (b *bar) DeferMain(dropBar bool) { func (b *bar) DeferMain(dropBar bool) {
b.b.Abort(dropBar) b.b.Abort(dropBar)
if !b.w {
b.s.DeferMain() b.s.DeferMain()
}
} }
func (b *bar) WaitAll() Error { func (b *bar) WaitAll() errors.Error {
if !b.w {
return b.s.WaitAll() return b.s.WaitAll()
} }
func (b bar) Context() context.Context { return nil
return b.s.Context()
}
func (b bar) Cancel() {
b.s.Cancel()
} }

View File

@@ -27,8 +27,8 @@ package progress
import ( import (
"context" "context"
"time"
"github.com/nabbar/golib/errors"
"github.com/nabbar/golib/semaphore" "github.com/nabbar/golib/semaphore"
"github.com/vbauerster/mpb/v5" "github.com/vbauerster/mpb/v5"
"github.com/vbauerster/mpb/v5/decor" "github.com/vbauerster/mpb/v5/decor"
@@ -62,35 +62,45 @@ func GetDefaultMessageDone() string {
type progressBar struct { type progressBar struct {
mpb *mpb.Progress mpb *mpb.Progress
ctx context.Context ctx context.Context
cnl context.CancelFunc
sTimeOut time.Duration
sMaxSimul int sMaxSimul int
sem semaphore.Sem
} }
type ProgressBar interface { type ProgressBar interface {
GetMPB() *mpb.Progress GetMPB() *mpb.Progress
SetMaxThread(maxSimultaneous int)
GetContext() context.Context
SetContext(ctx context.Context) SetContext(ctx context.Context)
GetCancel() context.CancelFunc UnicProcessInit()
SetCancel(cancel context.CancelFunc) UnicProcessWait() errors.Error
UnicProcessNewWorker() errors.Error
UnicProcessDeferWorker()
UnicProcessDefer()
SetSemaphoreOption(maxSimultaneous int, timeout time.Duration) NewBar(total int64, options ...mpb.BarOption) Bar
NewBar(parent context.Context, total int64, options ...mpb.BarOption) Bar NewBarETA(name string, total int64, job string, parent Bar) Bar
NewBarSimpleETA(name string) Bar NewBarCounter(name string, total int64, job string, parent Bar) Bar
NewBarKBits(name string, total int64, job string, parent Bar) Bar
NewBarSimpleETA(name string, total int64) Bar
NewBarSimpleCounter(name string, total int64) Bar NewBarSimpleCounter(name string, total int64) Bar
NewBarSimpleKBits(name string, total int64) Bar
} }
func NewProgressBar(timeout time.Duration, deadline time.Time, parent context.Context, options ...mpb.ContainerOption) ProgressBar { func NewProgressBar(options ...mpb.ContainerOption) ProgressBar {
x, c := semaphore.GetContext(timeout, deadline, parent) return NewProgressBarWithContext(context.Background(), options...)
}
func NewProgressBarWithContext(ctx context.Context, options ...mpb.ContainerOption) ProgressBar {
if ctx == nil {
ctx = context.Background()
}
return &progressBar{ return &progressBar{
mpb: mpb.New(options...), mpb: mpb.New(options...),
ctx: x, ctx: ctx,
cnl: c, sem: nil,
sTimeOut: timeout,
sMaxSimul: semaphore.GetMaxSimultaneous(), sMaxSimul: semaphore.GetMaxSimultaneous(),
} }
} }
@@ -99,77 +109,173 @@ func (p *progressBar) GetMPB() *mpb.Progress {
return p.mpb return p.mpb
} }
func (p *progressBar) SetSemaphoreOption(maxSimultaneous int, timeout time.Duration) { func (p *progressBar) SetMaxThread(maxSimultaneous int) {
p.sMaxSimul = maxSimultaneous p.sMaxSimul = maxSimultaneous
p.sTimeOut = timeout
} }
func (p *progressBar) NewBar(parent context.Context, total int64, options ...mpb.BarOption) Bar { func (p *progressBar) UnicProcessInit() {
if parent == nil { p.sem = p.semaphore()
parent = p.ctx }
}
func (p *progressBar) UnicProcessWait() errors.Error {
if p.sem != nil {
return p.sem.WaitAll()
}
return nil
}
func (p *progressBar) UnicProcessNewWorker() errors.Error {
if p.sem != nil {
return p.sem.NewWorker()
}
return nil
}
func (p *progressBar) UnicProcessDeferWorker() {
if p.sem != nil {
p.sem.DeferWorker()
}
}
func (p *progressBar) UnicProcessDefer() {
if p.sem != nil {
p.sem.DeferMain()
}
}
func (p *progressBar) semaphore() semaphore.Sem {
return semaphore.NewSemaphoreWithContext(p.ctx, p.sMaxSimul)
}
func (p *progressBar) NewBar(total int64, options ...mpb.BarOption) Bar {
return newBar( return newBar(
p.mpb.AddBar(0, options...), p.mpb.AddBar(0, options...),
semaphore.NewSemaphore(p.sMaxSimul, p.sTimeOut, semaphore.GetEmptyTime(), parent), p.semaphore(),
total, total,
p.sem != nil,
) )
} }
func (p *progressBar) NewBarSimpleETA(name string) Bar { func (p *progressBar) NewBarSimpleETA(name string, total int64) Bar {
return newBar( return p.NewBarETA(name, total, "", nil)
p.mpb.AddBar(0,
mpb.BarStyle(defaultStyle),
mpb.PrependDecorators(
// display our name with one space on the right
decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
// replace ETA decorator with "done" message, OnComplete event
decor.OnComplete(
// nolint: gomnd
decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), defaultMessageDone,
),
),
mpb.AppendDecorators(decor.Percentage()),
),
semaphore.NewSemaphore(p.sMaxSimul, p.sTimeOut, semaphore.GetEmptyTime(), p.ctx),
0,
)
} }
func (p *progressBar) NewBarSimpleCounter(name string, total int64) Bar { func (p *progressBar) NewBarSimpleCounter(name string, total int64) Bar {
return newBar( return p.NewBarCounter(name, total, "", nil)
p.mpb.AddBar(total, }
mpb.BarStyle(defaultStyle),
mpb.PrependDecorators( func (p *progressBar) NewBarSimpleKBits(name string, total int64) Bar {
return p.NewBarKBits(name, total, "", nil)
}
func (p *progressBar) NewBarETA(name string, total int64, job string, parent Bar) Bar {
if parent != nil && job != "" {
return newBar(p.addBarJob(total, name, job, nil, nil, parent.GetBarMPB()), p.semaphore(), total, p.sem != nil)
} else {
return newBar(p.addBarSimple(total, name, nil, nil), p.semaphore(), total, p.sem != nil)
}
}
func (p *progressBar) NewBarCounter(name string, total int64, job string, parent Bar) Bar {
d := decor.CountersNoUnit("[%d / %d] ", decor.WCSyncWidth)
if parent != nil && job != "" {
return newBar(p.addBarJob(total, name, job, d, nil, parent.GetBarMPB()), p.semaphore(), total, p.sem != nil)
} else {
return newBar(p.addBarSimple(total, name, d, nil), p.semaphore(), total, p.sem != nil)
}
}
func (p *progressBar) NewBarKBits(name string, total int64, job string, parent Bar) Bar {
//nolint #gomnd
d := decor.Counters(decor.UnitKiB, "% .2f / % .2f", decor.WC{W: 20, C: decor.DextraSpace})
a := []decor.Decorator{
//nolint #gomnd
decor.Percentage(decor.WC{W: 5, C: 0}),
decor.Name(" | "),
//nolint #gomnd
decor.EwmaSpeed(decor.UnitKiB, "% .2f", 60),
}
if parent != nil && job != "" {
return newBar(p.addBarJob(total, name, job, d, a, parent.GetBarMPB()), p.semaphore(), total, p.sem != nil)
} else {
return newBar(p.addBarSimple(total, name, d, a), p.semaphore(), total, p.sem != nil)
}
}
func (p *progressBar) addBarSimple(total int64, name string, counter decor.Decorator, pct []decor.Decorator) *mpb.Bar {
pr := make([]decor.Decorator, 0)
// display our name with one space on the right // display our name with one space on the right
decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), pr = append(pr, decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}))
if counter != nil {
// use counter (no ETA) // use counter (no ETA)
decor.CountersNoUnit("[%d / %d] ", decor.WCSyncWidth), pr = append(pr, counter)
}
//nolint #gomnd
pr = append(pr, decor.Name(" ", decor.WC{W: 3, C: decor.DidentRight | decor.DextraSpace}))
// replace ETA decorator with "done" message, OnComplete event // replace ETA decorator with "done" message, OnComplete event
decor.OnComplete( pr = append(pr, decor.OnComplete(
// nolint: gomnd // nolint: gomnd
decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), defaultMessageDone, decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: len(defaultMessageDone) + 1, C: 0}), defaultMessageDone,
), ))
),
mpb.AppendDecorators(decor.Percentage()), if pct == nil {
), pct = make([]decor.Decorator, 0)
semaphore.NewSemaphore(p.sMaxSimul, p.sTimeOut, semaphore.GetEmptyTime(), p.ctx), //nolint #gomnd
total, pct = append(pct, decor.Percentage(decor.WC{W: 5, C: 0}))
}
return p.mpb.AddBar(total,
mpb.BarStyle(defaultStyle),
mpb.BarFillerClearOnComplete(),
mpb.PrependDecorators(pr...),
mpb.AppendDecorators(pct...),
) )
} }
func (p *progressBar) GetContext() context.Context { func (p *progressBar) addBarJob(total int64, name, job string, counter decor.Decorator, pct []decor.Decorator, bar *mpb.Bar) *mpb.Bar {
return p.ctx pr := make([]decor.Decorator, 0)
// display our name with one space on the right
pr = append(pr, decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}))
// display our job task with one space on the right
pr = append(pr, decor.Name(job, decor.WC{W: len(job) + 1, C: decor.DidentRight | decor.DextraSpace}))
if counter != nil {
// use counter (no ETA)
pr = append(pr, counter)
}
//nolint #gomnd
pr = append(pr, decor.Name(" ", decor.WC{W: 3, C: decor.DidentRight | decor.DextraSpace}))
if bar != nil {
pr = append(pr, decor.OnComplete(
// replace ETA decorator with "done" message, OnComplete event
decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: len(defaultMessageDone) + 1, C: 0}), defaultMessageDone,
))
}
if pct == nil {
pct = make([]decor.Decorator, 0)
//nolint #gomnd
pct = append(pct, decor.Percentage(decor.WC{W: 5, C: 0}))
}
if bar == nil {
return p.mpb.AddBar(total,
mpb.BarStyle(defaultStyle),
mpb.BarFillerClearOnComplete(),
mpb.PrependDecorators(pr...),
mpb.AppendDecorators(pct...),
)
} else {
return p.mpb.AddBar(total,
mpb.BarStyle(defaultStyle),
mpb.BarQueueAfter(bar),
mpb.BarFillerClearOnComplete(),
mpb.PrependDecorators(pr...),
mpb.AppendDecorators(pct...),
)
}
} }
func (p *progressBar) SetContext(ctx context.Context) { func (p *progressBar) SetContext(ctx context.Context) {
p.ctx = ctx p.ctx = ctx
} }
func (p *progressBar) GetCancel() context.CancelFunc {
return p.cnl
}
func (p *progressBar) SetCancel(cancel context.CancelFunc) {
p.cnl = cancel
}