fix rendering and variable width issues on progress bar

This commit is contained in:
Jeffrey Morgan 2023-11-21 10:02:28 -05:00
parent f321b13a03
commit aabd71aede

View file

@ -42,6 +42,19 @@ func NewBar(message string, maxValue, initialValue int64) *Bar {
} }
} }
// formatDuration limits the rendering of a time.Duration to 2 units
func formatDuration(d time.Duration) string {
if d >= 100*time.Hour {
return "99h+"
}
if d >= time.Hour {
return fmt.Sprintf("%dh%dm", int(d.Hours()), int(d.Minutes())%60)
}
return d.Round(time.Second).String()
}
func (b *Bar) String() string { func (b *Bar) String() string {
termWidth, _, err := term.GetSize(int(os.Stderr.Fd())) termWidth, _, err := term.GetSize(int(os.Stderr.Fd()))
if err != nil { if err != nil {
@ -65,39 +78,43 @@ func (b *Bar) String() string {
} }
fmt.Fprintf(&pre, "%3.0f%% ", math.Floor(b.percent())) fmt.Fprintf(&pre, "%3.0f%% ", math.Floor(b.percent()))
fmt.Fprintf(&suf, "(%s/%s", format.HumanBytes(b.currentValue), format.HumanBytes(b.maxValue)) fmt.Fprintf(&suf, "(%s/%s", format.HumanBytes(b.currentValue), format.HumanBytes(b.maxValue))
stats := b.Stats() stats := b.Stats()
rate := int64(stats.rate) rate := stats.rate
if rate > 0 { if stats.value > b.initialValue && stats.value < b.maxValue {
fmt.Fprintf(&suf, ", %s/s", format.HumanBytes(rate)) fmt.Fprintf(&suf, ", %s/s", format.HumanBytes(int64(rate)))
} }
fmt.Fprintf(&suf, ")") fmt.Fprintf(&suf, ")")
elapsed := time.Since(b.started) elapsed := time.Since(b.started)
if b.percent() < 100 && rate > 0 { var timing string
fmt.Fprintf(&suf, " [%s:%s]", elapsed.Round(time.Second), stats.remaining) if stats.value > b.initialValue && stats.value < b.maxValue {
} else { timing = fmt.Sprintf("[%s:%s]", formatDuration(elapsed), formatDuration(stats.remaining))
fmt.Fprintf(&suf, " ")
} }
mid.WriteString("▕") // 44 is the maximum width for the stats on the right of the progress bar
if suf.Len() < 44 {
suf.WriteString(strings.Repeat(" ", 44-suf.Len()-len(timing)))
}
suf.WriteString(timing)
// add 3 extra spaces: 2 boundary characters and 1 space at the end // add 3 extra spaces: 2 boundary characters and 1 space at the end
f := termWidth - pre.Len() - suf.Len() - 3 f := termWidth - pre.Len() - suf.Len() - 3
n := int(float64(f) * b.percent() / 100) n := int(float64(f) * b.percent() / 100)
if n > 0 { if f > 0 {
mid.WriteString("▕")
mid.WriteString(strings.Repeat("█", n)) mid.WriteString(strings.Repeat("█", n))
if f-n > 0 {
mid.WriteString(strings.Repeat(" ", f-n))
}
mid.WriteString("▏")
} }
if f-n > 0 {
mid.WriteString(strings.Repeat(" ", f-n))
}
mid.WriteString("▏")
return pre.String() + mid.String() + suf.String() return pre.String() + mid.String() + suf.String()
} }
@ -140,6 +157,8 @@ func (b *Bar) Stats() Stats {
var remaining time.Duration var remaining time.Duration
if rate > 0 { if rate > 0 {
remaining = time.Second * time.Duration((float64(b.maxValue-b.currentValue))/(float64(rate))) remaining = time.Second * time.Duration((float64(b.maxValue-b.currentValue))/(float64(rate)))
} else {
remaining = time.Duration(math.MaxInt64)
} }
b.stats = Stats{ b.stats = Stats{