constellation/cli/internal/cmd/spinner.go
2022-10-10 13:43:15 +02:00

101 lines
1.8 KiB
Go

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package cmd
import (
"fmt"
"io"
"sync"
"sync/atomic"
"time"
"github.com/spf13/cobra"
)
var (
spinnerStates = []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"}
dotsStates = []string{".", "..", "..."}
)
type spinnerInterf interface {
Start(text string, showDots bool)
Stop()
}
type spinner struct {
out *cobra.Command
delay time.Duration
wg *sync.WaitGroup
stop int32
}
func newSpinner(c *cobra.Command, writer io.Writer) (*spinner, *interruptSpinWriter) {
spinner := &spinner{
out: c,
wg: &sync.WaitGroup{},
delay: 100 * time.Millisecond,
stop: 0,
}
if writer != nil {
interruptWriter := &interruptSpinWriter{
writer: writer,
spinner: spinner,
}
return spinner, interruptWriter
}
return spinner, nil
}
func (s *spinner) Start(text string, showDots bool) {
s.wg.Add(1)
go func() {
defer s.wg.Done()
for i := 0; ; i = (i + 1) % len(spinnerStates) {
if atomic.LoadInt32(&s.stop) != 0 {
break
}
dotsState := ""
if showDots {
dotsState = dotsStates[i%len(dotsStates)]
}
state := fmt.Sprintf("\r%s %s%s", spinnerStates[i], text, dotsState)
s.out.Print(state)
time.Sleep(s.delay)
}
dotsState := ""
if showDots {
dotsState = dotsStates[len(dotsStates)-1]
}
finalState := fmt.Sprintf("\r%s%s ", text, dotsState)
s.out.Println(finalState)
}()
}
func (s *spinner) Stop() {
atomic.StoreInt32(&s.stop, 1)
s.wg.Wait()
}
type interruptSpinWriter struct {
spinner *spinner
writer io.Writer
}
func (w *interruptSpinWriter) Write(p []byte) (n int, err error) {
w.spinner.Stop()
return w.writer.Write(p)
}
type nopSpinner struct{}
func (s nopSpinner) Start(string, bool) {}
func (s nopSpinner) Stop() {}