Initial code commit
This commit is contained in:
126
pkg/utils/progress.go
Normal file
126
pkg/utils/progress.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProgressBar struct {
|
||||
total int
|
||||
current int
|
||||
width int
|
||||
startTime time.Time
|
||||
label string
|
||||
}
|
||||
|
||||
func NewProgressBar(total int, label string) *ProgressBar {
|
||||
return &ProgressBar{
|
||||
total: total,
|
||||
current: 0,
|
||||
width: 40,
|
||||
startTime: time.Now(),
|
||||
label: label,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProgressBar) Update(current int) {
|
||||
p.current = current
|
||||
p.render()
|
||||
}
|
||||
|
||||
func (p *ProgressBar) Increment() {
|
||||
p.current++
|
||||
p.render()
|
||||
}
|
||||
|
||||
func (p *ProgressBar) render() {
|
||||
if p.total == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
percent := float64(p.current) / float64(p.total)
|
||||
filled := int(percent * float64(p.width))
|
||||
empty := p.width - filled
|
||||
|
||||
bar := ""
|
||||
for i := 0; i < filled; i++ {
|
||||
bar += "█"
|
||||
}
|
||||
for i := 0; i < empty; i++ {
|
||||
bar += "░"
|
||||
}
|
||||
|
||||
elapsed := time.Since(p.startTime)
|
||||
var eta time.Duration
|
||||
if p.current > 0 && elapsed.Seconds() > 0 {
|
||||
rate := float64(p.current) / elapsed.Seconds()
|
||||
if rate > 0 {
|
||||
remaining := float64(p.total-p.current) / rate
|
||||
eta = time.Duration(remaining) * time.Second
|
||||
if eta < 0 {
|
||||
eta = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if eta > 0 {
|
||||
fmt.Fprintf(os.Stderr, "\r%s [%s] %d/%d (%.1f%%) ETA: %s", p.label, bar, p.current, p.total, percent*100, eta.Round(time.Second))
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "\r%s [%s] %d/%d (%.1f%%)", p.label, bar, p.current, p.total, percent*100)
|
||||
}
|
||||
if p.current >= p.total {
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProgressBar) Finish() {
|
||||
p.current = p.total
|
||||
p.render()
|
||||
}
|
||||
|
||||
type Spinner struct {
|
||||
chars []string
|
||||
index int
|
||||
label string
|
||||
stopChan chan bool
|
||||
doneChan chan bool
|
||||
}
|
||||
|
||||
func NewSpinner(label string) *Spinner {
|
||||
return &Spinner{
|
||||
chars: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
|
||||
index: 0,
|
||||
label: label,
|
||||
stopChan: make(chan bool),
|
||||
doneChan: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Spinner) Start() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-s.stopChan:
|
||||
fmt.Fprintf(os.Stderr, "\r%s %s\n", s.chars[s.index], s.label)
|
||||
s.doneChan <- true
|
||||
return
|
||||
case <-ticker.C:
|
||||
fmt.Fprintf(os.Stderr, "\r%s %s", s.chars[s.index], s.label)
|
||||
s.index = (s.index + 1) % len(s.chars)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Spinner) Stop() {
|
||||
s.stopChan <- true
|
||||
<-s.doneChan
|
||||
}
|
||||
|
||||
func (s *Spinner) StopWithMessage(message string) {
|
||||
s.Stop()
|
||||
fmt.Fprintf(os.Stderr, "%s\n", message)
|
||||
}
|
||||
Reference in New Issue
Block a user