Codebase list golang-github-vbauerster-mpb / 6371244
add OccupiedWidth and TermWidth to decor.Statistics Vladimir Bauer 6 years ago
24 changed file(s) with 460 addition(s) and 428 deletion(s). Raw diff Collapse all Expand all
99 "time"
1010 "unicode/utf8"
1111
12 "github.com/acarl005/stripansi"
1213 "github.com/vbauerster/mpb/v5/decor"
1314 )
14
15 // BarFiller interface.
16 // Bar renders itself by calling BarFiller's Fill method. You can
17 // literally have any bar kind, by implementing this interface and
18 // passing it to the *Progress.Add(...) *Bar method.
19 type BarFiller interface {
20 Fill(w io.Writer, width int, stat *decor.Statistics)
21 }
22
23 // BarFillerFunc is function type adapter to convert function into Filler.
24 type BarFillerFunc func(w io.Writer, width int, stat *decor.Statistics)
25
26 func (f BarFillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
27 f(w, width, stat)
28 }
2915
3016 // Bar represents a progress Bar.
3117 type Bar struct {
5440 recoveredPanic interface{}
5541 }
5642
57 type extFunc func(in io.Reader, tw int, st *decor.Statistics) (out io.Reader, lines int)
43 type extFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int)
5844
5945 type bState struct {
6046 baseF BarFiller
334320 }
335321 }()
336322
337 st := newStatistics(s)
338 frame := s.draw(tw, st)
339 frame, b.extendedLines = s.extender(frame, tw, st)
323 st := newStatistics(tw, s)
324 frame, lines := s.extender(s.draw(st), s.width, st)
325 b.extendedLines = lines
340326
341327 b.toShutdown = s.toComplete && !s.completeFlushed
342328 s.completeFlushed = s.toComplete
344330 }:
345331 case <-b.done:
346332 s := b.cacheState
347 st := newStatistics(s)
348 frame := s.draw(tw, st)
349 frame, b.extendedLines = s.extender(frame, tw, st)
333 st := newStatistics(tw, s)
334 frame, lines := s.extender(s.draw(st), s.width, st)
335 b.extendedLines = lines
350336 b.frameCh <- frame
351337 }
352338 }
397383 }
398384 }
399385
400 func (s *bState) draw(termWidth int, stat *decor.Statistics) io.Reader {
386 func (s *bState) draw(stat decor.Statistics) io.Reader {
401387 for _, d := range s.pDecorators {
402 s.bufP.WriteString(d.Decor(stat))
388 str := d.Decor(stat)
389 stat.OccupiedWidth += utf8.RuneCountInString(stripansi.Strip(str))
390 s.bufP.WriteString(str)
403391 }
404392
405393 for _, d := range s.aDecorators {
406 s.bufA.WriteString(d.Decor(stat))
394 str := d.Decor(stat)
395 stat.OccupiedWidth += utf8.RuneCountInString(stripansi.Strip(str))
396 s.bufA.WriteString(str)
407397 }
408398
409399 s.bufA.WriteByte('\n')
410400
411 prependCount := utf8.RuneCount(s.bufP.Bytes())
412 appendCount := utf8.RuneCount(s.bufA.Bytes()) - 1
413
414 if fitWidth := s.width; termWidth > 1 {
415 if !s.trimSpace {
416 // reserve space for edge spaces
417 termWidth -= 2
418 s.bufB.WriteByte(' ')
419 defer s.bufB.WriteByte(' ')
420 }
421 if prependCount+s.width+appendCount > termWidth {
422 fitWidth = termWidth - prependCount - appendCount
423 }
424 s.filler.Fill(s.bufB, fitWidth, stat)
425 }
401 if !s.trimSpace && stat.TermWidth >= 2 {
402 defer s.bufB.WriteByte(' ')
403 s.bufB.WriteByte(' ')
404 stat.OccupiedWidth += 2
405 }
406
407 s.filler.Fill(s.bufB, s.width, stat)
426408
427409 return io.MultiReader(s.bufP, s.bufB, s.bufA)
428410 }
449431 return table
450432 }
451433
452 func newStatistics(s *bState) *decor.Statistics {
453 return &decor.Statistics{
434 func newStatistics(tw int, s *bState) decor.Statistics {
435 return decor.Statistics{
454436 ID: s.id,
455437 Completed: s.completeFlushed,
456438 Total: s.total,
457439 Current: s.current,
440 TermWidth: tw,
458441 }
459442 }
460443
11
22 import (
33 "io"
4 "unicode/utf8"
54
65 "github.com/vbauerster/mpb/v5/decor"
7 "github.com/vbauerster/mpb/v5/internal"
86 )
97
10 const (
11 rLeft = iota
12 rFill
13 rTip
14 rEmpty
15 rRight
16 rRevTip
17 rRefill
18 )
19
20 // DefaultBarStyle is a string containing 7 runes.
21 // Each rune is a building block of a progress bar.
8 // BarFiller interface.
9 // Bar (without decorators) renders itself by calling BarFiller's Fill method.
2210 //
23 // '1st rune' stands for left boundary rune
11 // `reqWidth` is requested width, which is set via:
12 // func WithWidth(width int) ContainerOption
13 // func BarWidth(width int) BarOption
2414 //
25 // '2nd rune' stands for fill rune
15 // Default implementations can be obtained via:
2616 //
27 // '3rd rune' stands for tip rune
17 // func NewBarFiller(style string, reverse bool) BarFiller
18 // func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller
2819 //
29 // '4th rune' stands for empty rune
30 //
31 // '5th rune' stands for right boundary rune
32 //
33 // '6th rune' stands for reverse tip rune
34 //
35 // '7th rune' stands for refill rune
36 //
37 const DefaultBarStyle string = "[=>-]<+"
38
39 type barFiller struct {
40 format [][]byte
41 tip []byte
42 refill int64
43 reverse bool
44 flush func(w io.Writer, bb [][]byte)
20 type BarFiller interface {
21 Fill(w io.Writer, reqWidth int, stat decor.Statistics)
4522 }
4623
47 // NewBarFiller constucts mpb.BarFiller, to be used with *Progress.Add(...) *Bar method.
48 func NewBarFiller(style string, reverse bool) BarFiller {
49 if style == "" {
50 style = DefaultBarStyle
51 }
52 bf := &barFiller{
53 format: make([][]byte, utf8.RuneCountInString(style)),
54 reverse: reverse,
55 }
56 bf.SetStyle(style)
57 return bf
24 // BarFillerFunc is function type adapter to convert function into BarFiller.
25 type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
26
27 func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
28 f(w, reqWidth, stat)
5829 }
59
60 func (s *barFiller) SetStyle(style string) {
61 if !utf8.ValidString(style) {
62 return
63 }
64 src := make([][]byte, 0, utf8.RuneCountInString(style))
65 for _, r := range style {
66 src = append(src, []byte(string(r)))
67 }
68 copy(s.format, src)
69 s.SetReverse(s.reverse)
70 }
71
72 func (s *barFiller) SetReverse(reverse bool) {
73 if reverse {
74 s.tip = s.format[rRevTip]
75 s.flush = reverseFlush
76 } else {
77 s.tip = s.format[rTip]
78 s.flush = regularFlush
79 }
80 s.reverse = reverse
81 }
82
83 func (s *barFiller) SetRefill(amount int64) {
84 s.refill = amount
85 }
86
87 func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
88 // don't count rLeft and rRight as progress
89 width -= 2
90 if width < 2 {
91 return
92 }
93 w.Write(s.format[rLeft])
94 defer w.Write(s.format[rRight])
95
96 bb := make([][]byte, width)
97
98 cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width))
99
100 for i := 0; i < cwidth; i++ {
101 bb[i] = s.format[rFill]
102 }
103
104 if s.refill > 0 {
105 var rwidth int
106 if s.refill > stat.Current {
107 rwidth = cwidth
108 } else {
109 rwidth = int(internal.PercentageRound(stat.Total, int64(s.refill), width))
110 }
111 for i := 0; i < rwidth; i++ {
112 bb[i] = s.format[rRefill]
113 }
114 }
115
116 if cwidth > 0 && cwidth < width {
117 bb[cwidth-1] = s.tip
118 }
119
120 for i := cwidth; i < width; i++ {
121 bb[i] = s.format[rEmpty]
122 }
123
124 s.flush(w, bb)
125 }
126
127 func regularFlush(w io.Writer, bb [][]byte) {
128 for i := 0; i < len(bb); i++ {
129 w.Write(bb[i])
130 }
131 }
132
133 func reverseFlush(w io.Writer, bb [][]byte) {
134 for i := len(bb) - 1; i >= 0; i-- {
135 w.Write(bb[i])
136 }
137 }
0 package mpb
1
2 import (
3 "io"
4 "unicode/utf8"
5
6 "github.com/vbauerster/mpb/v5/decor"
7 "github.com/vbauerster/mpb/v5/internal"
8 )
9
10 const (
11 rLeft = iota
12 rFill
13 rTip
14 rEmpty
15 rRight
16 rRevTip
17 rRefill
18 )
19
20 // DefaultBarStyle is a string containing 7 runes.
21 // Each rune is a building block of a progress bar.
22 //
23 // '1st rune' stands for left boundary rune
24 //
25 // '2nd rune' stands for fill rune
26 //
27 // '3rd rune' stands for tip rune
28 //
29 // '4th rune' stands for empty rune
30 //
31 // '5th rune' stands for right boundary rune
32 //
33 // '6th rune' stands for reverse tip rune
34 //
35 // '7th rune' stands for refill rune
36 //
37 const DefaultBarStyle string = "[=>-]<+"
38
39 type barFiller struct {
40 format [][]byte
41 tip []byte
42 refill int64
43 reverse bool
44 flush func(w io.Writer, bb [][]byte)
45 }
46
47 // NewBarFiller constucts mpb.BarFiller, to be used with *Progress.Add(...) *Bar method.
48 func NewBarFiller(style string, reverse bool) BarFiller {
49 if style == "" {
50 style = DefaultBarStyle
51 }
52 bf := &barFiller{
53 format: make([][]byte, utf8.RuneCountInString(style)),
54 reverse: reverse,
55 }
56 bf.SetStyle(style)
57 return bf
58 }
59
60 func (s *barFiller) SetStyle(style string) {
61 if !utf8.ValidString(style) {
62 return
63 }
64 src := make([][]byte, 0, utf8.RuneCountInString(style))
65 for _, r := range style {
66 src = append(src, []byte(string(r)))
67 }
68 copy(s.format, src)
69 s.SetReverse(s.reverse)
70 }
71
72 func (s *barFiller) SetReverse(reverse bool) {
73 if reverse {
74 s.tip = s.format[rRevTip]
75 s.flush = reverseFlush
76 } else {
77 s.tip = s.format[rTip]
78 s.flush = regularFlush
79 }
80 s.reverse = reverse
81 }
82
83 func (s *barFiller) SetRefill(amount int64) {
84 s.refill = amount
85 }
86
87 func (s *barFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
88 // auto shrink
89 if stat.OccupiedWidth+width > stat.TermWidth {
90 width = stat.TermWidth - stat.OccupiedWidth
91 }
92 // don't count rLeft and rRight as progress
93 width -= 2
94 if width < 2 {
95 return
96 }
97 w.Write(s.format[rLeft])
98 defer w.Write(s.format[rRight])
99
100 bb := make([][]byte, width)
101
102 cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width))
103
104 for i := 0; i < cwidth; i++ {
105 bb[i] = s.format[rFill]
106 }
107
108 if s.refill > 0 {
109 var rwidth int
110 if s.refill > stat.Current {
111 rwidth = cwidth
112 } else {
113 rwidth = int(internal.PercentageRound(stat.Total, int64(s.refill), width))
114 }
115 for i := 0; i < rwidth; i++ {
116 bb[i] = s.format[rRefill]
117 }
118 }
119
120 if cwidth > 0 && cwidth < width {
121 bb[cwidth-1] = s.tip
122 }
123
124 for i := cwidth; i < width; i++ {
125 bb[i] = s.format[rEmpty]
126 }
127
128 s.flush(w, bb)
129 }
130
131 func regularFlush(w io.Writer, bb [][]byte) {
132 for i := 0; i < len(bb); i++ {
133 w.Write(bb[i])
134 }
135 }
136
137 func reverseFlush(w io.Writer, bb [][]byte) {
138 for i := len(bb) - 1; i >= 0; i-- {
139 w.Write(bb[i])
140 }
141 }
0 package mpb
1
2 import (
3 "io"
4 "strings"
5 "unicode/utf8"
6
7 "github.com/vbauerster/mpb/v5/decor"
8 )
9
10 // SpinnerAlignment enum.
11 type SpinnerAlignment int
12
13 // SpinnerAlignment kinds.
14 const (
15 SpinnerOnLeft SpinnerAlignment = iota
16 SpinnerOnMiddle
17 SpinnerOnRight
18 )
19
20 // DefaultSpinnerStyle is a slice of strings, which makes a spinner.
21 var DefaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
22
23 type spinnerFiller struct {
24 frames []string
25 count uint
26 alignment SpinnerAlignment
27 }
28
29 // NewSpinnerFiller constucts mpb.BarFiller, to be used with *Progress.Add(...) *Bar method.
30 func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller {
31 if len(style) == 0 {
32 style = DefaultSpinnerStyle
33 }
34 filler := &spinnerFiller{
35 frames: style,
36 alignment: alignment,
37 }
38 return filler
39 }
40
41 func (s *spinnerFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
42 // auto shrink
43 if stat.OccupiedWidth+width > stat.TermWidth {
44 width = stat.TermWidth - stat.OccupiedWidth
45 }
46
47 frame := s.frames[s.count%uint(len(s.frames))]
48 frameWidth := utf8.RuneCountInString(frame)
49
50 if width < frameWidth {
51 return
52 }
53
54 switch rest := width - frameWidth; s.alignment {
55 case SpinnerOnLeft:
56 io.WriteString(w, frame+strings.Repeat(" ", rest))
57 case SpinnerOnMiddle:
58 str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
59 io.WriteString(w, str)
60 case SpinnerOnRight:
61 io.WriteString(w, strings.Repeat(" ", rest)+frame)
62 }
63 s.count++
64 }
8282 }
8383
8484 func makeBarFillerOnComplete(filler BarFiller, message string) BarFiller {
85 return BarFillerFunc(func(w io.Writer, width int, st *decor.Statistics) {
85 return BarFillerFunc(func(w io.Writer, width int, st decor.Statistics) {
8686 if st.Completed {
8787 io.WriteString(w, message)
8888 } else {
102102
103103 // BarExtender is an option to extend bar to the next new line, with
104104 // arbitrary output.
105 func BarExtender(extender BarFiller) BarOption {
106 if extender == nil {
107 return nil
108 }
109 return func(s *bState) {
110 s.extender = makeExtFunc(extender)
111 }
112 }
113
114 func makeExtFunc(extender BarFiller) extFunc {
105 func BarExtender(filler BarFiller) BarOption {
106 if filler == nil {
107 return nil
108 }
109 return func(s *bState) {
110 s.extender = makeExtFunc(filler)
111 }
112 }
113
114 func makeExtFunc(filler BarFiller) extFunc {
115115 buf := new(bytes.Buffer)
116 nl := []byte("\n")
117 return func(r io.Reader, tw int, st *decor.Statistics) (io.Reader, int) {
118 extender.Fill(buf, tw, st)
119 return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), nl)
116 return func(r io.Reader, reqWidth int, st decor.Statistics) (io.Reader, int) {
117 filler.Fill(buf, reqWidth, st)
118 return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), []byte("\n"))
120119 }
121120 }
122121
176176 var pCount uint32
177177 bar := p.AddBar(int64(total),
178178 PrependDecorators(panicDecorator(panicMsg,
179 func(st *decor.Statistics) bool {
179 func(st decor.Statistics) bool {
180180 if st.Current >= 42 {
181181 atomic.AddUint32(&pCount, 1)
182182 return true
212212 var pCount uint32
213213 bar := p.AddBar(int64(total),
214214 PrependDecorators(panicDecorator(panicMsg,
215 func(st *decor.Statistics) bool {
215 func(st decor.Statistics) bool {
216216 if st.Completed {
217217 atomic.AddUint32(&pCount, 1)
218218 return true
239239 }
240240 }
241241
242 func panicDecorator(panicMsg string, cond func(*decor.Statistics) bool) decor.Decorator {
242 func panicDecorator(panicMsg string, cond func(decor.Statistics) bool) decor.Decorator {
243243 d := &decorator{
244244 panicMsg: panicMsg,
245245 cond: cond,
251251 type decorator struct {
252252 decor.WC
253253 panicMsg string
254 cond func(*decor.Statistics) bool
255 }
256
257 func (d *decorator) Decor(st *decor.Statistics) string {
254 cond func(decor.Statistics) bool
255 }
256
257 func (d *decorator) Decor(st decor.Statistics) string {
258258 if d.cond(st) {
259259 panic(d.panicMsg)
260260 }
0 package mpb
1
2 import (
3 "io"
4 "io/ioutil"
5 "sync"
6 "time"
7 )
8
9 // ContainerOption is a function option which changes the default
10 // behavior of progress container, if passed to mpb.New(...ContainerOption).
11 type ContainerOption func(*pState)
12
13 // WithWaitGroup provides means to have a single joint point. If
14 // *sync.WaitGroup is provided, you can safely call just p.Wait()
15 // without calling Wait() on provided *sync.WaitGroup. Makes sense
16 // when there are more than one bar to render.
17 func WithWaitGroup(wg *sync.WaitGroup) ContainerOption {
18 return func(s *pState) {
19 s.uwg = wg
20 }
21 }
22
23 // WithWidth sets container width. Default is 80. Bars inherit this
24 // width, as long as no BarWidth is applied.
25 func WithWidth(width int) ContainerOption {
26 return func(s *pState) {
27 if width < 0 {
28 return
29 }
30 s.width = width
31 }
32 }
33
34 // WithRefreshRate overrides default 120ms refresh rate.
35 func WithRefreshRate(d time.Duration) ContainerOption {
36 return func(s *pState) {
37 s.rr = d
38 }
39 }
40
41 // WithManualRefresh disables internal auto refresh time.Ticker.
42 // Refresh will occur upon receive value from provided ch.
43 func WithManualRefresh(ch <-chan time.Time) ContainerOption {
44 return func(s *pState) {
45 s.refreshSrc = ch
46 }
47 }
48
49 // WithRenderDelay delays rendering. By default rendering starts as
50 // soon as bar is added, with this option it's possible to delay
51 // rendering process by keeping provided chan unclosed. In other words
52 // rendering will start as soon as provided chan is closed.
53 func WithRenderDelay(ch <-chan struct{}) ContainerOption {
54 return func(s *pState) {
55 s.renderDelay = ch
56 }
57 }
58
59 // WithShutdownNotifier provided chanel will be closed, after all bars
60 // have been rendered.
61 func WithShutdownNotifier(ch chan struct{}) ContainerOption {
62 return func(s *pState) {
63 s.shutdownNotifier = ch
64 }
65 }
66
67 // WithOutput overrides default os.Stdout output. Setting it to nil
68 // will effectively disable auto refresh rate and discard any output,
69 // useful if you want to disable progress bars with little overhead.
70 func WithOutput(w io.Writer) ContainerOption {
71 return func(s *pState) {
72 if w == nil {
73 s.refreshSrc = make(chan time.Time)
74 s.output = ioutil.Discard
75 return
76 }
77 s.output = w
78 }
79 }
80
81 // WithDebugOutput sets debug output.
82 func WithDebugOutput(w io.Writer) ContainerOption {
83 if w == nil {
84 return nil
85 }
86 return func(s *pState) {
87 s.debugOut = w
88 }
89 }
90
91 // PopCompletedMode will pop and stop rendering completed bars.
92 func PopCompletedMode() ContainerOption {
93 return func(s *pState) {
94 s.popCompleted = true
95 }
96 }
97
98 // ContainerOptOn returns option when condition evaluates to true.
99 func ContainerOptOn(option ContainerOption, condition func() bool) ContainerOption {
100 if condition() {
101 return option
102 }
103 return nil
104 }
00 package decor
11
22 // Any decorator displays text, that can be changed during decorator's
3 // lifetime via provided func call back.
3 // lifetime via provided DecorFunc.
44 //
5 // `f` call back which provides string to display
5 // `fn` DecorFunc callback
66 //
77 // `wcc` optional WC config
88 //
9 func Any(f func(*Statistics) string, wcc ...WC) Decorator {
10 return &any{initWC(wcc...), f}
9 func Any(fn DecorFunc, wcc ...WC) Decorator {
10 return &any{initWC(wcc...), fn}
1111 }
1212
1313 type any struct {
1414 WC
15 f func(*Statistics) string
15 fn DecorFunc
1616 }
1717
18 func (d *any) Decor(s *Statistics) string {
19 return d.FormatMsg(d.f(s))
18 func (d *any) Decor(s Statistics) string {
19 return d.FormatMsg(d.fn(s))
2020 }
4545 return Any(chooseSizeProducer(unit, pairFmt), wcc...)
4646 }
4747
48 func chooseSizeProducer(unit int, format string) func(*Statistics) string {
48 func chooseSizeProducer(unit int, format string) DecorFunc {
4949 if format == "" {
5050 format = "%d / %d"
5151 }
5252 switch unit {
5353 case UnitKiB:
54 return func(s *Statistics) string {
54 return func(s Statistics) string {
5555 return fmt.Sprintf(format, SizeB1024(s.Current), SizeB1024(s.Total))
5656 }
5757 case UnitKB:
58 return func(s *Statistics) string {
58 return func(s Statistics) string {
5959 return fmt.Sprintf(format, SizeB1000(s.Current), SizeB1000(s.Total))
6060 }
6161 default:
62 return func(s *Statistics) string {
62 return func(s Statistics) string {
6363 return fmt.Sprintf(format, s.Current, s.Total)
6464 }
6565 }
4646 // Statistics consists of progress related statistics, that Decorator
4747 // may need.
4848 type Statistics struct {
49 ID int
50 Completed bool
51 Total int64
52 Current int64
49 ID int
50 Completed bool
51 Total int64
52 Current int64
53 TermWidth int
54 OccupiedWidth int
5355 }
5456
5557 // Decorator interface.
56 // Implementors should embed WC type, that way only single method
57 // Decor(*Statistics) needs to be implemented, the rest will be handled
58 // by WC type.
58 // Most of the time there is no need to implement this interface
59 // manually, as decor package already provides a wide range of decorators
60 // which implement this interface. If however built-in decorators don't
61 // meet your needs, you're free to implement your own one by implementing
62 // this particular interface. The easy way to go is to convert a
63 // `DecorFunc` into a `Decorator` interface by using provided
64 // `func Any`(DecorFunc, ...WC) Decorator`.
5965 type Decorator interface {
6066 Configurator
6167 Synchronizer
62 Decor(*Statistics) string
68 Decor(Statistics) string
6369 }
70
71 // DecorFunc func type.
72 // To be used with `func Any`(DecorFunc, ...WC) Decorator`.
73 type DecorFunc func(Statistics) string
6474
6575 // Synchronizer interface.
6676 // All decorators implement this interface implicitly. Its Sync
2424 func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
2525 var msg string
2626 producer := chooseTimeProducer(style)
27 f := func(s *Statistics) string {
27 fn := func(s Statistics) string {
2828 if !s.Completed {
2929 msg = producer(time.Since(startTime))
3030 }
3131 return msg
3232 }
33 return Any(f, wcc...)
33 return Any(fn, wcc...)
3434 }
6262 producer func(time.Duration) string
6363 }
6464
65 func (d *movingAverageETA) Decor(s *Statistics) string {
65 func (d *movingAverageETA) Decor(s Statistics) string {
6666 v := math.Round(d.average.Value())
6767 remaining := time.Duration((s.Total - s.Current) * int64(v))
6868 if d.normalizer != nil {
116116 producer func(time.Duration) string
117117 }
118118
119 func (d *averageETA) Decor(s *Statistics) string {
119 func (d *averageETA) Decor(s Statistics) string {
120120 var remaining time.Duration
121121 if s.Current != 0 {
122122 durPerItem := float64(time.Since(d.startTime)) / float64(s.Current)
33 "fmt"
44 "strings"
55 "unicode/utf8"
6
7 "github.com/acarl005/stripansi"
68 )
79
810 // Merge wraps its decorator argument with intention to sync width
6365 return d.Decorator
6466 }
6567
66 func (d *mergeDecorator) Decor(s *Statistics) string {
68 func (d *mergeDecorator) Decor(s Statistics) string {
6769 msg := d.Decorator.Decor(s)
68 msgLen := utf8.RuneCountInString(msg)
70 msgLen := utf8.RuneCountInString(stripansi.Strip(msg))
6971 if (d.wc.C & DextraSpace) != 0 {
7072 msgLen++
7173 }
100102 WC
101103 }
102104
103 func (d *placeHolderDecorator) Decor(*Statistics) string {
105 func (d *placeHolderDecorator) Decor(Statistics) string {
104106 return ""
105107 }
77 // `wcc` optional WC config
88 //
99 func Name(str string, wcc ...WC) Decorator {
10 return Any(func(*Statistics) string { return str }, wcc...)
10 return Any(func(Statistics) string { return str }, wcc...)
1111 }
2323 msg string
2424 }
2525
26 func (d *onCompleteWrapper) Decor(s *Statistics) string {
26 func (d *onCompleteWrapper) Decor(s Statistics) string {
2727 if s.Completed {
2828 wc := d.GetConf()
2929 return wc.FormatMsg(d.msg)
4949 if format == "" {
5050 format = "% d"
5151 }
52 f := func(s *Statistics) string {
52 f := func(s Statistics) string {
5353 p := internal.Percentage(s.Total, s.Current, 100)
5454 return fmt.Sprintf(format, percentageType(p))
5555 }
7777 msg string
7878 }
7979
80 func (d *movingAverageSpeed) Decor(s *Statistics) string {
80 func (d *movingAverageSpeed) Decor(s Statistics) string {
8181 if !s.Completed {
8282 var speed float64
8383 if v := d.average.Value(); v > 0 {
139139 msg string
140140 }
141141
142 func (d *averageSpeed) Decor(s *Statistics) string {
142 func (d *averageSpeed) Decor(s Statistics) string {
143143 if !s.Completed {
144144 speed := float64(s.Current) / float64(time.Since(d.startTime))
145145 d.msg = d.producer(speed * 1e9)
121121 for _, tc := range cases {
122122 t.Run(tc.name, func(t *testing.T) {
123123 decor := NewAverageSpeed(tc.unit, tc.fmt, time.Now().Add(-tc.elapsed))
124 stat := &Statistics{
124 stat := Statistics{
125125 Current: tc.current,
126126 }
127127 res := decor.Decor(stat)
249249 for _, tc := range cases {
250250 t.Run(tc.name, func(t *testing.T) {
251251 decor := NewAverageSpeed(tc.unit, tc.fmt, time.Now().Add(-tc.elapsed))
252 stat := &Statistics{
252 stat := Statistics{
253253 Current: tc.current,
254254 }
255255 res := decor.Decor(stat)
1111 frames = defaultSpinnerStyle
1212 }
1313 var count uint
14 f := func(s *Statistics) string {
14 f := func(s Statistics) string {
1515 frame := frames[count%uint(len(frames))]
1616 count++
1717 return frame
3131 }
3232
3333 for _, test := range tests {
34 got := test.decorator.Decor(new(decor.Statistics))
34 got := test.decorator.Decor(decor.Statistics{})
3535 if got != test.want {
3636 t.Errorf("Want: %q, Got: %q\n", test.want, got)
3737 }
3939 }
4040
4141 type step struct {
42 stat *decor.Statistics
42 stat decor.Statistics
4343 decorator decor.Decorator
4444 want string
4545 }
4949 testCases := [][]step{
5050 {
5151 {
52 &decor.Statistics{Total: 100, Current: 8},
52 decor.Statistics{Total: 100, Current: 8},
5353 decor.Percentage(decor.WCSyncWidth),
5454 "8 %",
5555 },
5656 {
57 &decor.Statistics{Total: 100, Current: 9},
57 decor.Statistics{Total: 100, Current: 9},
5858 decor.Percentage(decor.WCSyncWidth),
5959 "9 %",
6060 },
6161 },
6262 {
6363 {
64 &decor.Statistics{Total: 100, Current: 9},
64 decor.Statistics{Total: 100, Current: 9},
6565 decor.Percentage(decor.WCSyncWidth),
6666 " 9 %",
6767 },
6868 {
69 &decor.Statistics{Total: 100, Current: 10},
69 decor.Statistics{Total: 100, Current: 10},
7070 decor.Percentage(decor.WCSyncWidth),
7171 "10 %",
7272 },
7373 },
7474 {
7575 {
76 &decor.Statistics{Total: 100, Current: 9},
76 decor.Statistics{Total: 100, Current: 9},
7777 decor.Percentage(decor.WCSyncWidth),
7878 " 9 %",
7979 },
8080 {
81 &decor.Statistics{Total: 100, Current: 100},
81 decor.Statistics{Total: 100, Current: 100},
8282 decor.Percentage(decor.WCSyncWidth),
8383 "100 %",
8484 },
9393 testCases := [][]step{
9494 {
9595 {
96 &decor.Statistics{Total: 100, Current: 8},
96 decor.Statistics{Total: 100, Current: 8},
9797 decor.Percentage(decor.WCSyncWidthR),
9898 "8 %",
9999 },
100100 {
101 &decor.Statistics{Total: 100, Current: 9},
101 decor.Statistics{Total: 100, Current: 9},
102102 decor.Percentage(decor.WCSyncWidthR),
103103 "9 %",
104104 },
105105 },
106106 {
107107 {
108 &decor.Statistics{Total: 100, Current: 9},
108 decor.Statistics{Total: 100, Current: 9},
109109 decor.Percentage(decor.WCSyncWidthR),
110110 "9 % ",
111111 },
112112 {
113 &decor.Statistics{Total: 100, Current: 10},
113 decor.Statistics{Total: 100, Current: 10},
114114 decor.Percentage(decor.WCSyncWidthR),
115115 "10 %",
116116 },
117117 },
118118 {
119119 {
120 &decor.Statistics{Total: 100, Current: 9},
120 decor.Statistics{Total: 100, Current: 9},
121121 decor.Percentage(decor.WCSyncWidthR),
122122 "9 % ",
123123 },
124124 {
125 &decor.Statistics{Total: 100, Current: 100},
125 decor.Statistics{Total: 100, Current: 100},
126126 decor.Percentage(decor.WCSyncWidthR),
127127 "100 %",
128128 },
137137 testCases := [][]step{
138138 {
139139 {
140 &decor.Statistics{Total: 100, Current: 8},
140 decor.Statistics{Total: 100, Current: 8},
141141 decor.Percentage(decor.WCSyncSpace),
142142 " 8 %",
143143 },
144144 {
145 &decor.Statistics{Total: 100, Current: 9},
145 decor.Statistics{Total: 100, Current: 9},
146146 decor.Percentage(decor.WCSyncSpace),
147147 " 9 %",
148148 },
149149 },
150150 {
151151 {
152 &decor.Statistics{Total: 100, Current: 9},
152 decor.Statistics{Total: 100, Current: 9},
153153 decor.Percentage(decor.WCSyncSpace),
154154 " 9 %",
155155 },
156156 {
157 &decor.Statistics{Total: 100, Current: 10},
157 decor.Statistics{Total: 100, Current: 10},
158158 decor.Percentage(decor.WCSyncSpace),
159159 " 10 %",
160160 },
161161 },
162162 {
163163 {
164 &decor.Statistics{Total: 100, Current: 9},
164 decor.Statistics{Total: 100, Current: 9},
165165 decor.Percentage(decor.WCSyncSpace),
166166 " 9 %",
167167 },
168168 {
169 &decor.Statistics{Total: 100, Current: 100},
169 decor.Statistics{Total: 100, Current: 100},
170170 decor.Percentage(decor.WCSyncSpace),
171171 " 100 %",
172172 },
2525 want: "",
2626 },
2727 {
28 name: "t,c,bw{60,20,80}",
28 name: "t,c,bw{60,20,80}trim",
2929 total: 60,
3030 current: 20,
3131 barWidth: 80,
4242 want: "",
4343 },
4444 {
45 name: "t,c,bw{60,20,80}",
45 name: "t,c,bw{60,20,80}trim",
4646 total: 60,
4747 current: 20,
4848 barWidth: 80,
5959 want: " ",
6060 },
6161 {
62 name: "t,c,bw,trim{60,20,80,true}",
62 name: "t,c,bw{60,20,80}trim",
6363 total: 60,
6464 current: 20,
6565 barWidth: 80,
7676 want: " ",
7777 },
7878 {
79 name: "t,c,bw,trim{60,20,80,true}",
79 name: "t,c,bw{60,20,80}trim",
8080 total: 60,
8181 current: 20,
8282 barWidth: 80,
9393 want: " ",
9494 },
9595 {
96 name: "t,c,bw,trim{60,20,80,true}",
96 name: "t,c,bw{60,20,80}trim",
9797 total: 60,
9898 current: 20,
9999 barWidth: 80,
110110 want: " ",
111111 },
112112 {
113 name: "t,c,bw,trim{60,20,80,true}",
113 name: "t,c,bw{60,20,80}trim",
114114 total: 60,
115115 current: 20,
116116 barWidth: 80,
127127 want: " [>-] ",
128128 },
129129 {
130 name: "t,c,bw,trim{60,20,80,true}",
130 name: "t,c,bw{60,20,80}trim",
131131 total: 60,
132132 current: 20,
133133 barWidth: 80,
144144 want: " [>--] ",
145145 },
146146 {
147 name: "t,c,bw,trim{60,20,80,true}",
147 name: "t,c,bw{60,20,80}trim",
148148 total: 60,
149149 current: 20,
150150 barWidth: 80,
161161 want: " [>---] ",
162162 },
163163 {
164 name: "t,c,bw,trim{60,20,80,true}",
164 name: "t,c,bw{60,20,80}trim",
165165 total: 60,
166166 current: 20,
167167 barWidth: 80,
178178 want: " [========================>---------------------------------------------------] ",
179179 },
180180 {
181 name: "t,c,bw,trim{60,20,80,true}",
181 name: "t,c,bw{60,20,80}trim",
182182 total: 60,
183183 current: 20,
184184 barWidth: 80,
195195 want: " [------------------------------------------------------------------------------------------------] ",
196196 },
197197 {
198 name: "t,c,bw,trim{100,100,0,true}",
198 name: "t,c,bw{100,100,0}trim",
199199 total: 100,
200200 current: 0,
201201 barWidth: 100,
210210 want: " [>-----------------------------------------------------------------------------------------------] ",
211211 },
212212 {
213 name: "t,c,bw,trim{100,1,100,true}",
213 name: "t,c,bw{100,1,100}trim",
214214 total: 100,
215215 current: 1,
216216 barWidth: 100,
225225 want: " [===============================>----------------------------------------------------------------] ",
226226 },
227227 {
228 name: "t,c,bw,trim{100,33,100,true}",
228 name: "t,c,bw{100,33,100}trim",
229229 total: 100,
230230 current: 33,
231231 barWidth: 100,
233233 want: "[===============================>------------------------------------------------------------------]",
234234 },
235235 {
236 name: "t,c,bw,trim,rev{100,33,100,true,true}",
236 name: "t,c,bw,rev{100,33,100}trim",
237237 total: 100,
238238 current: 33,
239239 barWidth: 100,
250250 want: " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ",
251251 },
252252 {
253 name: "t,c,bw,rup,trim{100,33,100,33,true}",
253 name: "t,c,bw,rup{100,33,100,33}trim",
254254 total: 100,
255255 current: 33,
256256 barWidth: 100,
259259 want: "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]",
260260 },
261261 {
262 name: "t,c,bw,rup,trim,rev{100,33,100,33,true,true}",
262 name: "t,c,bw,rup,rev{100,33,100,33}trim",
263263 total: 100,
264264 current: 33,
265265 barWidth: 100,
277277 want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ",
278278 },
279279 {
280 name: "t,c,bw,rup,trim{100,40,100,32,true}",
280 name: "t,c,bw,rup{100,40,100,32}trim",
281281 total: 100,
282282 current: 40,
283283 barWidth: 100,
293293 want: " [==============================================================================================>-] ",
294294 },
295295 {
296 name: "t,c,bw,trim{100,99,100,true}",
296 name: "t,c,bw{100,99,100}trim",
297297 total: 100,
298298 current: 99,
299299 barWidth: 100,
308308 want: " [================================================================================================] ",
309309 },
310310 {
311 name: "t,c,bw,trim{100,100,100,true}",
311 name: "t,c,bw{100,100,100}trim",
312312 total: 100,
313313 current: 100,
314314 barWidth: 100,
332332 }
333333 }
334334 tmpBuf.Reset()
335 tmpBuf.ReadFrom(s.draw(termWidth, newStatistics(s)))
335 tmpBuf.ReadFrom(s.draw(newStatistics(termWidth, s)))
336336 by := tmpBuf.Bytes()
337337 by = by[:len(by)-1]
338338
+0
-105
options.go less more
0 package mpb
1
2 import (
3 "io"
4 "io/ioutil"
5 "sync"
6 "time"
7 )
8
9 // ContainerOption is a function option which changes the default
10 // behavior of progress container, if passed to mpb.New(...ContainerOption).
11 type ContainerOption func(*pState)
12
13 // WithWaitGroup provides means to have a single joint point. If
14 // *sync.WaitGroup is provided, you can safely call just p.Wait()
15 // without calling Wait() on provided *sync.WaitGroup. Makes sense
16 // when there are more than one bar to render.
17 func WithWaitGroup(wg *sync.WaitGroup) ContainerOption {
18 return func(s *pState) {
19 s.uwg = wg
20 }
21 }
22
23 // WithWidth sets container width. Default is 80. Bars inherit this
24 // width, as long as no BarWidth is applied.
25 func WithWidth(w int) ContainerOption {
26 return func(s *pState) {
27 if w < 0 {
28 return
29 }
30 s.width = w
31 }
32 }
33
34 // WithRefreshRate overrides default 120ms refresh rate.
35 func WithRefreshRate(d time.Duration) ContainerOption {
36 return func(s *pState) {
37 s.rr = d
38 }
39 }
40
41 // WithManualRefresh disables internal auto refresh time.Ticker.
42 // Refresh will occur upon receive value from provided ch.
43 func WithManualRefresh(ch <-chan time.Time) ContainerOption {
44 return func(s *pState) {
45 s.refreshSrc = ch
46 }
47 }
48
49 // WithRenderDelay delays rendering. By default rendering starts as
50 // soon as bar is added, with this option it's possible to delay
51 // rendering process by keeping provided chan unclosed. In other words
52 // rendering will start as soon as provided chan is closed.
53 func WithRenderDelay(ch <-chan struct{}) ContainerOption {
54 return func(s *pState) {
55 s.renderDelay = ch
56 }
57 }
58
59 // WithShutdownNotifier provided chanel will be closed, after all bars
60 // have been rendered.
61 func WithShutdownNotifier(ch chan struct{}) ContainerOption {
62 return func(s *pState) {
63 s.shutdownNotifier = ch
64 }
65 }
66
67 // WithOutput overrides default os.Stdout output. Setting it to nil
68 // will effectively disable auto refresh rate and discard any output,
69 // useful if you want to disable progress bars with little overhead.
70 func WithOutput(w io.Writer) ContainerOption {
71 return func(s *pState) {
72 if w == nil {
73 s.refreshSrc = make(chan time.Time)
74 s.output = ioutil.Discard
75 return
76 }
77 s.output = w
78 }
79 }
80
81 // WithDebugOutput sets debug output.
82 func WithDebugOutput(w io.Writer) ContainerOption {
83 if w == nil {
84 return nil
85 }
86 return func(s *pState) {
87 s.debugOut = w
88 }
89 }
90
91 // PopCompletedMode will pop and stop rendering completed bars.
92 func PopCompletedMode() ContainerOption {
93 return func(s *pState) {
94 s.popCompleted = true
95 }
96 }
97
98 // ContainerOptOn returns option when condition evaluates to true.
99 func ContainerOptOn(option ContainerOption, condition func() bool) ContainerOption {
100 if condition() {
101 return option
102 }
103 return nil
104 }
348348 id: s.idCount,
349349 width: s.width,
350350 debugOut: s.debugOut,
351 extender: func(r io.Reader, _ int, _ *decor.Statistics) (io.Reader, int) {
351 extender: func(r io.Reader, _ int, _ decor.Statistics) (io.Reader, int) {
352352 return r, 0
353353 },
354354 }
+0
-61
spinner_filler.go less more
0 package mpb
1
2 import (
3 "io"
4 "strings"
5 "unicode/utf8"
6
7 "github.com/vbauerster/mpb/v5/decor"
8 )
9
10 // SpinnerAlignment enum.
11 type SpinnerAlignment int
12
13 // SpinnerAlignment kinds.
14 const (
15 SpinnerOnLeft SpinnerAlignment = iota
16 SpinnerOnMiddle
17 SpinnerOnRight
18 )
19
20 // DefaultSpinnerStyle is a slice of strings, which makes a spinner.
21 var DefaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
22
23 type spinnerFiller struct {
24 frames []string
25 count uint
26 alignment SpinnerAlignment
27 }
28
29 // NewSpinnerFiller constucts mpb.BarFiller, to be used with *Progress.Add(...) *Bar method.
30 func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller {
31 if len(style) == 0 {
32 style = DefaultSpinnerStyle
33 }
34 filler := &spinnerFiller{
35 frames: style,
36 alignment: alignment,
37 }
38 return filler
39 }
40
41 func (s *spinnerFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
42
43 frame := s.frames[s.count%uint(len(s.frames))]
44 frameWidth := utf8.RuneCountInString(frame)
45
46 if width < frameWidth {
47 return
48 }
49
50 switch rest := width - frameWidth; s.alignment {
51 case SpinnerOnLeft:
52 io.WriteString(w, frame+strings.Repeat(" ", rest))
53 case SpinnerOnMiddle:
54 str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
55 io.WriteString(w, str)
56 case SpinnerOnRight:
57 io.WriteString(w, strings.Repeat(" ", rest)+frame)
58 }
59 s.count++
60 }