| 31 | 31 |
|
| 32 | 32 |
// Bar represents a progress Bar.
|
| 33 | 33 |
type Bar struct {
|
| 34 | |
priority int
|
| 35 | |
index int
|
| 36 | |
|
| 37 | |
cacheState *bState
|
| 38 | |
operateState chan func(*bState)
|
| 39 | |
bFrameCh chan *bFrame
|
| 40 | |
syncTableCh chan [][]chan int
|
| 41 | |
completed chan bool
|
| 42 | |
forceRefresh chan<- time.Time
|
| 43 | |
|
| 44 | |
// done is closed by Bar's goroutine, after cacheState is written
|
|
34 |
priority int // used by heap
|
|
35 |
index int // used by heap
|
|
36 |
|
|
37 |
extendedLines int
|
|
38 |
toShutdown bool
|
|
39 |
dropOnComplete bool
|
|
40 |
operateState chan func(*bState)
|
|
41 |
frameCh chan io.Reader
|
|
42 |
syncTableCh chan [][]chan int
|
|
43 |
completed chan bool
|
|
44 |
forceRefresh chan<- time.Time
|
|
45 |
|
|
46 |
// shutdown is closed when bar completed and flushed
|
|
47 |
shutdown chan struct{}
|
|
48 |
// done is closed after cacheState is written
|
| 45 | 49 |
done chan struct{}
|
| 46 | |
// shutdown is closed from master Progress goroutine only
|
| 47 | |
shutdown chan struct{}
|
|
50 |
// cacheState is populated, right after close(shutdown)
|
|
51 |
cacheState *bState
|
| 48 | 52 |
|
| 49 | 53 |
arbitraryCurrent struct {
|
| 50 | 54 |
sync.Mutex
|
|
| 54 | 58 |
dlogger *log.Logger
|
| 55 | 59 |
}
|
| 56 | 60 |
|
| 57 | |
type (
|
| 58 | |
bState struct {
|
| 59 | |
filler Filler
|
| 60 | |
extender Filler
|
| 61 | |
id int
|
| 62 | |
width int
|
| 63 | |
total int64
|
| 64 | |
current int64
|
| 65 | |
trimSpace bool
|
| 66 | |
toComplete bool
|
| 67 | |
removeOnComplete bool
|
| 68 | |
barClearOnComplete bool
|
| 69 | |
completeFlushed bool
|
| 70 | |
aDecorators []decor.Decorator
|
| 71 | |
pDecorators []decor.Decorator
|
| 72 | |
amountReceivers []decor.AmountReceiver
|
| 73 | |
shutdownListeners []decor.ShutdownListener
|
| 74 | |
bufP, bufB, bufA *bytes.Buffer
|
| 75 | |
bufE *bytes.Buffer
|
| 76 | |
panicMsg string
|
| 77 | |
|
| 78 | |
// priority overrides *Bar's priority, if set
|
| 79 | |
priority int
|
| 80 | |
// runningBar is a key for *pState.parkedBars
|
| 81 | |
runningBar *Bar
|
| 82 | |
}
|
| 83 | |
bFrame struct {
|
| 84 | |
rd io.Reader
|
| 85 | |
extendedLines int
|
| 86 | |
toShutdown bool
|
| 87 | |
removeOnComplete bool
|
| 88 | |
}
|
| 89 | |
)
|
| 90 | |
|
| 91 | |
func newBar(
|
| 92 | |
ctx context.Context,
|
| 93 | |
wg *sync.WaitGroup,
|
| 94 | |
forceRefresh chan<- time.Time,
|
| 95 | |
bs *bState,
|
| 96 | |
dlogger *log.Logger,
|
| 97 | |
) *Bar {
|
|
61 |
type bState struct {
|
|
62 |
filler Filler
|
|
63 |
extender Filler
|
|
64 |
id int
|
|
65 |
width int
|
|
66 |
total int64
|
|
67 |
current int64
|
|
68 |
trimSpace bool
|
|
69 |
toComplete bool
|
|
70 |
completeFlushed bool
|
|
71 |
noBufBOnComplete bool
|
|
72 |
aDecorators []decor.Decorator
|
|
73 |
pDecorators []decor.Decorator
|
|
74 |
amountReceivers []decor.AmountReceiver
|
|
75 |
shutdownListeners []decor.ShutdownListener
|
|
76 |
bufP, bufB, bufA *bytes.Buffer
|
|
77 |
bufE *bytes.Buffer
|
|
78 |
panicMsg string
|
|
79 |
|
|
80 |
// priority overrides *Bar's priority, if set
|
|
81 |
priority int
|
|
82 |
// dropOnComplete propagates to *Bar
|
|
83 |
dropOnComplete bool
|
|
84 |
// runningBar is a key for *pState.parkedBars
|
|
85 |
runningBar *Bar
|
|
86 |
}
|
|
87 |
|
|
88 |
func newBar(ctx context.Context, wg *sync.WaitGroup, bs *bState) *Bar {
|
| 98 | 89 |
|
| 99 | 90 |
bs.bufP = bytes.NewBuffer(make([]byte, 0, bs.width))
|
| 100 | 91 |
bs.bufB = bytes.NewBuffer(make([]byte, 0, bs.width))
|
|
| 104 | 95 |
}
|
| 105 | 96 |
|
| 106 | 97 |
bar := &Bar{
|
| 107 | |
priority: bs.priority,
|
| 108 | |
operateState: make(chan func(*bState)),
|
| 109 | |
bFrameCh: make(chan *bFrame, 1),
|
| 110 | |
syncTableCh: make(chan [][]chan int),
|
| 111 | |
completed: make(chan bool),
|
| 112 | |
done: make(chan struct{}),
|
| 113 | |
shutdown: make(chan struct{}),
|
| 114 | |
forceRefresh: forceRefresh,
|
| 115 | |
dlogger: dlogger,
|
|
98 |
priority: bs.priority,
|
|
99 |
dropOnComplete: bs.dropOnComplete,
|
|
100 |
operateState: make(chan func(*bState)),
|
|
101 |
frameCh: make(chan io.Reader, 1),
|
|
102 |
syncTableCh: make(chan [][]chan int),
|
|
103 |
completed: make(chan bool),
|
|
104 |
done: make(chan struct{}),
|
|
105 |
shutdown: make(chan struct{}),
|
| 116 | 106 |
}
|
| 117 | 107 |
|
| 118 | 108 |
go bar.serve(ctx, wg, bs)
|
|
| 282 | 272 |
defer func() {
|
| 283 | 273 |
// recovering if user defined decorator panics for example
|
| 284 | 274 |
if p := recover(); p != nil {
|
|
275 |
b.dlogger.Println(p)
|
| 285 | 276 |
s.panicMsg = fmt.Sprintf("panic: %v", p)
|
| 286 | |
b.dlogger.Println(s.panicMsg)
|
| 287 | |
b.bFrameCh <- &bFrame{
|
| 288 | |
rd: strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg)),
|
| 289 | |
toShutdown: true,
|
| 290 | |
}
|
|
277 |
close(b.shutdown)
|
|
278 |
b.frameCh <- s.drawPanic(tw)
|
| 291 | 279 |
}
|
| 292 | 280 |
}()
|
| 293 | |
frame := &bFrame{
|
| 294 | |
rd: s.draw(tw),
|
| 295 | |
toShutdown: s.toComplete && !s.completeFlushed,
|
| 296 | |
removeOnComplete: s.removeOnComplete,
|
| 297 | |
}
|
|
281 |
|
|
282 |
frame := s.draw(tw)
|
|
283 |
b.toShutdown = s.toComplete && !s.completeFlushed
|
|
284 |
s.completeFlushed = s.toComplete
|
|
285 |
|
| 298 | 286 |
if s.extender != nil {
|
| 299 | 287 |
s.extender.Fill(s.bufE, tw, newStatistics(s))
|
| 300 | |
frame.extendedLines = countLines(s.bufE.Bytes())
|
| 301 | |
frame.rd = io.MultiReader(frame.rd, s.bufE)
|
| 302 | |
}
|
| 303 | |
b.bFrameCh <- frame
|
| 304 | |
s.completeFlushed = s.toComplete
|
|
288 |
b.extendedLines = countLines(s.bufE.Bytes())
|
|
289 |
frame = io.MultiReader(frame, s.bufE)
|
|
290 |
}
|
|
291 |
b.frameCh <- frame
|
| 305 | 292 |
}:
|
| 306 | 293 |
case <-b.done:
|
| 307 | 294 |
s := b.cacheState
|
| 308 | |
frame := &bFrame{
|
| 309 | |
rd: s.draw(tw),
|
| 310 | |
}
|
|
295 |
if s.panicMsg != "" {
|
|
296 |
b.frameCh <- s.drawPanic(tw)
|
|
297 |
return
|
|
298 |
}
|
|
299 |
frame := s.draw(tw)
|
| 311 | 300 |
if s.extender != nil {
|
| 312 | 301 |
s.extender.Fill(s.bufE, tw, newStatistics(s))
|
| 313 | |
frame.extendedLines = countLines(s.bufE.Bytes())
|
| 314 | |
frame.rd = io.MultiReader(frame.rd, s.bufE)
|
| 315 | |
}
|
| 316 | |
b.bFrameCh <- frame
|
| 317 | |
}
|
|
302 |
b.extendedLines = countLines(s.bufE.Bytes())
|
|
303 |
frame = io.MultiReader(frame, s.bufE)
|
|
304 |
}
|
|
305 |
b.frameCh <- frame
|
|
306 |
}
|
|
307 |
}
|
|
308 |
|
|
309 |
func (s *bState) drawPanic(termWidth int) io.Reader {
|
|
310 |
return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
|
| 318 | 311 |
}
|
| 319 | 312 |
|
| 320 | 313 |
func (s *bState) draw(termWidth int) io.Reader {
|
| 321 | |
if s.panicMsg != "" {
|
| 322 | |
return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
|
| 323 | |
}
|
| 324 | 314 |
|
| 325 | 315 |
stat := newStatistics(s)
|
| 326 | 316 |
|
|
| 332 | 322 |
s.bufA.WriteString(d.Decor(stat))
|
| 333 | 323 |
}
|
| 334 | 324 |
|
| 335 | |
if s.barClearOnComplete && s.completeFlushed {
|
|
325 |
if s.noBufBOnComplete && s.completeFlushed {
|
| 336 | 326 |
s.bufA.WriteByte('\n')
|
| 337 | 327 |
return io.MultiReader(s.bufP, s.bufA)
|
| 338 | 328 |
}
|