1package config
2
3import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "runtime"
8 "sync"
9
10 "github.com/charmbracelet/crush/internal/fur/provider"
11)
12
13type ProviderClient interface {
14 GetProviders() ([]provider.Provider, error)
15}
16
17var (
18 providerOnce sync.Once
19 providerList []provider.Provider
20)
21
22// file to cache provider data
23func providerCacheFileData() string {
24 xdgDataHome := os.Getenv("XDG_DATA_HOME")
25 if xdgDataHome != "" {
26 return filepath.Join(xdgDataHome, appName)
27 }
28
29 // return the path to the main data directory
30 // for windows, it should be in `%LOCALAPPDATA%/crush/`
31 // for linux and macOS, it should be in `$HOME/.local/share/crush/`
32 if runtime.GOOS == "windows" {
33 localAppData := os.Getenv("LOCALAPPDATA")
34 if localAppData == "" {
35 localAppData = filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Local")
36 }
37 return filepath.Join(localAppData, appName)
38 }
39
40 return filepath.Join(os.Getenv("HOME"), ".local", "share", appName, "providers.json")
41}
42
43func saveProvidersInCache(path string, providers []provider.Provider) error {
44 dir := filepath.Dir(path)
45 if err := os.MkdirAll(dir, 0o755); err != nil {
46 return err
47 }
48
49 data, err := json.MarshalIndent(providers, "", " ")
50 if err != nil {
51 return err
52 }
53
54 return os.WriteFile(path, data, 0o644)
55}
56
57func loadProvidersFromCache(path string) ([]provider.Provider, error) {
58 data, err := os.ReadFile(path)
59 if err != nil {
60 return nil, err
61 }
62
63 var providers []provider.Provider
64 err = json.Unmarshal(data, &providers)
65 return providers, err
66}
67
68func loadProviders(path string, client ProviderClient) ([]provider.Provider, error) {
69 providers, err := client.GetProviders()
70 if err != nil {
71 fallbackToCache, err := loadProvidersFromCache(path)
72 if err != nil {
73 return nil, err
74 }
75 providers = fallbackToCache
76 } else {
77 if err := saveProvidersInCache(path, providerList); err != nil {
78 return nil, err
79 }
80 }
81 return providers, nil
82}
83
84func LoadProviders(client ProviderClient) ([]provider.Provider, error) {
85 var err error
86 providerOnce.Do(func() {
87 providerList, err = loadProviders(providerCacheFileData(), client)
88 })
89 if err != nil {
90 return nil, err
91 }
92 return providerList, nil
93}