refactoring
Vladimir Bauer
9 years ago
| 15 | 15 | // after Stop() has been called |
| 16 | 16 | var ErrCallAfterStop = errors.New("method call on stopped Progress instance") |
| 17 | 17 | |
| 18 | type opType uint | |
| 18 | type ( | |
| 19 | // SortType defines sort direction of bar | |
| 20 | SortType uint | |
| 21 | opType uint | |
| 22 | ||
| 23 | operation struct { | |
| 24 | kind opType | |
| 25 | bar *Bar | |
| 26 | result chan bool | |
| 27 | } | |
| 28 | ||
| 29 | indexedBarBuffer struct { | |
| 30 | index int | |
| 31 | buff []byte | |
| 32 | } | |
| 33 | ||
| 34 | indexedBar struct { | |
| 35 | index int | |
| 36 | width int | |
| 37 | bar *Bar | |
| 38 | } | |
| 39 | ) | |
| 19 | 40 | |
| 20 | 41 | const ( |
| 21 | 42 | opBarAdd opType = iota |
| 22 | 43 | opBarRemove |
| 23 | 44 | ) |
| 24 | ||
| 25 | type SortType uint | |
| 26 | 45 | |
| 27 | 46 | const ( |
| 28 | 47 | SortNone SortType = iota |
| 49 | 68 | outChangeReqCh chan io.Writer |
| 50 | 69 | barCountReqCh chan chan int |
| 51 | 70 | done chan struct{} |
| 52 | } | |
| 53 | ||
| 54 | type operation struct { | |
| 55 | kind opType | |
| 56 | bar *Bar | |
| 57 | result chan bool | |
| 58 | 71 | } |
| 59 | 72 | |
| 60 | 73 | // New creates new Progress instance, which will orchestrate bars rendering |
| 110 | 123 | return p |
| 111 | 124 | } |
| 112 | 125 | |
| 113 | // func (p *Progress) WithContext(ctx context.Context) *Progress { | |
| 114 | // if p.BarCount() > 0 { | |
| 115 | // panic("cannot apply ctx after AddBar has been called") | |
| 116 | // } | |
| 117 | // if ctx == nil { | |
| 118 | // panic("nil context") | |
| 119 | // } | |
| 120 | // p.ctx = ctx | |
| 121 | // return p | |
| 122 | // } | |
| 123 | ||
| 124 | 126 | // WithSort sorts the bars, while redering |
| 125 | 127 | func (p *Progress) WithSort(sort SortType) *Progress { |
| 126 | 128 | p.sort = sort |
| 173 | 175 | } |
| 174 | 176 | } |
| 175 | 177 | |
| 178 | func (p *Progress) isDone() bool { | |
| 179 | select { | |
| 180 | case <-p.done: | |
| 181 | return true | |
| 182 | default: | |
| 183 | return false | |
| 184 | } | |
| 185 | } | |
| 186 | ||
| 176 | 187 | // server monitors underlying channels and renders any progress bars |
| 177 | 188 | func (p *Progress) server(cw *cwriter.Writer, t *time.Ticker) { |
| 178 | const numDigesters = 4 | |
| 189 | const numDrawers = 4 | |
| 179 | 190 | bars := make([]*Bar, 0, 4) |
| 180 | 191 | for { |
| 181 | 192 | select { |
| 214 | 225 | } |
| 215 | 226 | |
| 216 | 227 | width, _ := cwriter.TerminalWidth() |
| 217 | ibars := iBarsGen(p.ctx.Done(), bars, width) | |
| 218 | c := make(chan *indexedBarBuffer) | |
| 228 | ibars := iBarsGen(bars, width) | |
| 229 | c := make(chan indexedBarBuffer) | |
| 219 | 230 | var wg sync.WaitGroup |
| 220 | wg.Add(numDigesters) | |
| 221 | for i := 0; i < numDigesters; i++ { | |
| 231 | wg.Add(numDrawers) | |
| 232 | for i := 0; i < numDrawers; i++ { | |
| 222 | 233 | go func() { |
| 223 | drawer(p.ctx.Done(), ibars, c) | |
| 234 | drawer(ibars, c) | |
| 224 | 235 | wg.Done() |
| 225 | 236 | }() |
| 226 | 237 | } |
| 237 | 248 | m[i] = append(m[i], '\n') |
| 238 | 249 | cw.Write(m[i]) |
| 239 | 250 | } |
| 251 | ||
| 240 | 252 | cw.Flush() |
| 241 | go flushed(p.ctx.Done(), bars) | |
| 242 | ||
| 253 | ||
| 254 | go func() { | |
| 255 | for _, b := range bars { | |
| 256 | b.flushDone() | |
| 257 | } | |
| 258 | }() | |
| 243 | 259 | case d := <-p.rrChangeReqCh: |
| 244 | 260 | t.Stop() |
| 245 | 261 | t = time.NewTicker(d) |
| 251 | 267 | } |
| 252 | 268 | } |
| 253 | 269 | |
| 254 | type indexedBarBuffer struct { | |
| 255 | index int | |
| 256 | buff []byte | |
| 257 | } | |
| 258 | ||
| 259 | type indexedBar struct { | |
| 260 | index int | |
| 261 | width int | |
| 262 | bar *Bar | |
| 263 | } | |
| 264 | ||
| 265 | func drawer(done <-chan struct{}, ibars <-chan *indexedBar, c chan<- *indexedBarBuffer) { | |
| 270 | func drawer(ibars <-chan indexedBar, c chan<- indexedBarBuffer) { | |
| 266 | 271 | for b := range ibars { |
| 267 | select { | |
| 268 | case c <- &indexedBarBuffer{b.index, b.bar.bytes(b.width)}: | |
| 269 | case <-done: | |
| 270 | return | |
| 271 | } | |
| 272 | } | |
| 273 | } | |
| 274 | ||
| 275 | func iBarsGen(done <-chan struct{}, bars []*Bar, width int) <-chan *indexedBar { | |
| 276 | ibars := make(chan *indexedBar) | |
| 272 | c <- indexedBarBuffer{b.index, b.bar.bytes(b.width)} | |
| 273 | } | |
| 274 | } | |
| 275 | ||
| 276 | func iBarsGen(bars []*Bar, width int) <-chan indexedBar { | |
| 277 | ibars := make(chan indexedBar) | |
| 277 | 278 | go func() { |
| 278 | 279 | defer close(ibars) |
| 279 | 280 | for i, b := range bars { |
| 280 | select { | |
| 281 | case ibars <- &indexedBar{i, width, b}: | |
| 282 | case <-done: | |
| 283 | return | |
| 284 | } | |
| 281 | ibars <- indexedBar{i, width, b} | |
| 285 | 282 | } |
| 286 | 283 | }() |
| 287 | 284 | return ibars |
| 288 | 285 | } |
| 289 | ||
| 290 | func flushed(done <-chan struct{}, bars []*Bar) { | |
| 291 | for _, b := range bars { | |
| 292 | select { | |
| 293 | case <-done: | |
| 294 | return | |
| 295 | default: | |
| 296 | b.flushDone() | |
| 297 | } | |
| 298 | } | |
| 299 | } | |
| 300 | ||
| 301 | func (p *Progress) isDone() bool { | |
| 302 | select { | |
| 303 | case <-p.done: | |
| 304 | return true | |
| 305 | default: | |
| 306 | return false | |
| 307 | } | |
| 308 | } | |