package getproviders import ( "context" "sync" "github.com/hashicorp/terraform/internal/addrs" ) // MemoizeSource is a Source that wraps another Source and remembers its // results so that they can be returned more quickly on future calls to the // same object. // // Each MemoizeSource maintains a cache of response it has seen as part of its // body. All responses are retained for the remaining lifetime of the object. // Errors from the underlying source are also cached, and so subsequent calls // with the same arguments will always produce the same errors. // // A MemoizeSource can be called concurrently, with incoming requests processed // sequentially. type MemoizeSource struct { underlying Source availableVersions map[addrs.Provider]memoizeAvailableVersionsRet packageMetas map[memoizePackageMetaCall]memoizePackageMetaRet mu sync.Mutex } type memoizeAvailableVersionsRet struct { VersionList VersionList Warnings Warnings Err error } type memoizePackageMetaCall struct { Provider addrs.Provider Version Version Target Platform } type memoizePackageMetaRet struct { PackageMeta PackageMeta Err error } var _ Source = (*MemoizeSource)(nil) // NewMemoizeSource constructs and returns a new MemoizeSource that wraps // the given underlying source and memoizes its results. func NewMemoizeSource(underlying Source) *MemoizeSource { return &MemoizeSource{ underlying: underlying, availableVersions: make(map[addrs.Provider]memoizeAvailableVersionsRet), packageMetas: make(map[memoizePackageMetaCall]memoizePackageMetaRet), } } // AvailableVersions requests the available versions from the underlying source // and caches them before returning them, or on subsequent calls returns the // result directly from the cache. func (s *MemoizeSource) AvailableVersions(ctx context.Context, provider addrs.Provider) (VersionList, Warnings, error) { s.mu.Lock() defer s.mu.Unlock() if existing, exists := s.availableVersions[provider]; exists { return existing.VersionList, nil, existing.Err } ret, warnings, err := s.underlying.AvailableVersions(ctx, provider) s.availableVersions[provider] = memoizeAvailableVersionsRet{ VersionList: ret, Err: err, Warnings: warnings, } return ret, warnings, err } // PackageMeta requests package metadata from the underlying source and caches // the result before returning it, or on subsequent calls returns the result // directly from the cache. func (s *MemoizeSource) PackageMeta(ctx context.Context, provider addrs.Provider, version Version, target Platform) (PackageMeta, error) { s.mu.Lock() defer s.mu.Unlock() key := memoizePackageMetaCall{ Provider: provider, Version: version, Target: target, } if existing, exists := s.packageMetas[key]; exists { return existing.PackageMeta, existing.Err } ret, err := s.underlying.PackageMeta(ctx, provider, version, target) s.packageMetas[key] = memoizePackageMetaRet{ PackageMeta: ret, Err: err, } return ret, err } func (s *MemoizeSource) ForDisplay(provider addrs.Provider) string { return s.underlying.ForDisplay(provider) }