Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 73 additions & 7 deletions progressbar.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ type config struct {
// specifies how many rows of details to show,default value is 0 and no details will be shown
maxDetailRow int

// separator to format numbers (e.g. "1,048,576" or "1_048_576")
numberSeparator string

stdBuffer bytes.Buffer
}

Expand Down Expand Up @@ -445,6 +448,23 @@ func OptionSetStartingBytes(num int64) Option {
}
}

// OptionSetNumberSeparator sets the separator used when formatting numbers (e.g. "," or "_").
func OptionSetNumberSeparator(separator string) Option {
return func(p *ProgressBar) {
p.config.numberSeparator = separator
}
}

// OptionSetSeparator is an alias for OptionSetNumberSeparator.
func OptionSetSeparator(separator string) Option {
return OptionSetNumberSeparator(separator)
}

// OptionSeparator is an alias for OptionSetNumberSeparator.
func OptionSeparator(separator string) Option {
return OptionSetNumberSeparator(separator)
}

// NewOptions constructs a new instance of ProgressBar, with any options you specify
func NewOptions(max int, options ...Option) *ProgressBar {
return NewOptions64(int64(max), options...)
Expand Down Expand Up @@ -1269,18 +1289,18 @@ func renderProgressBar(c config, s *state) (int, error) {
sb.WriteString(fmt.Sprintf("%s%s", currentHumanize, currentSuffix))
}
} else if c.showTotalBytes {
sb.WriteString(fmt.Sprintf("%.0f/%d", s.currentBytes, c.max))
sb.WriteString(fmt.Sprintf("%s/%s", formatNumber(s.currentBytes, c.numberSeparator), formatNumber(float64(c.max), c.numberSeparator)))
} else {
sb.WriteString(fmt.Sprintf("%.0f", s.currentBytes))
sb.WriteString(formatNumber(s.currentBytes, c.numberSeparator))
}
} else {
if c.showBytes {
currentHumanize, currentSuffix := humanizeBytes(s.currentBytes, c.useIECUnits)
sb.WriteString(fmt.Sprintf("%s%s", currentHumanize, currentSuffix))
} else if c.showTotalBytes {
sb.WriteString(fmt.Sprintf("%.0f/%s", s.currentBytes, "-"))
sb.WriteString(fmt.Sprintf("%s/%s", formatNumber(s.currentBytes, c.numberSeparator), "-"))
} else {
sb.WriteString(fmt.Sprintf("%.0f", s.currentBytes))
sb.WriteString(formatNumber(s.currentBytes, c.numberSeparator))
}
}
}
Expand All @@ -1304,11 +1324,11 @@ func renderProgressBar(c config, s *state) (int, error) {
sb.WriteString(", ")
}
if averageRate > 1 {
sb.WriteString(fmt.Sprintf("%0.0f %s/s", averageRate, c.iterationString))
sb.WriteString(fmt.Sprintf("%s %s/s", formatNumber(averageRate, c.numberSeparator), c.iterationString))
} else if averageRate*60 > 1 {
sb.WriteString(fmt.Sprintf("%0.0f %s/min", 60*averageRate, c.iterationString))
sb.WriteString(fmt.Sprintf("%s %s/min", formatNumber(60*averageRate, c.numberSeparator), c.iterationString))
} else {
sb.WriteString(fmt.Sprintf("%0.0f %s/hr", 3600*averageRate, c.iterationString))
sb.WriteString(fmt.Sprintf("%s %s/hr", formatNumber(3600*averageRate, c.numberSeparator), c.iterationString))
}
}
if sb.Len() > 0 {
Expand Down Expand Up @@ -1653,3 +1673,49 @@ func Bprintf(pb *ProgressBar, format string, a ...interface{}) (int, error) {
return fmt.Fprintf(&pb.config.stdBuffer, format, a...)
}
}

func formatNumber(val float64, sep string) string {
s := fmt.Sprintf("%.0f", val)
return insertSeparator(s, sep)
}

func insertSeparator(s string, sep string) string {
if sep == "" || len(s) <= 3 {
return s
}

isNeg := false
if s[0] == '-' {
isNeg = true
s = s[1:]
}

if len(s) <= 3 {
if isNeg {
return "-" + s
}
return s
}

var b strings.Builder
if isNeg {
b.WriteByte('-')
}

rem := len(s) % 3
if rem > 0 {
b.WriteString(s[:rem])
if rem < len(s) {
b.WriteString(sep)
}
}

for i := rem; i < len(s); i += 3 {
b.WriteString(s[i : i+3])
if i+3 < len(s) {
b.WriteString(sep)
}
}

return b.String()
}
73 changes: 73 additions & 0 deletions progressbar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1393,3 +1393,76 @@ func TestRateAveragingWindow(t *testing.T) {
t.Errorf("windowed: expected more than %d retained samples, got %d", maxLegacyRateSamples, got)
}
}

func TestFormatNumber(t *testing.T) {
tests := []struct {
val float64
sep string
expected string
}{
{val: 0, sep: "_", expected: "0"},
{val: 9, sep: "_", expected: "9"},
{val: 99, sep: "_", expected: "99"},
{val: 999, sep: "_", expected: "999"},
{val: 1000, sep: "_", expected: "1_000"},
{val: 10000, sep: ",", expected: "10,000"},
{val: 100000, sep: ",", expected: "100,000"},
{val: 1048576, sep: "_", expected: "1_048_576"},
{val: 1048576, sep: ",", expected: "1,048,576"},
{val: 1048576, sep: " ", expected: "1 048 576"},
{val: 1048576, sep: "", expected: "1048576"},
{val: 1000000000, sep: ",", expected: "1,000,000,000"},
{val: -1234, sep: ",", expected: "-1,234"},
{val: -1048576, sep: "_", expected: "-1_048_576"},
}

for _, tt := range tests {
actual := formatNumber(tt.val, tt.sep)
assert.Equal(t, tt.expected, actual, "val=%v sep=%q", tt.val, tt.sep)
}
}

func TestOptionSetNumberSeparator(t *testing.T) {
buf := strings.Builder{}
bar := NewOptions(1048576,
OptionSetWidth(10),
OptionShowCount(),
OptionSetPredictTime(false),
OptionSetNumberSeparator("_"),
OptionSetWriter(&buf),
)
_ = bar.Add(1024)
assert.Contains(t, buf.String(), "1_024/1_048_576")

// test OptionSetSeparator alias
buf.Reset()
bar = NewOptions(1000000,
OptionSetWidth(10),
OptionShowCount(),
OptionSetPredictTime(false),
OptionSetSeparator(","),
OptionSetWriter(&buf),
)
_ = bar.Add(5000)
assert.Contains(t, buf.String(), "5,000/1,000,000")

// test OptionSeparator alias
buf.Reset()
bar = NewOptions(1000000,
OptionSetWidth(10),
OptionShowCount(),
OptionSetPredictTime(false),
OptionSeparator(" "),
OptionSetWriter(&buf),
)
_ = bar.Add(5000)
assert.Contains(t, buf.String(), "5 000/1 000 000")
}

func ExampleOptionSetNumberSeparator() {
bar := NewOptions(1048576, OptionSetWidth(10), OptionShowCount(), OptionSetPredictTime(false), OptionSetNumberSeparator("_"))
bar.Add(1024)
// Output:
// 0% | | (1_024/1_048_576)
}