Files
sslh-multiplex-lab/pkg/utils/progress.go
2026-01-29 00:03:02 +00:00

127 lines
2.4 KiB
Go

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)
}