func (s *Spinner) Pause() error {
	if !atomic.CompareAndSwapUint32(s.status, statusRunning, statusPausing) {
		return errors.New("spinner not running")
	}
	s.unpauseCh, s.unpausedCh = make(chan struct{}), make(chan struct{})
	s.pauseCh <- struct{}{}
	if !atomic.CompareAndSwapUint32(s.status, statusPausing, statusPaused) {
		panic("atomic invariant encountered")
	}
	return nil
}
Pause puts the spinner in a state where it no longer animates or renders
updates to data. This function blocks until the spinner's internal goroutine
enters a paused state.

If you want to make a few configuration changes and have them to appear at
the same time, like changing the suffix, message, and color, you can Pause()
the spinner first and then Unpause() after making the changes.

If the spinner is not running (stopped, paused, or in transition to another
state) this returns an error.
package yacspin
import (
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"github.com/mattn/go-runewidth"
	"github.com/pkg/errors"
)
type character struct {
	Value string
	Size  int
}
func setToCharSlice(ss []string) ([]character, int) {
	if len(ss) == 0 {
		return nil, 0
	}
	var maxWidth int
	c := make([]character, len(ss))
	for i, s := range ss {
		n := runewidth.StringWidth(s)
		if n > maxWidth {
			maxWidth = n
		}
		c[i] = character{
			Value: s,
			Size:  n,
		}
	}
	return c, maxWidth
}
type Config struct {
	Frequency time.Duration
	Delay time.Duration
	Writer io.Writer
	HideCursor bool
	ColorAll bool
	Colors []string
	CharSet []string
	Prefix string
	Suffix string
	SuffixAutoColon bool
	Message string
	StopMessage string
	StopCharacter string
	StopColors []string
	StopFailMessage string
	StopFailCharacter string
	StopFailColors []string
}
type Spinner struct {
	writer          io.Writer
	colorAll        bool
	cursorHidden    bool
	suffixAutoColon bool
	isDumbTerm      bool
	status       *uint32
	lastPrintLen int
	cancelCh     chan struct{}  
	doneCh       chan struct{}
	pauseCh      chan struct{}
	unpauseCh    chan struct{}
	unpausedCh   chan struct{}
	mu                *sync.Mutex
	frequency         time.Duration
	chars             []character
	maxWidth          int
	index             int
	prefix            string
	suffix            string
	message           string
	colorFn           func(format string, a ...interface{}) string
	stopMsg           string
	stopChar          character
	stopColorFn       func(format string, a ...interface{}) string
	stopFailMsg       string
	stopFailChar      character
	stopFailColorFn   func(format string, a ...interface{}) string
	frequencyUpdateCh chan time.Duration
	dataUpdateCh      chan struct{}
}
const (
	statusStopped uint32 = iota
	statusStarting
	statusRunning
	statusStopping
	statusPausing
	statusPaused
	statusUnpausing
)
func New(cfg Config) (*Spinner, error) {
	if cfg.Delay > 0 && cfg.Frequency == 0 {
		cfg.Frequency = cfg.Delay
	}
	if cfg.Frequency < 1 {
		return nil, errors.New("cfg.Frequency must be greater than 0")
	}
	s := &Spinner{
		mu:                &sync.Mutex{},
		frequency:         cfg.Frequency,
		status:            uint32Ptr(0),
		frequencyUpdateCh: make(chan time.Duration),
		dataUpdateCh:      make(chan struct{}),
		colorAll:        cfg.ColorAll,
		cursorHidden:    cfg.HideCursor,
		suffixAutoColon: cfg.SuffixAutoColon,
		isDumbTerm:      os.Getenv("TERM") == "dumb" || runtime.GOOS == "windows",
		colorFn:         fmt.Sprintf,
		stopColorFn:     fmt.Sprintf,
		stopFailColorFn: fmt.Sprintf,
	}
	if err := s.Colors(cfg.Colors...); err != nil {
		return nil, err
	}
	if err := s.StopColors(cfg.StopColors...); err != nil {
		return nil, err
	}
	if err := s.StopFailColors(cfg.StopFailColors...); err != nil {
		return nil, err
	}
	if len(cfg.CharSet) == 0 {
		cfg.CharSet = CharSets[9]
	}
	_ = s.CharSet(cfg.CharSet)
	if cfg.Writer == nil {
		cfg.Writer = os.Stdout
	}
	s.writer = cfg.Writer
	if len(cfg.Prefix) > 0 {
		s.Prefix(cfg.Prefix)
	}
	if len(cfg.Suffix) > 0 {
		s.Suffix(cfg.Suffix)
	}
	if len(cfg.Message) > 0 {
		s.Message(cfg.Message)
	}
	if len(cfg.StopMessage) > 0 {
		s.StopMessage(cfg.StopMessage)
	}
	if len(cfg.StopCharacter) > 0 {
		s.StopCharacter(cfg.StopCharacter)
	}
	if len(cfg.StopFailMessage) > 0 {
		s.StopFailMessage(cfg.StopFailMessage)
	}
	if len(cfg.StopFailCharacter) > 0 {
		s.StopFailCharacter(cfg.StopFailCharacter)
	}
	return s, nil
}
func (s *Spinner) notifyDataChange() {
	select {
	case s.dataUpdateCh <- struct{}{}:
	default:
	}
}
func (s *Spinner) Active() bool {
	v := atomic.LoadUint32(s.status)
	return v == 1 || v == 2
}
type SpinnerStatus uint32
const (
	SpinnerStopped SpinnerStatus = iota
	SpinnerStarting
	SpinnerRunning
	SpinnerStopping
	SpinnerPausing
	SpinnerPaused
	SpinnerUnpausing
)
func (s SpinnerStatus) String() string {
	switch s {
	case SpinnerStopped:
		return "stopped"
	case SpinnerStarting:
		return "starting"
	case SpinnerRunning:
		return "running"
	case SpinnerStopping:
		return "stopping"
	case SpinnerPausing:
		return "pausing"
	case SpinnerPaused:
		return "paused"
	case SpinnerUnpausing:
		return "unpausing"
	default:
		return fmt.Sprintf("unknown (%d)", s)
	}
}
func (s *Spinner) Status() SpinnerStatus {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.status == nil {
		panic("status field is nil")
	}
	return SpinnerStatus(atomic.LoadUint32(s.status))
}
func (s *Spinner) Start() error {
	if !atomic.CompareAndSwapUint32(s.status, statusStopped, statusStarting) {
		return errors.New("spinner already running or shutting down")
	}
	s.mu.Lock()
	s.frequencyUpdateCh = make(chan time.Duration, 1)
	s.dataUpdateCh, s.cancelCh = make(chan struct{}, 1), make(chan struct{}, 1)
	s.mu.Unlock()
	s.doneCh = make(chan struct{})
	s.pauseCh = make(chan struct{})  
	go s.painter(s.cancelCh, s.dataUpdateCh, s.pauseCh, s.doneCh, s.frequencyUpdateCh)
	if !atomic.CompareAndSwapUint32(s.status, statusStarting, statusRunning) {
		panic("atomic invariant encountered")
	}
	return nil
}