diff --git a/bar.go b/bar.go index 2a56984..be10ba1 100644 --- a/bar.go +++ b/bar.go @@ -29,14 +29,14 @@ lastState state } -// Statistics represents statistics of the progress bar -// instance of this, sent to DecoratorFunc, as param +// Statistics represents statistics of the progress bar. +// Cantains: Total, Current, TimeElapsed, TimePerItemEstimate type Statistics struct { Total, Current int64 - TermWidth int TimeElapsed, TimePerItemEstimate time.Duration } +// Eta returns exponential-weighted-moving-average ETA estimator func (s *Statistics) Eta() time.Duration { return time.Duration(s.Total-s.Current) * s.TimePerItemEstimate } @@ -201,15 +201,23 @@ b.refillCh <- &refill{c, int64(n)} } -// Current returns the actual current. -func (b *Bar) Current() int64 { - if IsClosed(b.done) { - return b.lastState.current - } - ch := make(chan state, 1) - b.stateReqCh <- ch - state := <-ch - return state.current +// GetAppenders returns slice of appender DecoratorFunc +func (b *Bar) GetAppenders() []DecoratorFunc { + s := b.getState() + return s.appendFuncs +} + +// GetAppenders returns slice of prepender DecoratorFunc +func (b *Bar) GetPrependers() []DecoratorFunc { + s := b.getState() + return s.prependFuncs +} + +// GetStatistics returs *Statistics, which contains information like Tottal, +// Current, TimeElapsed and TimePerItemEstimate +func (b *Bar) GetStatistics() *Statistics { + state := b.getState() + return state.newStat() } // InProgress returns true, while progress is running @@ -262,14 +270,18 @@ b.completeReqCh <- struct{}{} } -func (b *Bar) bytes(termWidth int) []byte { - if IsClosed(b.done) { - return b.lastState.draw(termWidth) +func (b *Bar) getState() state { + if IsClosed(b.done) { + return b.lastState } ch := make(chan state, 1) b.stateReqCh <- ch - s := <-ch - return s.draw(termWidth) + return <-ch +} + +func (b *Bar) bytes(termWidth int) []byte { + state := b.getState() + return state.draw(termWidth) } func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, total int64, barWidth int) { @@ -365,17 +377,21 @@ b.removeReqCh <- struct{}{} } +func (s *state) newStat() *Statistics { + return &Statistics{ + Total: s.total, + Current: s.current, + TimeElapsed: s.timeElapsed, + TimePerItemEstimate: s.timePerItem, + } +} + func (s *state) draw(termWidth int) []byte { if termWidth <= 0 { termWidth = s.barWidth } - stat := &Statistics{ - Total: s.total, - Current: s.current, - TermWidth: termWidth, - TimeElapsed: s.timeElapsed, - TimePerItemEstimate: s.timePerItem, - } + + stat := s.newStat() // render append functions to the right of the bar var appendBlock []byte @@ -459,30 +475,6 @@ return buf } -func (b *Bar) status() int { - var total, current int64 - if IsClosed(b.done) { - total = b.lastState.total - current = b.lastState.current - } else { - ch := make(chan state, 1) - b.stateReqCh <- ch - state := <-ch - total = state.total - current = state.current - } - return percentage(total, current, 100) -} - -// SortableBarSlice satisfies sort interface -type SortableBarSlice []*Bar - -func (p SortableBarSlice) Len() int { return len(p) } - -func (p SortableBarSlice) Less(i, j int) bool { return p[i].status() < p[j].status() } - -func (p SortableBarSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - func calcTimePerItemEstimate(tpie time.Duration, blockStartTime time.Time, alpha float64, items int64) time.Duration { lastBlockTime := time.Since(blockStartTime) lastItemEstimate := float64(lastBlockTime) / float64(items) diff --git a/progress.go b/progress.go index 4e3e06a..5d4ccb3 100644 --- a/progress.go +++ b/progress.go @@ -6,7 +6,6 @@ "io" "log" "os" - "sort" "sync" "time" @@ -20,12 +19,12 @@ var ErrCallAfterStop = errors.New("method call on stopped Progress instance") type ( - // SortType defines sort direction of bar - SortType uint - opType uint + // BeforeRender is a func, which gets called before render process + BeforeRender func([]*Bar) + barOpType uint operation struct { - kind opType + kind barOpType bar *Bar result chan bool } @@ -43,14 +42,8 @@ ) const ( - opBarAdd opType = iota - opBarRemove -) - -const ( - SortNone SortType = iota - SortTop - SortBottom + barAdd barOpType = iota + barRemove ) // default RefreshRate @@ -65,12 +58,12 @@ out io.Writer width int - sort SortType operationCh chan *operation rrChangeReqCh chan time.Duration outChangeReqCh chan io.Writer barCountReqCh chan chan int + brCh chan BeforeRender done chan struct{} } @@ -87,6 +80,7 @@ rrChangeReqCh: make(chan time.Duration), outChangeReqCh: make(chan io.Writer), barCountReqCh: make(chan chan int), + brCh: make(chan BeforeRender), done: make(chan struct{}), wg: new(sync.WaitGroup), ctx: ctx, @@ -127,9 +121,11 @@ return p } -// WithSort sorts the bars, while redering -func (p *Progress) WithSort(sort SortType) *Progress { - p.sort = sort +func (p *Progress) BeforeRenderFunc(f BeforeRender) *Progress { + if IsClosed(p.done) { + panic(ErrCallAfterStop) + } + p.brCh <- f return p } @@ -141,7 +137,7 @@ } result := make(chan bool) bar := newBar(p.ctx, p.wg, total, p.width) - p.operationCh <- &operation{opBarAdd, bar, result} + p.operationCh <- &operation{barAdd, bar, result} if <-result { p.wg.Add(1) } @@ -155,7 +151,7 @@ panic(ErrCallAfterStop) } result := make(chan bool) - p.operationCh <- &operation{opBarRemove, b, result} + p.operationCh <- &operation{barRemove, b, result} return <-result } @@ -187,6 +183,7 @@ }() const numDrawers = 3 bars := make([]*Bar, 0, 4) + var beforeRender BeforeRender var wg sync.WaitGroup recoverIfPanic := func() { if e := recover(); e != nil { @@ -204,10 +201,10 @@ return } switch op.kind { - case opBarAdd: + case barAdd: bars = append(bars, op.bar) op.result <- true - case opBarRemove: + case barRemove: var ok bool for i, b := range bars { if b == op.bar { @@ -221,12 +218,10 @@ } case respCh := <-p.barCountReqCh: respCh <- len(bars) + case beforeRender = <-p.brCh: case <-t.C: - switch p.sort { - case SortTop: - sort.Sort(sort.Reverse(SortableBarSlice(bars))) - case SortBottom: - sort.Sort(SortableBarSlice(bars)) + if beforeRender != nil { + beforeRender(bars) } width, _, _ := cwriter.GetTermSize()