| 3 | 3 |
"io"
|
| 4 | 4 |
"sync"
|
| 5 | 5 |
"time"
|
|
6 |
"unicode/utf8"
|
| 6 | 7 |
)
|
| 7 | 8 |
|
| 8 | 9 |
// Bar represents a progress Bar
|
|
| 20 | 21 |
lastStatus int
|
| 21 | 22 |
|
| 22 | 23 |
incrCh chan int
|
| 23 | |
redrawReqCh chan chan []byte
|
|
24 |
redrawReqCh chan *redrawRequest
|
| 24 | 25 |
currentReqCh chan chan int
|
| 25 | 26 |
statusReqCh chan chan int
|
| 26 | 27 |
decoratorCh chan *decorator
|
|
| 32 | 33 |
// Statistics represents statistics of the progress bar
|
| 33 | 34 |
// instance of this, sent to DecoratorFunc, as param
|
| 34 | 35 |
type Statistics struct {
|
| 35 | |
Total, Current int
|
|
36 |
Total, Current, TermWidth int
|
| 36 | 37 |
TimeElapsed, TimePerItemEstimate time.Duration
|
| 37 | 38 |
}
|
| 38 | 39 |
|
|
| 51 | 52 |
width: width,
|
| 52 | 53 |
|
| 53 | 54 |
incrCh: make(chan int),
|
| 54 | |
redrawReqCh: make(chan chan []byte),
|
|
55 |
redrawReqCh: make(chan *redrawRequest),
|
| 55 | 56 |
currentReqCh: make(chan chan int),
|
| 56 | 57 |
statusReqCh: make(chan chan int),
|
| 57 | 58 |
decoratorCh: make(chan *decorator),
|
|
| 163 | 164 |
}
|
| 164 | 165 |
|
| 165 | 166 |
// String returns the string representation of the bar
|
| 166 | |
func (b *Bar) String() string {
|
|
167 |
// func (b *Bar) String() string {
|
|
168 |
// if b.isDone() {
|
|
169 |
// return string(b.lastFrame)
|
|
170 |
// }
|
|
171 |
// respCh := make(chan []byte)
|
|
172 |
// b.redrawReqCh <- respCh
|
|
173 |
// return string(<-respCh)
|
|
174 |
// }
|
|
175 |
|
|
176 |
type redrawRequest struct {
|
|
177 |
width int
|
|
178 |
respCh chan []byte
|
|
179 |
}
|
|
180 |
|
|
181 |
func (b *Bar) Bytes(width int) []byte {
|
| 167 | 182 |
if b.isDone() {
|
| 168 | |
return string(b.lastFrame)
|
|
183 |
return b.lastFrame
|
| 169 | 184 |
}
|
| 170 | 185 |
respCh := make(chan []byte)
|
| 171 | |
b.redrawReqCh <- respCh
|
| 172 | |
return string(<-respCh)
|
|
186 |
b.redrawReqCh <- &redrawRequest{width, respCh}
|
|
187 |
return <-respCh
|
| 173 | 188 |
}
|
| 174 | 189 |
|
| 175 | 190 |
func (b *Bar) server(wg *sync.WaitGroup, total int) {
|
| 176 | 191 |
timeStarted := time.Now()
|
| 177 | 192 |
blockStartTime := timeStarted
|
| 178 | |
buf := make([]byte, b.width, b.width+24)
|
|
193 |
// buf := make([]byte, 0, b.width+32)
|
| 179 | 194 |
var tpie time.Duration
|
| 180 | 195 |
var timeElapsed time.Duration
|
| 181 | 196 |
var appendFuncs []DecoratorFunc
|
| 182 | 197 |
var prependFuncs []DecoratorFunc
|
| 183 | 198 |
var completed bool
|
| 184 | 199 |
var current int
|
|
200 |
var termWidth int
|
| 185 | 201 |
for {
|
| 186 | 202 |
select {
|
| 187 | 203 |
case i := <-b.incrCh:
|
|
| 207 | 223 |
}
|
| 208 | 224 |
case respCh := <-b.currentReqCh:
|
| 209 | 225 |
respCh <- current
|
| 210 | |
case respCh := <-b.redrawReqCh:
|
| 211 | |
stat := &Statistics{total, current, timeElapsed, tpie}
|
| 212 | |
respCh <- b.draw(stat, buf, appendFuncs, prependFuncs)
|
|
226 |
case r := <-b.redrawReqCh:
|
|
227 |
termWidth = r.width
|
|
228 |
stat := &Statistics{total, current, termWidth, timeElapsed, tpie}
|
|
229 |
r.respCh <- b.draw(stat, appendFuncs, prependFuncs)
|
| 213 | 230 |
case respCh := <-b.statusReqCh:
|
| 214 | 231 |
respCh <- percentage(total, current, 100)
|
| 215 | 232 |
case <-b.flushedCh:
|
| 216 | 233 |
if completed && !b.isDone() {
|
| 217 | |
stat := &Statistics{total, current, timeElapsed, tpie}
|
| 218 | |
b.lastFrame = b.draw(stat, buf, appendFuncs, prependFuncs)
|
|
234 |
stat := &Statistics{total, current, termWidth, timeElapsed, tpie}
|
|
235 |
b.lastFrame = b.draw(stat, appendFuncs, prependFuncs)
|
| 219 | 236 |
b.lastStatus = percentage(total, current, 100)
|
| 220 | 237 |
close(b.done)
|
| 221 | 238 |
wg.Done()
|
|
| 231 | 248 |
}
|
| 232 | 249 |
}
|
| 233 | 250 |
|
| 234 | |
func (b *Bar) draw(stat *Statistics, buf []byte, appendFuncs, prependFuncs []DecoratorFunc) []byte {
|
| 235 | |
completedWidth := percentage(stat.Total, stat.Current, b.width)
|
| 236 | |
|
| 237 | |
for i := 0; i < completedWidth; i++ {
|
|
251 |
func (b *Bar) draw(stat *Statistics, appendFuncs, prependFuncs []DecoratorFunc) []byte {
|
|
252 |
|
|
253 |
buf := make([]byte, 0, stat.TermWidth)
|
|
254 |
|
|
255 |
barBlock := b.fillBar(stat.Total, stat.Current, b.width)
|
|
256 |
|
|
257 |
// render append functions to the right of the bar
|
|
258 |
var appendBlock []byte
|
|
259 |
for _, f := range appendFuncs {
|
|
260 |
appendBlock = append(appendBlock, []byte(f(stat))...)
|
|
261 |
}
|
|
262 |
|
|
263 |
// render prepend functions to the left of the bar
|
|
264 |
var prependBlock []byte
|
|
265 |
for _, f := range prependFuncs {
|
|
266 |
prependBlock = append(prependBlock, []byte(f(stat))...)
|
|
267 |
}
|
|
268 |
|
|
269 |
prependCount := utf8.RuneCount(prependBlock)
|
|
270 |
barCount := utf8.RuneCount(barBlock)
|
|
271 |
appendCount := utf8.RuneCount(appendBlock)
|
|
272 |
totalCount := prependCount + barCount + appendCount
|
|
273 |
|
|
274 |
if totalCount >= stat.TermWidth {
|
|
275 |
newWidth := stat.TermWidth - prependCount - appendCount
|
|
276 |
barBlock = b.fillBar(stat.Total, stat.Current, newWidth-1)
|
|
277 |
}
|
|
278 |
|
|
279 |
for _, block := range [...][]byte{prependBlock, barBlock, appendBlock} {
|
|
280 |
buf = append(buf, block...)
|
|
281 |
}
|
|
282 |
|
|
283 |
return buf
|
|
284 |
}
|
|
285 |
|
|
286 |
func (b *Bar) fillBar(total, current, width int) []byte {
|
|
287 |
if width < 2 {
|
|
288 |
return []byte{b.leftEnd, b.rightEnd}
|
|
289 |
}
|
|
290 |
buf := make([]byte, width)
|
|
291 |
completedWidth := percentage(total, current, width)
|
|
292 |
|
|
293 |
for i := 1; i < completedWidth; i++ {
|
| 238 | 294 |
buf[i] = b.fill
|
| 239 | 295 |
}
|
| 240 | |
for i := completedWidth; i < b.width; i++ {
|
|
296 |
for i := completedWidth; i < width-1; i++ {
|
| 241 | 297 |
buf[i] = b.empty
|
| 242 | 298 |
}
|
| 243 | 299 |
// set tip bit
|
| 244 | |
if completedWidth > 0 && completedWidth < b.width {
|
|
300 |
if completedWidth > 0 && completedWidth < width {
|
| 245 | 301 |
buf[completedWidth-1] = b.tip
|
| 246 | 302 |
}
|
| 247 | |
|
| 248 | 303 |
// set left and right ends bits
|
| 249 | |
buf[0], buf[len(buf)-1] = b.leftEnd, b.rightEnd
|
| 250 | |
|
| 251 | |
// render append functions to the right of the bar
|
| 252 | |
for _, f := range appendFuncs {
|
| 253 | |
buf = append(buf, ' ')
|
| 254 | |
buf = append(buf, []byte(f(stat))...)
|
| 255 | |
}
|
| 256 | |
|
| 257 | |
// render prepend functions to the left of the bar
|
| 258 | |
for _, f := range prependFuncs {
|
| 259 | |
args := []byte(f(stat))
|
| 260 | |
args = append(args, ' ')
|
| 261 | |
buf = append(args, buf...)
|
| 262 | |
}
|
|
304 |
buf[0], buf[width-1] = b.leftEnd, b.rightEnd
|
| 263 | 305 |
return buf
|
| 264 | 306 |
}
|
| 265 | 307 |
|