RemoveBar implementation
Vladimir Bauer
9 years ago
| 8 | 8 | "github.com/gosuri/uilive" |
| 9 | 9 | ) |
| 10 | 10 | |
| 11 | type opType uint | |
| 12 | ||
| 13 | const ( | |
| 14 | add opType = iota | |
| 15 | remove | |
| 16 | ) | |
| 17 | ||
| 18 | const refreshRate = 30 | |
| 19 | ||
| 11 | 20 | // progress represents the container that renders progress bars |
| 12 | 21 | type progress struct { |
| 13 | 22 | // out is the writer to render progress bars to |
| 18 | 27 | |
| 19 | 28 | lw *uilive.Writer |
| 20 | 29 | |
| 21 | // new Bars can be added over this channel | |
| 22 | bars chan *Bar | |
| 30 | op chan *operation | |
| 23 | 31 | |
| 24 | 32 | // new refresh interval to be send over this channel |
| 25 | 33 | interval chan time.Duration |
| 34 | } | |
| 35 | ||
| 36 | type operation struct { | |
| 37 | kind opType | |
| 38 | bar *Bar | |
| 39 | ok chan bool | |
| 26 | 40 | } |
| 27 | 41 | |
| 28 | 42 | // New returns a new progress bar with defaults |
| 30 | 44 | p := &progress{ |
| 31 | 45 | out: os.Stdout, |
| 32 | 46 | lw: uilive.New(), |
| 33 | bars: make(chan *Bar), | |
| 47 | op: make(chan *operation), | |
| 34 | 48 | interval: make(chan time.Duration), |
| 35 | 49 | } |
| 36 | 50 | go p.server() |
| 37 | 51 | return p |
| 38 | 52 | } |
| 39 | 53 | |
| 40 | // RefreshInterval overrides default interval value 30 ms | |
| 41 | func (p *progress) RefreshInterval(d time.Duration) *progress { | |
| 54 | // RefreshRate overrides default (30ms) refreshRate value | |
| 55 | func (p *progress) RefreshRate(d time.Duration) *progress { | |
| 42 | 56 | p.interval <- d |
| 43 | 57 | return p |
| 44 | 58 | } |
| 54 | 68 | func (p *progress) AddBar(total int) *Bar { |
| 55 | 69 | bar := NewBar(total) |
| 56 | 70 | // bar.Width = p.Width |
| 57 | p.bars <- bar | |
| 71 | p.op <- &operation{add, bar, nil} | |
| 58 | 72 | return bar |
| 73 | } | |
| 74 | ||
| 75 | func (p *progress) RemoveBar(b *Bar) bool { | |
| 76 | result := make(chan bool) | |
| 77 | p.op <- &operation{remove, b, result} | |
| 78 | return <-result | |
| 59 | 79 | } |
| 60 | 80 | |
| 61 | 81 | // Bypass returns a writer which allows non-buffered data to be written to the underlying output |
| 65 | 85 | |
| 66 | 86 | // Stop stops listening |
| 67 | 87 | func (p *progress) Stop() { |
| 68 | close(p.bars) | |
| 88 | close(p.op) | |
| 69 | 89 | } |
| 70 | 90 | |
| 71 | // server listens for updates and renders the progress bars | |
| 91 | // server monitors underlying channels and renders any progress bars | |
| 72 | 92 | func (p *progress) server() { |
| 73 | t := time.NewTicker(30 * time.Millisecond) | |
| 93 | t := time.NewTicker(refreshRate * time.Millisecond) | |
| 74 | 94 | bars := make([]*Bar, 0) |
| 75 | 95 | p.lw.Out = p.out |
| 76 | 96 | for { |
| 77 | 97 | select { |
| 78 | case bar, ok := <-p.bars: | |
| 98 | case op, ok := <-p.op: | |
| 79 | 99 | if !ok { |
| 80 | 100 | t.Stop() |
| 101 | close(p.interval) | |
| 81 | 102 | return |
| 82 | 103 | } |
| 83 | bars = append(bars, bar) | |
| 104 | switch op.kind { | |
| 105 | case add: | |
| 106 | bars = append(bars, op.bar) | |
| 107 | case remove: | |
| 108 | var ok bool | |
| 109 | for i, b := range bars { | |
| 110 | if b == op.bar { | |
| 111 | bars = append(bars[:i], bars[i+1:]...) | |
| 112 | ok = true | |
| 113 | break | |
| 114 | } | |
| 115 | } | |
| 116 | op.ok <- ok | |
| 117 | } | |
| 84 | 118 | case <-t.C: |
| 85 | for _, bar := range bars { | |
| 86 | fmt.Fprintln(p.lw, bar.String()) | |
| 119 | for _, b := range bars { | |
| 120 | fmt.Fprintln(p.lw, b.String()) | |
| 87 | 121 | } |
| 88 | 122 | p.lw.Flush() |
| 89 | 123 | case d := <-p.interval: |