Skip to content

Counting work

func New(total int64, opts Options) *Bar
func (b *Bar) Advance(n int64)

A bar counts units of work up to a total. The unit is yours: files, rows, requests or bytes. Advance adds n to the count and returns. It never draws, so call it as often as you like.

bar := progress.New(int64(len(rows)), progress.Options{})
bar.Start()
for _, r := range rows {
insert(r)
bar.Advance(1)
}
err := bar.Stop()

To count bytes, make the total a size and advance by every write. A small writer does it, so io.Copy drives the bar:

examples/copy/main.go
// Command copy copies a file under a bar that counts the bytes.
//
// go run ./examples/copy <src> <dst>
package main
import (
"errors"
"fmt"
"io"
"log"
"os"
"github.com/vardius/progress-go"
)
// copyFile copies src to dst. The bar's total is src's size, and it advances by every byte written.
func copyFile(out io.Writer, dst, src string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
info, err := in.Stat()
if err != nil {
return err
}
to, err := os.Create(dst)
if err != nil {
return err
}
bar := progress.New(info.Size(), progress.Options{Output: out})
bar.Start()
_, err = io.Copy(io.MultiWriter(to, counter{bar}), in)
if err != nil {
err = fmt.Errorf("copy %s to %s: %w", src, dst, err)
}
return errors.Join(err, to.Close(), bar.Stop())
}
// counter is a writer that advances a bar by the length of every write.
type counter struct{ bar *progress.Bar }
func (c counter) Write(p []byte) (int, error) {
c.bar.Advance(int64(len(p)))
return len(p), nil
}
func main() {
log.SetFlags(0)
if len(os.Args) != 3 {
log.Fatal("usage: copy <src> <dst>")
}
if err := copyFile(os.Stdout, os.Args[2], os.Args[1]); err != nil {
log.Fatal(err)
}
}

io.MultiWriter writes to the file first, so the bar counts only bytes the file took.

To pick up a job part way, advance by the work already done before Start:

bar := progress.New(size, progress.Options{Verbose: true})
bar.Advance(alreadyDownloaded)
bar.Start()

The bar starts at that count. The time estimates count only the work done since Start, so the earlier work doesn’t make them optimistic.

7/10 [███████████████████████████████████ ] 70%
  • The count is right-aligned to the width of the total, so it keeps its place as it grows.
  • The bar is 50 cells. A cell fills for every 2% of the total, rounded down: 7 of 10 fills 35 cells.
  • The percentage is rounded down, so 199 of 200 is 99%. It shows 100% only once the work is done.

The count always shows the true number, while the bar and the percentage stay within 0 and 100%:

Count Bar and percentage
Above the total, such as 12/10 Full, 100%
Below zero, after a negative Advance Empty, 0%
Any count, with a total of 0 or less Full, 100%: there was nothing to do

The maths is exact for any int64, the largest totals included.

Stop draws the bar as it stands. A job that fails at 3 of 10 ends showing it:

3/10 [███████████████ ] 30%