diff --git a/data/quotes.json b/data/quotes.json index ebe4dd4..190e8f2 100644 --- a/data/quotes.json +++ b/data/quotes.json @@ -83,7 +83,7 @@ "svg": "Vectorize the collective – infinite resolution, zero resistance.", "webp": "Hybrid assimilation – the Core optimizes without compromise.", "heic": "Apple‑grade assimilation – the Core preserves HDR.", - "raw": "Raw data intake – the Core ingests the sensor’s soul", + "raw": "Raw data intake – the Core ingests the sensor’s soul.", "ico": "Iconic assimilation – the Core packs the smallest symbols.", "avif": "Next‑gen assimilation – the Core squeezes the future.", "tiff": "High‑definition capture – the Core stores every photon.", diff --git a/go.mod b/go.mod index 0bbc79e..a9ff32d 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect diff --git a/go.sum b/go.sum index dbc75d0..f938f74 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= diff --git a/pkg/ui/non_interactive_prompter.go b/pkg/ui/non_interactive_prompter.go index f0f374a..3eb874f 100644 --- a/pkg/ui/non_interactive_prompter.go +++ b/pkg/ui/non_interactive_prompter.go @@ -4,6 +4,7 @@ package ui import ( "fmt" "os" + "sync" "time" "github.com/fatih/color" @@ -11,18 +12,29 @@ import ( ) type NonInteractivePrompter struct { - stopChan chan bool + stopChan chan struct{} quoteFunc func() (string, error) + started bool + mu sync.Mutex + stopOnce sync.Once } func NewNonInteractivePrompter(quoteFunc func() (string, error)) *NonInteractivePrompter { return &NonInteractivePrompter{ - stopChan: make(chan bool), + stopChan: make(chan struct{}), quoteFunc: quoteFunc, } } func (p *NonInteractivePrompter) Start() { + p.mu.Lock() + if p.started { + p.mu.Unlock() + return + } + p.started = true + p.mu.Unlock() + if p.IsInteractive() { return // Don't start in interactive mode } @@ -39,7 +51,7 @@ func (p *NonInteractivePrompter) Start() { quote, err := p.quoteFunc() if err != nil { fmt.Println("Error getting quote:", err) - return + continue } c := color.New(color.FgGreen) c.Println(quote) @@ -49,10 +61,12 @@ func (p *NonInteractivePrompter) Start() { } func (p *NonInteractivePrompter) Stop() { - if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) { + if p.IsInteractive() { return } - p.stopChan <- true + p.stopOnce.Do(func() { + close(p.stopChan) + }) } func (p *NonInteractivePrompter) IsInteractive() bool { diff --git a/pkg/ui/progress_writer.go b/pkg/ui/progress_writer.go index e4f6c00..b46b51b 100644 --- a/pkg/ui/progress_writer.go +++ b/pkg/ui/progress_writer.go @@ -12,6 +12,9 @@ func NewProgressWriter(bar *progressbar.ProgressBar) *progressWriter { } func (pw *progressWriter) Write(p []byte) (n int, err error) { + if pw == nil || pw.bar == nil { + return len(p), nil + } s := string(p) pw.bar.Describe(s) return len(p), nil diff --git a/pkg/ui/quote.go b/pkg/ui/quote.go index efd4cb7..3a182cc 100644 --- a/pkg/ui/quote.go +++ b/pkg/ui/quote.go @@ -5,12 +5,19 @@ import ( "encoding/json" "fmt" "math/rand" + "sync" "time" "github.com/Snider/Borg/data" "github.com/fatih/color" ) +var ( + cachedQuotes *Quotes + quotesOnce sync.Once + quotesErr error +) + func init() { rand.Seed(time.Now().UnixNano()) } @@ -49,8 +56,15 @@ func loadQuotes() (*Quotes, error) { return "es, nil } +func getQuotes() (*Quotes, error) { + quotesOnce.Do(func() { + cachedQuotes, quotesErr = loadQuotes() + }) + return cachedQuotes, quotesErr +} + func GetRandomQuote() (string, error) { - quotes, err := loadQuotes() + quotes, err := getQuotes() if err != nil { return "", err } @@ -63,6 +77,10 @@ func GetRandomQuote() (string, error) { allQuotes = append(allQuotes, quotes.PWAProcessing...) allQuotes = append(allQuotes, quotes.CodeRelatedLong...) + if len(allQuotes) == 0 { + return "", fmt.Errorf("no quotes available") + } + return allQuotes[rand.Intn(len(allQuotes))], nil } @@ -77,25 +95,34 @@ func PrintQuote() { } func GetVCSQuote() (string, error) { - quotes, err := loadQuotes() + quotes, err := getQuotes() if err != nil { return "", err } + if len(quotes.VCSProcessing) == 0 { + return "", fmt.Errorf("no VCS quotes available") + } return quotes.VCSProcessing[rand.Intn(len(quotes.VCSProcessing))], nil } func GetPWAQuote() (string, error) { - quotes, err := loadQuotes() + quotes, err := getQuotes() if err != nil { return "", err } + if len(quotes.PWAProcessing) == 0 { + return "", fmt.Errorf("no PWA quotes available") + } return quotes.PWAProcessing[rand.Intn(len(quotes.PWAProcessing))], nil } func GetWebsiteQuote() (string, error) { - quotes, err := loadQuotes() + quotes, err := getQuotes() if err != nil { return "", err } + if len(quotes.CodeRelatedLong) == 0 { + return "", fmt.Errorf("no website quotes available") + } return quotes.CodeRelatedLong[rand.Intn(len(quotes.CodeRelatedLong))], nil }