github.com/anchore/syft@v1.38.2/internal/cache/resolver.go (about) 1 package cache 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "path" 8 9 "github.com/anchore/syft/internal" 10 "github.com/anchore/syft/internal/log" 11 ) 12 13 // Resolver interface provides a single Resolve method, which will return from cache 14 // or call the provided resolve function to get the value if not available in cache 15 type Resolver[T any] interface { 16 // Resolve attempts to resolve the given key from cache and convert it to the type of the cache, 17 // or calls the resolver function if unable to resolve a cached value 18 Resolve(key string, resolver resolverFunc[T]) (T, error) 19 } 20 21 // GetResolver returns a cache resolver for persistent cached data across Syft runs, stored in a unique 22 // location based on the provided name and versioned by the type 23 func GetResolver[T any](name, version string) Resolver[T] { 24 typeHash := hashType[T]() 25 versionKey := path.Join(version, typeHash) 26 return &cacheResolver[T]{ 27 name: fmt.Sprintf("%s/%s", name, versionKey), 28 cache: manager.GetCache(name, versionKey), 29 } 30 } 31 32 const resolverKeySuffix = ".json" 33 34 type resolverFunc[T any] func() (T, error) 35 36 type cacheResolver[T any] struct { 37 name string 38 cache Cache 39 } 40 41 var _ interface { 42 Resolver[int] 43 } = (*cacheResolver[int])(nil) 44 45 func (r *cacheResolver[T]) Resolve(key string, resolver resolverFunc[T]) (T, error) { 46 key += resolverKeySuffix 47 48 rdr, err := r.cache.Read(key) 49 if rdr == nil || err != nil { 50 return r.resolveAndCache(key, resolver) 51 } 52 defer internal.CloseAndLogError(rdr, key) 53 54 dec := json.NewDecoder(rdr) 55 if dec == nil { 56 log.Tracef("error getting cache json decoder for %s %v: %v", r.name, key, err) 57 return r.resolveAndCache(key, resolver) 58 } 59 var t T 60 err = dec.Decode(&t) 61 if err != nil { 62 log.Tracef("error decoding cached entry for %s %v: %v", r.name, key, err) 63 return r.resolveAndCache(key, resolver) 64 } 65 // no error, able to resolve from cache 66 return t, nil 67 } 68 69 func (r *cacheResolver[T]) resolveAndCache(key string, resolver func() (T, error)) (T, error) { 70 t, err := resolver() 71 if err != nil { 72 return t, err 73 } 74 var data bytes.Buffer 75 enc := json.NewEncoder(&data) 76 enc.SetEscapeHTML(false) 77 err = enc.Encode(t) 78 if err != nil { 79 return t, err 80 } 81 err = r.cache.Write(key, &data) 82 return t, err 83 }