catwalk.go

 1package config
 2
 3import (
 4	"context"
 5	"errors"
 6	"log/slog"
 7	"sync"
 8	"sync/atomic"
 9
10	"github.com/charmbracelet/catwalk/pkg/catwalk"
11	"github.com/charmbracelet/catwalk/pkg/embedded"
12)
13
14type catwalkClient interface {
15	GetProviders(context.Context, string) ([]catwalk.Provider, error)
16}
17
18var _ syncer[[]catwalk.Provider] = (*catwalkSync)(nil)
19
20type catwalkSync struct {
21	once       sync.Once
22	result     []catwalk.Provider
23	cache      cache[[]catwalk.Provider]
24	client     catwalkClient
25	autoupdate bool
26	init       atomic.Bool
27}
28
29func (s *catwalkSync) Init(client catwalkClient, path string, autoupdate bool) {
30	s.client = client
31	s.cache = newCache[[]catwalk.Provider](path)
32	s.autoupdate = autoupdate
33	s.init.Store(true)
34}
35
36func (s *catwalkSync) Get(ctx context.Context) ([]catwalk.Provider, error) {
37	if !s.init.Load() {
38		panic("called Get before Init")
39	}
40
41	var throwErr error
42	s.once.Do(func() {
43		if !s.autoupdate {
44			slog.Info("Using embedded Catwalk providers")
45			s.result = embedded.GetAll()
46			return
47		}
48
49		cached, etag, cachedErr := s.cache.Get()
50		if len(cached) == 0 || cachedErr != nil {
51			// if cached file is empty, default to embedded providers
52			cached = embedded.GetAll()
53		}
54
55		slog.Info("Fetching providers from Catwalk")
56		result, err := s.client.GetProviders(ctx, etag)
57		if errors.Is(err, context.DeadlineExceeded) {
58			slog.Warn("Catwalk providers not updated in time")
59			s.result = cached
60			return
61		}
62		if errors.Is(err, catwalk.ErrNotModified) {
63			slog.Info("Catwalk providers not modified")
64			s.result = cached
65			return
66		}
67		if err != nil {
68			// On error, fall back to cached (which defaults to embedded if empty).
69			s.result = cached
70			return
71		}
72		if len(result) == 0 {
73			s.result = cached
74			throwErr = errors.New("empty providers list from catwalk")
75			return
76		}
77
78		s.result = result
79		throwErr = s.cache.Store(result)
80	})
81	return s.result, throwErr
82}