package main import ( "strconv" "github.com/logrusorgru/aurora/v4" "github.com/vbauerster/mpb/v8" "github.com/vbauerster/mpb/v8/decor" "src.lwithers.me.uk/go/htpack/cmd/htpacker/packer" ) // mpbProgress returns a new progress object that keeps the user informed via // the visual mpb library. func mpbProgress(ftp packer.FilesToPack) *mpbProg { mp := new(mpbProg) mp.un.max = len(ftp) for _, f := range ftp { if !f.DisableCompression && !f.DisableGzip { mp.gzip.max++ } if !f.DisableCompression && !f.DisableBrotli { mp.brotli.max++ } } barStyle := mpb.BarStyle().Filler("█").Refiller("█").Padding("░").Tip("█").Build() mp.p = mpb.New() mp.un.bar = mp.p.MustAdd(int64(mp.un.max), barStyle, mpb.PrependDecorators(barName("uncompressed")), mpb.AppendDecorators(&mp.un)) if mp.gzip.max > 0 { mp.gzip.bar = mp.p.MustAdd(int64(mp.gzip.max), barStyle, mpb.PrependDecorators(barName("gzip")), mpb.AppendDecorators(&mp.gzip)) } if mp.brotli.max > 0 { mp.brotli.bar = mp.p.MustAdd(int64(mp.brotli.max), barStyle, mpb.PrependDecorators(barName("brotli")), mpb.AppendDecorators(&mp.brotli)) } return mp } func barName(n string) decor.Decorator { return decor.Name(aurora.Magenta(n).String(), decor.WCSyncWidth) } // mpbProg is the mpb progress tracker. It has one bar for each type of // compression, and its methods simply dispatch onto the type-specific // bars. type mpbProg struct { un, gzip, brotli mpbProg1 p *mpb.Progress } func (mp *mpbProg) Count(_ int) { } func (mp *mpbProg) Begin(filename, compression string) { switch compression { case "uncompressed": mp.un.Begin(filename) case "gzip": mp.gzip.Begin(filename) case "brotli": mp.brotli.Begin(filename) default: return } } func (mp *mpbProg) End(filename, compression string) { switch compression { case "uncompressed": mp.un.End(filename) case "gzip": mp.gzip.End(filename) case "brotli": mp.brotli.End(filename) default: return } } func (mp *mpbProg) Complete() { mp.un.Complete() mp.gzip.Complete() mp.brotli.Complete() mp.p.Wait() } // mpbProg1 is a type-specific progress bar. In addition to holding state and // methods for updating the bar, it also implements decor.Decor. type mpbProg1 struct { max int // number of items we expect done int // number of items completed cur []string // list of currently-packing filenames bar *mpb.Bar // embedding this type lets us implement decor.Decor decor.WC } func (mp1 *mpbProg1) Decor(stat decor.Statistics) (string, int) { if stat.Completed { return "", 0 } switch len(mp1.cur) { case 0: return aurora.Gray(8, "(idle)").String(), 6 case 1: return aurora.Blue(mp1.cur[0]).String(), len(mp1.cur[0]) default: s := mp1.cur[0] d := strconv.Itoa(len(mp1.cur) - 1) return aurora.Sprintf(aurora.Green("%s + %s more"), s, d), len(s) + len(d) + 8 } } func (mp1 *mpbProg1) Begin(filename string) { mp1.cur = append(mp1.cur, filename) } func (mp1 *mpbProg1) End(filename string) { for i, v := range mp1.cur { if v == filename { mp1.cur[i] = mp1.cur[len(mp1.cur)-1] mp1.cur = mp1.cur[:len(mp1.cur)-1] break } } mp1.done++ if mp1.bar != nil { mp1.bar.SetCurrent(int64(mp1.done)) } } func (mp1 *mpbProg1) Complete() { if mp1.bar != nil { mp1.bar.SetTotal(int64(mp1.max), true) } }