spinner.go 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. package progress
  2. import (
  3. "fmt"
  4. "strings"
  5. "sync/atomic"
  6. "time"
  7. )
  8. type Spinner struct {
  9. message atomic.Value
  10. messageWidth int
  11. parts []string
  12. value int
  13. ticker *time.Ticker
  14. started time.Time
  15. stopped time.Time
  16. }
  17. func NewSpinner(message string) *Spinner {
  18. s := &Spinner{
  19. parts: []string{
  20. "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏",
  21. },
  22. started: time.Now(),
  23. }
  24. s.SetMessage(message)
  25. go s.start()
  26. return s
  27. }
  28. func (s *Spinner) SetMessage(message string) {
  29. s.message.Store(message)
  30. }
  31. func (s *Spinner) String() string {
  32. var sb strings.Builder
  33. if message, ok := s.message.Load().(string); ok && len(message) > 0 {
  34. message := strings.TrimSpace(message)
  35. if s.messageWidth > 0 && len(message) > s.messageWidth {
  36. message = message[:s.messageWidth]
  37. }
  38. fmt.Fprintf(&sb, "%s", message)
  39. if padding := s.messageWidth - sb.Len(); padding > 0 {
  40. sb.WriteString(strings.Repeat(" ", padding))
  41. }
  42. sb.WriteString(" ")
  43. }
  44. if s.stopped.IsZero() {
  45. spinner := s.parts[s.value]
  46. sb.WriteString(spinner)
  47. sb.WriteString(" ")
  48. }
  49. return sb.String()
  50. }
  51. func (s *Spinner) start() {
  52. s.ticker = time.NewTicker(100 * time.Millisecond)
  53. for range s.ticker.C {
  54. s.value = (s.value + 1) % len(s.parts)
  55. if !s.stopped.IsZero() {
  56. return
  57. }
  58. }
  59. }
  60. func (s *Spinner) Stop() {
  61. if s.stopped.IsZero() {
  62. s.stopped = time.Now()
  63. }
  64. }