detect: cache Default() result with sync.Once, add Reset()

Co-authored-by: taigrr <8261498+taigrr@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-28 06:18:45 +00:00
parent 52d276bf43
commit 68ec5cb659
2 changed files with 51 additions and 6 deletions

View File

@@ -3,20 +3,42 @@ package detect
import (
"os/exec"
"sync"
"github.com/gogrlx/snack"
)
var (
defaultOnce sync.Once
defaultMgr snack.Manager
defaultErr error
)
// Default returns the first available package manager on the system.
// The result is cached after the first call.
// Returns ErrManagerNotFound if no supported manager is detected.
func Default() (snack.Manager, error) {
for _, fn := range candidates() {
m := fn()
if m.Available() {
return m, nil
defaultOnce.Do(func() {
for _, fn := range candidates() {
m := fn()
if m.Available() {
defaultMgr = m
return
}
}
}
return nil, snack.ErrManagerNotFound
defaultErr = snack.ErrManagerNotFound
})
return defaultMgr, defaultErr
}
// Reset clears the cached result of Default(), forcing re-detection on the
// next call. This is intended for use in tests or dynamic environments where
// the available package managers may change.
// Reset is not safe to call concurrently with Default().
func Reset() {
defaultOnce = sync.Once{}
defaultMgr = nil
defaultErr = nil
}
// All returns all available package managers on the system.

View File

@@ -20,3 +20,26 @@ func TestDefaultDoesNotPanic(t *testing.T) {
// May return error if no managers available; that's fine.
_, _ = Default()
}
func TestDefaultCachesResult(t *testing.T) {
Reset()
m1, err1 := Default()
m2, err2 := Default()
if m1 != m2 {
t.Error("expected Default() to return the same Manager on repeated calls")
}
if err1 != err2 {
t.Error("expected Default() to return the same error on repeated calls")
}
// Exactly one of m1/err1 must be non-nil (either a manager was found or not).
if (m1 == nil) == (err1 == nil) {
t.Error("expected exactly one of Manager or error to be non-nil")
}
}
func TestResetAllowsRedetection(t *testing.T) {
_, _ = Default()
Reset()
// After reset, defaultOnce should be fresh; calling Default() again should work.
_, _ = Default()
}