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