diff --git a/progress.go b/progress.go index 0df63b0..4359144 100644 --- a/progress.go +++ b/progress.go @@ -9,6 +9,15 @@ "github.com/gosuri/uilive" ) +type opType uint + +const ( + add opType = iota + remove +) + +const refreshRate = 30 + // progress represents the container that renders progress bars type progress struct { // out is the writer to render progress bars to @@ -19,11 +28,16 @@ lw *uilive.Writer - // new Bars can be added over this channel - bars chan *Bar + op chan *operation // new refresh interval to be send over this channel interval chan time.Duration +} + +type operation struct { + kind opType + bar *Bar + ok chan bool } // New returns a new progress bar with defaults @@ -31,15 +45,15 @@ p := &progress{ out: os.Stdout, lw: uilive.New(), - bars: make(chan *Bar), + op: make(chan *operation), interval: make(chan time.Duration), } go p.server() return p } -// RefreshInterval overrides default interval value 30 ms -func (p *progress) RefreshInterval(d time.Duration) *progress { +// RefreshRate overrides default (30ms) refreshRate value +func (p *progress) RefreshRate(d time.Duration) *progress { p.interval <- d return p } @@ -55,8 +69,14 @@ func (p *progress) AddBar(total int) *Bar { bar := NewBar(total) // bar.Width = p.Width - p.bars <- bar + p.op <- &operation{add, bar, nil} return bar +} + +func (p *progress) RemoveBar(b *Bar) bool { + result := make(chan bool) + p.op <- &operation{remove, b, result} + return <-result } // Bypass returns a writer which allows non-buffered data to be written to the underlying output @@ -66,25 +86,39 @@ // Stop stops listening func (p *progress) Stop() { - close(p.bars) + close(p.op) } -// server listens for updates and renders the progress bars +// server monitors underlying channels and renders any progress bars func (p *progress) server() { - t := time.NewTicker(30 * time.Millisecond) + t := time.NewTicker(refreshRate * time.Millisecond) bars := make([]*Bar, 0) p.lw.Out = p.out for { select { - case bar, ok := <-p.bars: + case op, ok := <-p.op: if !ok { t.Stop() + close(p.interval) return } - bars = append(bars, bar) + switch op.kind { + case add: + bars = append(bars, op.bar) + case remove: + var ok bool + for i, b := range bars { + if b == op.bar { + bars = append(bars[:i], bars[i+1:]...) + ok = true + break + } + } + op.ok <- ok + } case <-t.C: - for _, bar := range bars { - fmt.Fprintln(p.lw, bar.String()) + for _, b := range bars { + fmt.Fprintln(p.lw, b.String()) } p.lw.Flush() case d := <-p.interval: