1package config
2
3import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "sync"
8
9 "github.com/charmbracelet/crush/internal/fur/client"
10 "github.com/charmbracelet/crush/internal/fur/provider"
11)
12
13var fur = client.New()
14
15var (
16 providerOnc sync.Once // Ensures the initialization happens only once
17 providerList []provider.Provider
18 // UseMockProviders can be set to true in tests to avoid API calls
19 UseMockProviders bool
20)
21
22func providersPath() string {
23 return filepath.Join(baseDataPath(), "providers.json")
24}
25
26func saveProviders(providers []provider.Provider) error {
27 path := providersPath()
28 dir := filepath.Dir(path)
29 if err := os.MkdirAll(dir, 0o755); err != nil {
30 return err
31 }
32
33 data, err := json.MarshalIndent(providers, "", " ")
34 if err != nil {
35 return err
36 }
37
38 return os.WriteFile(path, data, 0o644)
39}
40
41func loadProviders() ([]provider.Provider, error) {
42 path := providersPath()
43 data, err := os.ReadFile(path)
44 if err != nil {
45 return nil, err
46 }
47
48 var providers []provider.Provider
49 err = json.Unmarshal(data, &providers)
50 return providers, err
51}
52
53func Providers() []provider.Provider {
54 providerOnc.Do(func() {
55 // Use mock providers when testing
56 if UseMockProviders {
57 providerList = MockProviders()
58 return
59 }
60
61 // Try to get providers from upstream API
62 if providers, err := fur.GetProviders(); err == nil {
63 providerList = providers
64 // Save providers locally for future fallback
65 _ = saveProviders(providers)
66 } else {
67 // If upstream fails, try to load from local cache
68 if localProviders, localErr := loadProviders(); localErr == nil {
69 providerList = localProviders
70 } else {
71 // If both fail, return empty list
72 providerList = []provider.Provider{}
73 }
74 }
75 })
76 return providerList
77}
78
79// ResetProviders resets the provider cache. Useful for testing.
80func ResetProviders() {
81 providerOnc = sync.Once{}
82 providerList = nil
83}