1package config
 2
 3import (
 4	"encoding/json"
 5	"os"
 6	"path/filepath"
 7	"runtime"
 8	"sync"
 9
10	"github.com/charmbracelet/catwalk/pkg/catwalk"
11)
12
13type ProviderClient interface {
14	GetProviders() ([]catwalk.Provider, error)
15}
16
17var (
18	providerOnce sync.Once
19	providerList []catwalk.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, "providers.json")
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, "providers.json")
38	}
39
40	return filepath.Join(os.Getenv("HOME"), ".local", "share", appName, "providers.json")
41}
42
43func saveProvidersInCache(path string, providers []catwalk.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) ([]catwalk.Provider, error) {
58	data, err := os.ReadFile(path)
59	if err != nil {
60		return nil, err
61	}
62
63	var providers []catwalk.Provider
64	err = json.Unmarshal(data, &providers)
65	return providers, err
66}
67
68func loadProviders(path string, client ProviderClient) ([]catwalk.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 Providers() ([]catwalk.Provider, error) {
85	return LoadProviders(catwalk.NewWithURL(catwalkURL))
86}
87
88func LoadProviders(client ProviderClient) ([]catwalk.Provider, error) {
89	var err error
90	providerOnce.Do(func() {
91		providerList, err = loadProviders(providerCacheFileData(), client)
92	})
93	if err != nil {
94		return nil, err
95	}
96	return providerList, nil
97}