summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/progress-bar/progress-bar.go (renamed from progress-bar.go)31
-rw-r--r--go.mod7
-rw-r--r--go.sum2
-rw-r--r--progress.go63
4 files changed, 74 insertions, 29 deletions
diff --git a/progress-bar.go b/cmd/progress-bar/progress-bar.go
index 0b1cefe..65079ef 100644
--- a/progress-bar.go
+++ b/cmd/progress-bar/progress-bar.go
@@ -8,19 +8,14 @@ import (
"os"
"os/exec"
"strconv"
- "strings"
"time"
- "golang.org/x/term"
+ "gitlab.com/rhogenson/progress-bar/progress"
)
var n = flag.Duration("n", 2*time.Minute, "polling interval")
func run() error {
- cols, _, err := term.GetSize(int(os.Stdout.Fd()))
- if err != nil {
- return err
- }
args := flag.Args()
if len(args) < 2 {
return errors.New("usage error")
@@ -31,11 +26,7 @@ func run() error {
return fmt.Errorf("max: %s", err)
}
cmd := args[1:]
- type measurement struct {
- t time.Time
- i int
- }
- var measurements []measurement
+ bar := progress.New(maxVal)
for i := 0; ; i++ {
outputBytes, err := exec.Command(cmd[0], cmd[1:]...).Output()
if err != nil {
@@ -47,22 +38,8 @@ func run() error {
fmt.Println(err)
continue
}
- p := output * cols / maxVal
- if i > 0 {
- fmt.Print("\033[2F\033[J")
- }
- fmt.Printf("%s>\n", strings.Repeat("=", max(p-1, 0)))
- measurements = append(measurements, measurement{time.Now(), output})
- if len(measurements) > 20 {
- measurements = measurements[1:]
- }
- deltaT := measurements[len(measurements)-1].t.Sub(measurements[0].t)
- delta := measurements[len(measurements)-1].i - measurements[0].i
- if delta == 0 {
- fmt.Println()
- } else {
- fmt.Printf("ETA: %s\n", time.Duration(float64(maxVal-output)/float64(delta)*float64(deltaT)).Round(time.Second))
- }
+ bar.Set(output)
+ bar.Print()
time.Sleep(*n)
}
}
diff --git a/go.mod b/go.mod
index 8d19cf8..9531fa9 100644
--- a/go.mod
+++ b/go.mod
@@ -1,7 +1,10 @@
-module gitlab.com/rhogenson/progress-bar
+module gitlab.com/rhogenson/progress-bar/progress
go 1.24.1
-require golang.org/x/term v0.28.0
+require (
+ gitlab.com/rhogenson/vecdeque v0.0.0-20250405201931-eb7fb2bc95c6
+ golang.org/x/term v0.28.0
+)
require golang.org/x/sys v0.29.0 // indirect
diff --git a/go.sum b/go.sum
index f05f63b..4af7e2b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+gitlab.com/rhogenson/vecdeque v0.0.0-20250405201931-eb7fb2bc95c6 h1:S1vSZ4W5v+eL/ayT5M4RDqPUHlAN2SKl/x65O9D4jPo=
+gitlab.com/rhogenson/vecdeque v0.0.0-20250405201931-eb7fb2bc95c6/go.mod h1:i++skCmDHJAQbyC5LqvJbehbEGCiO8EkrsPon6h0Jgo=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
diff --git a/progress.go b/progress.go
new file mode 100644
index 0000000..7660605
--- /dev/null
+++ b/progress.go
@@ -0,0 +1,63 @@
+package progress
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "time"
+
+ "gitlab.com/rhogenson/vecdeque"
+ "golang.org/x/term"
+)
+
+const measurements = 20
+
+type measurement struct {
+ t time.Time
+ i int
+}
+
+type Bar struct {
+ max int
+ measurements vecdeque.DQ[measurement]
+ cols int
+}
+
+func New(max int) *Bar {
+ b := &Bar{max: max}
+ b.measurements.Grow(measurements)
+ return b
+}
+
+func (b *Bar) Set(i int) {
+ if b.measurements.Len() == measurements {
+ b.measurements.PopFront()
+ }
+ b.measurements.PushBack(measurement{time.Now(), i})
+}
+
+func (b *Bar) Print() {
+ first := b.measurements.Get(0)
+ last := b.measurements.Get(b.measurements.Len() - 1)
+ deltaT := last.t.Sub(first.t)
+ delta := last.i - first.i
+ eta := time.Duration(-1)
+ if delta != 0 {
+ eta = time.Duration(float64(b.max-last.i) / float64(delta) * float64(deltaT))
+ }
+
+ if b.cols == 0 {
+ var err error
+ if b.cols, _, err = term.GetSize(int(os.Stderr.Fd())); err != nil {
+ fmt.Fprintf(os.Stderr, "Warning: unable to determine terminal size: %s\n", err)
+ }
+ } else {
+ p := last.i * b.cols / b.max
+ fmt.Fprintf(os.Stderr, "\033[2F\033[J%s>\n", strings.Repeat("=", max(p-1, 0)))
+ }
+ if eta < 0 {
+ fmt.Fprintln(os.Stderr, "ETA: calculating...")
+ } else {
+ fmt.Fprintf(os.Stderr, "ETA: %s\n", eta.Round(time.Second))
+ }
+}