theckman / yacspin

Yet Another CLi Spinner; providing over 80 easy to use and customizable terminal spinners for multiple OSes
Apache License 2.0
434 stars 13 forks source link

Looking for some implementation advice #68

Closed securisec closed 2 years ago

securisec commented 2 years ago

Hey, first off, love the lib! Great work!

I was wondering if you could provide some implementation advice. Refer to this stackoverflow question for the original question.

I refactored since the post, and almost have it working the way I would want it, but because I am stopping and then starting the spinner because before logger print, it is adding a new line which I cannot get rid off.

Any advice on how to fix this?

Currently, the output is:

Apr 19 01:05:29 INF  data=k

Apr 19 01:05:29 INF  data=m

Apr 19 01:05:29 INF  data=l
⣾13/26

What I am hoping for is (no extra newline):

Apr 19 01:05:29 INF  data=k
Apr 19 01:05:29 INF  data=m
Apr 19 01:05:29 INF  data=l
⣾13/26

This is my working code so far:

package main

import (
    "fmt"
    "os"
    "os/signal"
    "sync"
    "syscall"
    "time"

    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
    "github.com/theckman/yacspin"
)

var stdOut = zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "Jan 02 15:04:05"}

func main() {
    var Logger = log.Output(stdOut)

    // pauseItForAMoment := false
    // create a spinner
    spinner, err := createSpinner()
    if err != nil {
        fmt.Printf("failed to make spinner from config struct: %v\n", err)
        os.Exit(1)
    }

    // start the spinner
    if err := spinner.Start(); err != nil {
        panic(err)
    }

    wg := &sync.WaitGroup{}
    wg.Add(1)
    var (
        total   int
        current int
    )

    data := make(chan string)

    // only want one go routine at a time but this is not important
    max := make(chan struct{}, 1)

    go func() {
        defer wg.Done()
        for d := range data {
            wg.Add(1)
            go func(wg *sync.WaitGroup, d string) {
                max <- struct{}{}
                defer func() {
                    <-max
                }()

                // function is doing work and printing the result once done.
                // pauseItForAMoment = true
                // spinner.Prefix(d)
                if spinner.Status() == yacspin.SpinnerRunning {
                    spinner.Stop()
                }

                // fmt.Println(d)
                Logger.Info().Str("data", d).Send()
                // fmt.Println(d)
                if spinner.Status() == yacspin.SpinnerStopped {
                    spinner.Start()
                }
                current += 1
                spinner.Message(fmt.Sprintf("%d/%d", current, total))
                // pauseItForAMoment = false

                // sends a value to the spinner go routine so that it can show
                // the updated count
                time.Sleep(100 * time.Millisecond)
                // spinnerCh <- 1
                wg.Done()
            }(wg, d)
        }
    }()

    // simulate queing some work
    ss := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
    for _, s := range ss {
        data <- s
    }
    total = len(ss)

    close(data)
    wg.Wait()
    if err := spinner.Stop(); err != nil {
        spinner.StopFail()
    }
}

func createSpinner() (*yacspin.Spinner, error) {
    // build the configuration, each field is documented
    cfg := yacspin.Config{
        Frequency: 100 * time.Millisecond,
        CharSet:   yacspin.CharSets[11],
        Suffix:    "", // puts a least one space between the animating spinner and the Message
        // Message:           "collecting files",
        // SuffixAutoColon: true,
        // ColorAll:        true,
        Colors:        []string{"fgYellow"},
        StopCharacter: " ",
        StopColors:    []string{"fgGreen"},
        // StopMessage:       "done",
        StopFailCharacter: "✗",
        StopFailColors:    []string{"fgRed"},
        StopFailMessage:   "failed",
    }

    s, err := yacspin.New(cfg)
    if err != nil {
        return nil, fmt.Errorf("failed to make spinner from struct: %w", err)
    }

    return s, nil
}

func stopOnSignal(spinner *yacspin.Spinner) {
    // ensure we stop the spinner before exiting, otherwise cursor will remain
    // hidden and terminal will require a `reset`
    sigCh := make(chan os.Signal, 1)
    signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
    go func() {
        <-sigCh

        spinner.StopFailMessage("interrupted")

        // ignoring error intentionally
        _ = spinner.StopFail()

        os.Exit(0)
    }()
}

Thanks in advance!

theckman commented 2 years ago

Don't set the StopCharacter, or set it to empty string (""). I believe that'll do what you want.

If not let me know!