diff --git a/bar_filler.go b/bar_filler.go index a6eb23d..7a4b858 100644 --- a/bar_filler.go +++ b/bar_filler.go @@ -64,7 +64,7 @@ bb := make([][]byte, width) - cwidth := int(internal.Percentage(stat.Total, stat.Current, int64(width))) + cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width)) for i := 0; i < cwidth; i++ { bb[i] = s.format[rFill] @@ -75,7 +75,7 @@ if s.refillAmount > stat.Current { rwidth = cwidth } else { - rwidth = int(internal.Percentage(stat.Total, int64(s.refillAmount), int64(width))) + rwidth = int(internal.PercentageRound(stat.Total, int64(s.refillAmount), width)) } for i := 0; i < rwidth; i++ { bb[i] = s.format[rRefill] diff --git a/decor/percentage.go b/decor/percentage.go index 23938d6..8930f50 100644 --- a/decor/percentage.go +++ b/decor/percentage.go @@ -2,27 +2,76 @@ import ( "fmt" + "io" + "strconv" + "strings" "github.com/vbauerster/mpb/v4/internal" ) + +type PercentageType float64 + +func (s PercentageType) Format(st fmt.State, verb rune) { + var prec int + switch verb { + case 'd': + case 's': + prec = -1 + default: + if p, ok := st.Precision(); ok { + prec = p + } else { + prec = 6 + } + } + + res := strconv.FormatFloat(float64(s), 'f', prec, 64) + + if st.Flag(' ') { + res += " " + } + res += "%" + + if w, ok := st.Width(); ok { + if len(res) < w { + pad := strings.Repeat(" ", w-len(res)) + if st.Flag('-') { + res += pad + } else { + res = pad + res + } + } + } + + io.WriteString(st, res) +} // Percentage returns percentage decorator. // // `wcc` optional WC config func Percentage(wcc ...WC) Decorator { + return PercentageFmt("% d", wcc...) +} + +// PercentageFmt percentage decorator with custom fmt. +// "%.1f" = "1.0%" or "% .1f" = "1.0 %" +// "%d" = "1%" or "% d" = "1 %" +func PercentageFmt(fmt string, wcc ...WC) Decorator { var wc WC for _, widthConf := range wcc { wc = widthConf } wc.Init() d := &percentageDecorator{ - WC: wc, + WC: wc, + fmt: fmt, } return d } type percentageDecorator struct { WC + fmt string completeMsg *string } @@ -30,8 +79,8 @@ if st.Completed && d.completeMsg != nil { return d.FormatMsg(*d.completeMsg) } - str := fmt.Sprintf("%d %%", internal.Percentage(st.Total, st.Current, 100)) - return d.FormatMsg(str) + p := internal.Percentage(st.Total, st.Current, 100) + return d.FormatMsg(fmt.Sprintf(d.fmt, PercentageType(p))) } func (d *percentageDecorator) OnCompleteMessage(msg string) { diff --git a/decor/speed.go b/decor/speed.go index e8839bd..1f24ebc 100644 --- a/decor/speed.go +++ b/decor/speed.go @@ -14,17 +14,17 @@ type SpeedKiB float64 func (s SpeedKiB) Format(st fmt.State, verb rune) { - prec, ok := st.Precision() - - if verb == 'd' || !ok { - prec = 0 - } - if verb == 'f' && !ok { - prec = 6 - } - // retain old beahavior if s verb used - if verb == 's' { - prec = 1 + var prec int + switch verb { + case 'd': + case 's': + prec = -1 + default: + if p, ok := st.Precision(); ok { + prec = p + } else { + prec = 6 + } } var res, unit string @@ -54,7 +54,7 @@ if w, ok := st.Width(); ok { if len(res) < w { pad := strings.Repeat(" ", w-len(res)) - if st.Flag(int('-')) { + if st.Flag('-') { res += pad } else { res = pad + res @@ -68,17 +68,17 @@ type SpeedKB float64 func (s SpeedKB) Format(st fmt.State, verb rune) { - prec, ok := st.Precision() - - if verb == 'd' || !ok { - prec = 0 - } - if verb == 'f' && !ok { - prec = 6 - } - // retain old beahavior if s verb used - if verb == 's' { - prec = 1 + var prec int + switch verb { + case 'd': + case 's': + prec = -1 + default: + if p, ok := st.Precision(); ok { + prec = p + } else { + prec = 6 + } } var res, unit string diff --git a/decor/speed_test.go b/decor/speed_test.go index a95252d..82c7c6e 100644 --- a/decor/speed_test.go +++ b/decor/speed_test.go @@ -49,19 +49,19 @@ "1024 %f": {1024, "%f", "1.000000KiB/s"}, "1024 %d": {1024, "%d", "1KiB/s"}, "1024 %.1f": {1024, "%.1f", "1.0KiB/s"}, - "1024 %s": {1024, "%s", "1.0KiB/s"}, + "1024 %s": {1024, "%s", "1KiB/s"}, "3*MiB/s+140KiB/s %f": {3*MiB + 140*KiB, "%f", "3.136719MiB/s"}, "3*MiB/s+140KiB/s %d": {3*MiB + 140*KiB, "%d", "3MiB/s"}, "3*MiB/s+140KiB/s %.1f": {3*MiB + 140*KiB, "%.1f", "3.1MiB/s"}, - "3*MiB/s+140KiB/s %s": {3*MiB + 140*KiB, "%s", "3.1MiB/s"}, + "3*MiB/s+140KiB/s %s": {3*MiB + 140*KiB, "%s", "3.13671875MiB/s"}, "2*GiB/s %f": {2 * GiB, "%f", "2.000000GiB/s"}, "2*GiB/s %d": {2 * GiB, "%d", "2GiB/s"}, "2*GiB/s %.1f": {2 * GiB, "%.1f", "2.0GiB/s"}, - "2*GiB/s %s": {2 * GiB, "%s", "2.0GiB/s"}, + "2*GiB/s %s": {2 * GiB, "%s", "2GiB/s"}, "4*TiB/s %f": {4 * TiB, "%f", "4.000000TiB/s"}, "4*TiB/s %d": {4 * TiB, "%d", "4TiB/s"}, "4*TiB/s %.1f": {4 * TiB, "%.1f", "4.0TiB/s"}, - "4*TiB/s %s": {4 * TiB, "%s", "4.0TiB/s"}, + "4*TiB/s %s": {4 * TiB, "%s", "4TiB/s"}, } for name, tc := range cases { t.Run(name, func(t *testing.T) { @@ -113,23 +113,23 @@ "1000 %f": {1000, "%f", "1.000000kB/s"}, "1000 %d": {1000, "%d", "1kB/s"}, - "1000 %s": {1000, "%s", "1.0kB/s"}, + "1000 %s": {1000, "%s", "1kB/s"}, "1024 %f": {1024, "%f", "1.024000kB/s"}, "1024 %d": {1024, "%d", "1kB/s"}, "1024 %.1f": {1024, "%.1f", "1.0kB/s"}, - "1024 %s": {1024, "%s", "1.0kB/s"}, + "1024 %s": {1024, "%s", "1.024kB/s"}, "3*MB/s+140*KB/s %f": {3*MB + 140*KB, "%f", "3.140000MB/s"}, "3*MB/s+140*KB/s %d": {3*MB + 140*KB, "%d", "3MB/s"}, "3*MB/s+140*KB/s %.1f": {3*MB + 140*KB, "%.1f", "3.1MB/s"}, - "3*MB/s+140*KB/s %s": {3*MB + 140*KB, "%s", "3.1MB/s"}, + "3*MB/s+140*KB/s %s": {3*MB + 140*KB, "%s", "3.14MB/s"}, "2*GB/s %f": {2 * GB, "%f", "2.000000GB/s"}, "2*GB/s %d": {2 * GB, "%d", "2GB/s"}, "2*GB/s %.1f": {2 * GB, "%.1f", "2.0GB/s"}, - "2*GB/s %s": {2 * GB, "%s", "2.0GB/s"}, + "2*GB/s %s": {2 * GB, "%s", "2GB/s"}, "4*TB/s %f": {4 * TB, "%f", "4.000000TB/s"}, "4*TB/s %d": {4 * TB, "%d", "4TB/s"}, "4*TB/s %.1f": {4 * TB, "%.1f", "4.0TB/s"}, - "4*TB/s %s": {4 * TB, "%s", "4.0TB/s"}, + "4*TB/s %s": {4 * TB, "%s", "4TB/s"}, } for name, tc := range cases { t.Run(name, func(t *testing.T) { diff --git a/internal/percentage.go b/internal/percentage.go index 0483d25..7e261cb 100644 --- a/internal/percentage.go +++ b/internal/percentage.go @@ -3,10 +3,13 @@ import "math" // Percentage is a helper function, to calculate percentage. -func Percentage(total, current, width int64) int64 { +func Percentage(total, current int64, width int) float64 { if total <= 0 { return 0 } - p := float64(width*current) / float64(total) - return int64(math.Round(p)) + return float64(int64(width)*current) / float64(total) } + +func PercentageRound(total, current int64, width int) float64 { + return math.Round(Percentage(total, current, width)) +} diff --git a/internal/percentage_test.go b/internal/percentage_test.go index 4a649ce..a2fa3f7 100644 --- a/internal/percentage_test.go +++ b/internal/percentage_test.go @@ -4,9 +4,11 @@ func TestPercentage(t *testing.T) { // key is barWidth - testSuite := map[int64][]struct { - name string - total, current, expected int64 + testSuite := map[int][]struct { + name string + total int64 + current int64 + expected int64 }{ 100: { {"t,c,e{-1,-1,0}", -1, -1, 0}, @@ -64,7 +66,7 @@ for width, cases := range testSuite { for _, tc := range cases { - got := Percentage(tc.total, tc.current, width) + got := int64(PercentageRound(tc.total, tc.current, width)) if got != tc.expected { t.Errorf("width %d; %s: Expected: %d, got: %d\n", width, tc.name, tc.expected, got) }