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  }