85 lines
1.5 KiB
Go
85 lines
1.5 KiB
Go
package progress
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Spinner displays an animated spinner with a message
|
|
type Spinner struct {
|
|
message string
|
|
frames []string
|
|
interval time.Duration
|
|
stop chan struct{}
|
|
done chan struct{}
|
|
mu sync.Mutex
|
|
running bool
|
|
}
|
|
|
|
// NewSpinner creates a new spinner with the given message
|
|
func NewSpinner(message string) *Spinner {
|
|
return &Spinner{
|
|
message: message,
|
|
frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
|
|
interval: 80 * time.Millisecond,
|
|
stop: make(chan struct{}),
|
|
done: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Start begins the spinner animation
|
|
func (s *Spinner) Start() {
|
|
s.mu.Lock()
|
|
if s.running {
|
|
s.mu.Unlock()
|
|
return
|
|
}
|
|
s.running = true
|
|
s.mu.Unlock()
|
|
|
|
go func() {
|
|
i := 0
|
|
for {
|
|
select {
|
|
case <-s.stop:
|
|
// Clear the line and signal done
|
|
fmt.Print("\r\033[K")
|
|
close(s.done)
|
|
return
|
|
default:
|
|
fmt.Printf("\r%s %s", s.frames[i%len(s.frames)], s.message)
|
|
i++
|
|
time.Sleep(s.interval)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Stop stops the spinner and clears the line
|
|
func (s *Spinner) Stop() {
|
|
s.mu.Lock()
|
|
if !s.running {
|
|
s.mu.Unlock()
|
|
return
|
|
}
|
|
s.running = false
|
|
s.mu.Unlock()
|
|
|
|
close(s.stop)
|
|
<-s.done
|
|
}
|
|
|
|
// StopWithMessage stops the spinner and prints a final message
|
|
func (s *Spinner) StopWithMessage(message string) {
|
|
s.Stop()
|
|
fmt.Println(message)
|
|
}
|
|
|
|
// UpdateMessage updates the spinner message while running
|
|
func (s *Spinner) UpdateMessage(message string) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.message = message
|
|
}
|