drop merge decorator
use case is pretty marginal
suppor cost is very high
Vladimir Bauer
2 years ago
| 0 | module github.com/vbauerster/mpb/_examples/merge | |
| 1 | ||
| 2 | go 1.17 | |
| 3 | ||
| 4 | require github.com/vbauerster/mpb/v8 v8.4.0 | |
| 5 | ||
| 6 | require ( | |
| 7 | github.com/VividCortex/ewma v1.2.0 // indirect | |
| 8 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect | |
| 9 | github.com/mattn/go-runewidth v0.0.14 // indirect | |
| 10 | github.com/rivo/uniseg v0.4.4 // indirect | |
| 11 | golang.org/x/sys v0.7.0 // indirect | |
| 12 | ) |
| 0 | package main | |
| 1 | ||
| 2 | import ( | |
| 3 | "math/rand" | |
| 4 | "strings" | |
| 5 | "sync" | |
| 6 | "time" | |
| 7 | ||
| 8 | "github.com/vbauerster/mpb/v8" | |
| 9 | "github.com/vbauerster/mpb/v8/decor" | |
| 10 | ) | |
| 11 | ||
| 12 | func main() { | |
| 13 | var wg sync.WaitGroup | |
| 14 | // passed wg will be accounted at p.Wait() call | |
| 15 | p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(60)) | |
| 16 | total, numBars := 100, 3 | |
| 17 | wg.Add(numBars) | |
| 18 | ||
| 19 | for i := 0; i < numBars; i++ { | |
| 20 | var pdecorators mpb.BarOption | |
| 21 | if i == 0 { | |
| 22 | pdecorators = mpb.PrependDecorators( | |
| 23 | decor.Merge( | |
| 24 | decor.OnComplete( | |
| 25 | newVariadicSpinner(decor.WCSyncSpace), | |
| 26 | "done", | |
| 27 | ), | |
| 28 | decor.WCSyncSpace, // Placeholder | |
| 29 | decor.WCSyncSpace, // Placeholder | |
| 30 | ), | |
| 31 | ) | |
| 32 | } else { | |
| 33 | pdecorators = mpb.PrependDecorators( | |
| 34 | decor.CountersNoUnit("% .1d / % .1d", decor.WCSyncSpace), | |
| 35 | decor.OnComplete(decor.Spinner(nil, decor.WCSyncSpace), "done"), | |
| 36 | decor.OnComplete(decor.Spinner(nil, decor.WCSyncSpace), "done"), | |
| 37 | ) | |
| 38 | } | |
| 39 | bar := p.AddBar(int64(total), | |
| 40 | pdecorators, | |
| 41 | mpb.AppendDecorators( | |
| 42 | decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_GO, 30), "done"), | |
| 43 | ), | |
| 44 | ) | |
| 45 | // simulating some work | |
| 46 | go func() { | |
| 47 | defer wg.Done() | |
| 48 | rng := rand.New(rand.NewSource(time.Now().UnixNano())) | |
| 49 | max := 100 * time.Millisecond | |
| 50 | for i := 0; i < total; i++ { | |
| 51 | // start variable is solely for EWMA calculation | |
| 52 | // EWMA's unit of measure is an iteration's duration | |
| 53 | start := time.Now() | |
| 54 | time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) | |
| 55 | // we need to call EwmaIncrement to fulfill ewma decorator's contract | |
| 56 | bar.EwmaIncrement(time.Since(start)) | |
| 57 | } | |
| 58 | }() | |
| 59 | } | |
| 60 | // wait for passed wg and for all bars to complete and flush | |
| 61 | p.Wait() | |
| 62 | } | |
| 63 | ||
| 64 | func newVariadicSpinner(wc decor.WC) decor.Decorator { | |
| 65 | spinner := decor.Spinner(nil) | |
| 66 | fn := func(s decor.Statistics) string { | |
| 67 | return strings.Repeat(spinner.Decor(s), int(s.Current/3)) | |
| 68 | } | |
| 69 | return decor.Any(fn, wc) | |
| 70 | } |
| 13 | 13 | for _, decorator := range decorators { |
| 14 | 14 | if decorator == nil { |
| 15 | 15 | continue |
| 16 | } | |
| 17 | if d, ok := decorator.(interface { | |
| 18 | PlaceHolders() []decor.Decorator | |
| 19 | }); ok { | |
| 20 | dest = append(dest, d.PlaceHolders()...) | |
| 21 | 16 | } |
| 22 | 17 | dest = append(dest, decorator) |
| 23 | 18 | } |
| 0 | package decor | |
| 1 | ||
| 2 | import ( | |
| 3 | "strings" | |
| 4 | ||
| 5 | "github.com/acarl005/stripansi" | |
| 6 | "github.com/mattn/go-runewidth" | |
| 7 | ) | |
| 8 | ||
| 9 | var ( | |
| 10 | _ Decorator = (*mergeDecorator)(nil) | |
| 11 | _ Wrapper = (*mergeDecorator)(nil) | |
| 12 | _ Decorator = (*placeHolderDecorator)(nil) | |
| 13 | ) | |
| 14 | ||
| 15 | // Merge wraps its decorator argument with intention to sync width | |
| 16 | // with several decorators of another bar. Visual example: | |
| 17 | // | |
| 18 | // +----+--------+---------+--------+ | |
| 19 | // | B1 | MERGE(D, P1, Pn) | | |
| 20 | // +----+--------+---------+--------+ | |
| 21 | // | B2 | D0 | D1 | Dn | | |
| 22 | // +----+--------+---------+--------+ | |
| 23 | func Merge(decorator Decorator, placeholders ...WC) Decorator { | |
| 24 | if decorator == nil { | |
| 25 | return nil | |
| 26 | } | |
| 27 | if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 { | |
| 28 | return decorator | |
| 29 | } | |
| 30 | md := &mergeDecorator{ | |
| 31 | Decorator: decorator, | |
| 32 | wc: decorator.GetConf(), | |
| 33 | placeHolders: make([]Decorator, len(placeholders)), | |
| 34 | } | |
| 35 | decorator.SetConf(WC{}) | |
| 36 | for i, wc := range placeholders { | |
| 37 | if (wc.C & DSyncWidth) == 0 { | |
| 38 | return decorator | |
| 39 | } | |
| 40 | md.placeHolders[i] = &placeHolderDecorator{wc.Init()} | |
| 41 | } | |
| 42 | return md | |
| 43 | } | |
| 44 | ||
| 45 | type mergeDecorator struct { | |
| 46 | Decorator | |
| 47 | wc WC | |
| 48 | placeHolders []Decorator | |
| 49 | } | |
| 50 | ||
| 51 | func (d *mergeDecorator) GetConf() WC { | |
| 52 | return d.wc | |
| 53 | } | |
| 54 | ||
| 55 | func (d *mergeDecorator) SetConf(conf WC) { | |
| 56 | d.wc = conf.Init() | |
| 57 | } | |
| 58 | ||
| 59 | func (d *mergeDecorator) PlaceHolders() []Decorator { | |
| 60 | return d.placeHolders | |
| 61 | } | |
| 62 | ||
| 63 | func (d *mergeDecorator) Sync() (chan int, bool) { | |
| 64 | return d.wc.Sync() | |
| 65 | } | |
| 66 | ||
| 67 | func (d *mergeDecorator) Unwrap() Decorator { | |
| 68 | return d.Decorator | |
| 69 | } | |
| 70 | ||
| 71 | func (d *mergeDecorator) Decor(s Statistics) string { | |
| 72 | msg := d.Decorator.Decor(s) | |
| 73 | pureWidth := runewidth.StringWidth(msg) | |
| 74 | stripWidth := runewidth.StringWidth(stripansi.Strip(msg)) | |
| 75 | cellCount := stripWidth | |
| 76 | if (d.wc.C & DextraSpace) != 0 { | |
| 77 | cellCount++ | |
| 78 | } | |
| 79 | ||
| 80 | total := runewidth.StringWidth(d.placeHolders[0].GetConf().FormatMsg("")) | |
| 81 | pw := (cellCount - total) / len(d.placeHolders) | |
| 82 | rem := (cellCount - total) % len(d.placeHolders) | |
| 83 | ||
| 84 | var diff int | |
| 85 | for i := 1; i < len(d.placeHolders); i++ { | |
| 86 | wc := d.placeHolders[i].GetConf() | |
| 87 | width := pw - diff | |
| 88 | if (wc.C & DextraSpace) != 0 { | |
| 89 | width-- | |
| 90 | if width < 0 { | |
| 91 | width = 0 | |
| 92 | } | |
| 93 | } | |
| 94 | max := runewidth.StringWidth(wc.FormatMsg(strings.Repeat(" ", width))) | |
| 95 | total += max | |
| 96 | diff = max - pw | |
| 97 | } | |
| 98 | ||
| 99 | d.wc.wsync <- pw + rem | |
| 100 | max := <-d.wc.wsync | |
| 101 | return d.wc.fill(msg, max+total+(pureWidth-stripWidth)) | |
| 102 | } | |
| 103 | ||
| 104 | type placeHolderDecorator struct { | |
| 105 | WC | |
| 106 | } | |
| 107 | ||
| 108 | func (d *placeHolderDecorator) Decor(Statistics) string { | |
| 109 | return "" | |
| 110 | } |