| 35 | 35 |
leftEnd byte
|
| 36 | 36 |
rightEnd byte
|
| 37 | 37 |
|
| 38 | |
incrCh chan int
|
| 39 | |
redrawReqCh chan chan []byte
|
| 40 | |
progressReqCh chan chan int
|
| 41 | |
decoratorCh chan *decorator
|
| 42 | |
flushedCh chan struct{}
|
| 43 | |
stopCh chan struct{}
|
| 44 | |
done chan struct{}
|
|
38 |
incrCh chan int
|
|
39 |
redrawReqCh chan chan []byte
|
|
40 |
statusReqCh chan chan int
|
|
41 |
decoratorCh chan *decorator
|
|
42 |
flushedCh chan struct{}
|
|
43 |
stopCh chan struct{}
|
|
44 |
done chan struct{}
|
| 45 | 45 |
}
|
| 46 | 46 |
|
| 47 | 47 |
type Statistics struct {
|
|
| 55 | 55 |
|
| 56 | 56 |
func newBar(total, width int, wg *sync.WaitGroup) *Bar {
|
| 57 | 57 |
b := &Bar{
|
| 58 | |
fill: '=',
|
| 59 | |
empty: '-',
|
| 60 | |
tip: '>',
|
| 61 | |
leftEnd: '[',
|
| 62 | |
rightEnd: ']',
|
| 63 | |
alpha: 0.25,
|
| 64 | |
total: total,
|
| 65 | |
width: width,
|
| 66 | |
incrCh: make(chan int),
|
| 67 | |
redrawReqCh: make(chan chan []byte),
|
| 68 | |
progressReqCh: make(chan chan int),
|
| 69 | |
decoratorCh: make(chan *decorator),
|
| 70 | |
flushedCh: make(chan struct{}),
|
| 71 | |
stopCh: make(chan struct{}),
|
| 72 | |
done: make(chan struct{}),
|
|
58 |
fill: '=',
|
|
59 |
empty: '-',
|
|
60 |
tip: '>',
|
|
61 |
leftEnd: '[',
|
|
62 |
rightEnd: ']',
|
|
63 |
alpha: 0.25,
|
|
64 |
total: total,
|
|
65 |
width: width,
|
|
66 |
incrCh: make(chan int),
|
|
67 |
redrawReqCh: make(chan chan []byte),
|
|
68 |
statusReqCh: make(chan chan int),
|
|
69 |
decoratorCh: make(chan *decorator),
|
|
70 |
flushedCh: make(chan struct{}),
|
|
71 |
stopCh: make(chan struct{}),
|
|
72 |
done: make(chan struct{}),
|
| 73 | 73 |
}
|
| 74 | 74 |
go b.server(wg)
|
| 75 | 75 |
return b
|
|
| 139 | 139 |
}
|
| 140 | 140 |
|
| 141 | 141 |
func (b *Bar) Incr(n int) {
|
| 142 | |
if !b.IsCompleted() {
|
|
142 |
if !b.isDone() {
|
| 143 | 143 |
b.incrCh <- n
|
| 144 | 144 |
}
|
| 145 | 145 |
}
|
|
| 151 | 151 |
}
|
| 152 | 152 |
}
|
| 153 | 153 |
|
| 154 | |
func (b *Bar) IsCompleted() bool {
|
| 155 | |
select {
|
| 156 | |
case <-b.done:
|
| 157 | |
return true
|
| 158 | |
default:
|
| 159 | |
return false
|
| 160 | |
}
|
|
154 |
func (b *Bar) InProgress() bool {
|
|
155 |
return !b.isDone()
|
| 161 | 156 |
}
|
| 162 | 157 |
|
| 163 | 158 |
func (b *Bar) PrependFunc(f DecoratorFunc) *Bar {
|
|
| 233 | 228 |
var timeElapsed time.Duration
|
| 234 | 229 |
var appendFuncs []DecoratorFunc
|
| 235 | 230 |
var prependFuncs []DecoratorFunc
|
| 236 | |
var done bool
|
|
231 |
var completed bool
|
| 237 | 232 |
var current int
|
| 238 | 233 |
for {
|
| 239 | 234 |
select {
|
| 240 | 235 |
case i := <-b.incrCh:
|
| 241 | 236 |
n := current + i
|
| 242 | |
// fmt.Fprintf(os.Stderr, "n = %+v\n", n)
|
| 243 | 237 |
if n > b.total {
|
| 244 | 238 |
current = b.total
|
| 245 | |
done = true
|
|
239 |
completed = true
|
| 246 | 240 |
break
|
| 247 | 241 |
}
|
| 248 | 242 |
timeElapsed = time.Since(timeStarted)
|
| 249 | 243 |
tpie = calcTimePerItemEstimate(tpie, blockStartTime, b.alpha, i)
|
| 250 | 244 |
blockStartTime = time.Now()
|
| 251 | 245 |
current = n
|
| 252 | |
if current == b.total && !done {
|
| 253 | |
done = true
|
|
246 |
if current == b.total && !completed {
|
|
247 |
completed = true
|
| 254 | 248 |
}
|
| 255 | 249 |
case d := <-b.decoratorCh:
|
| 256 | 250 |
switch d.kind {
|
|
| 262 | 256 |
case respCh := <-b.redrawReqCh:
|
| 263 | 257 |
stat := &Statistics{b.total, current, timeElapsed, tpie}
|
| 264 | 258 |
respCh <- b.draw(stat, buf, appendFuncs, prependFuncs)
|
| 265 | |
case respCh := <-b.progressReqCh:
|
|
259 |
case respCh := <-b.statusReqCh:
|
| 266 | 260 |
respCh <- int(100 * float64(current) / float64(b.total))
|
| 267 | 261 |
case <-b.flushedCh:
|
| 268 | |
if done && !b.IsCompleted() {
|
| 269 | |
// fmt.Fprintln(os.Stderr, "flushedCh: wg.Done")
|
|
262 |
if completed && !b.isDone() {
|
| 270 | 263 |
close(b.done)
|
| 271 | 264 |
wg.Done()
|
| 272 | 265 |
}
|
| 273 | 266 |
case <-b.stopCh:
|
| 274 | |
// fmt.Fprintln(os.Stderr, "received stop signal")
|
| 275 | |
if !done {
|
| 276 | |
// fmt.Fprintln(os.Stderr, "closing done chan: done = false")
|
|
267 |
if !completed {
|
| 277 | 268 |
close(b.done)
|
| 278 | 269 |
wg.Done()
|
| 279 | 270 |
}
|
|
| 314 | 305 |
return buf
|
| 315 | 306 |
}
|
| 316 | 307 |
|
|
308 |
func (b *Bar) isDone() bool {
|
|
309 |
select {
|
|
310 |
case <-b.done:
|
|
311 |
return true
|
|
312 |
default:
|
|
313 |
return false
|
|
314 |
}
|
|
315 |
}
|
|
316 |
|
|
317 |
func (b *Bar) status() int {
|
|
318 |
respCh := make(chan int)
|
|
319 |
b.statusReqCh <- respCh
|
|
320 |
return <-respCh
|
|
321 |
}
|
|
322 |
|
|
323 |
// SortableBarSlice satisfies sort interface
|
|
324 |
type SortableBarSlice []*Bar
|
|
325 |
|
|
326 |
func (p SortableBarSlice) Len() int { return len(p) }
|
|
327 |
|
|
328 |
func (p SortableBarSlice) Less(i, j int) bool { return p[i].status() < p[j].status() }
|
|
329 |
|
|
330 |
func (p SortableBarSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
|
331 |
|
| 317 | 332 |
func calcTimePerItemEstimate(tpie time.Duration, blockStartTime time.Time, alpha float64, items int) time.Duration {
|
| 318 | 333 |
lastBlockTime := time.Since(blockStartTime)
|
| 319 | 334 |
lastItemEstimate := float64(lastBlockTime) / float64(items)
|
| 320 | 335 |
return time.Duration((alpha * lastItemEstimate) + (1-alpha)*float64(tpie))
|
| 321 | 336 |
}
|
| 322 | |
|
| 323 | |
func (b *Bar) progress() int {
|
| 324 | |
respCh := make(chan int)
|
| 325 | |
b.progressReqCh <- respCh
|
| 326 | |
return <-respCh
|
| 327 | |
}
|
| 328 | |
|
| 329 | |
type SortableBarSlice []*Bar
|
| 330 | |
|
| 331 | |
func (p SortableBarSlice) Len() int { return len(p) }
|
| 332 | |
|
| 333 | |
func (p SortableBarSlice) Less(i, j int) bool { return p[i].progress() < p[j].progress() }
|
| 334 | |
|
| 335 | |
func (p SortableBarSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|