| package main | 
 |  | 
 | import ( | 
 | 	"strings" | 
 | 	"sync" | 
 |  | 
 | 	"github.com/FiloSottile/gvt/gbvendor" | 
 | ) | 
 |  | 
 | type cacheKey struct { | 
 | 	url, repoType         string | 
 | 	branch, tag, revision string | 
 | } | 
 |  | 
 | type cacheEntry struct { | 
 | 	wg  sync.WaitGroup | 
 | 	v   vendor.WorkingCopy | 
 | 	err error | 
 | } | 
 |  | 
 | // Downloader acts as a cache for downloaded repositories | 
 | type Downloader struct { | 
 | 	wcsMu sync.Mutex | 
 | 	wcs   map[cacheKey]*cacheEntry | 
 |  | 
 | 	reposMu sync.RWMutex | 
 | 	repos   map[string]vendor.RemoteRepo | 
 | 	reposI  map[string]vendor.RemoteRepo | 
 | } | 
 |  | 
 | var GlobalDownloader = Downloader{} | 
 |  | 
 | func init() { | 
 | 	GlobalDownloader.wcs = make(map[cacheKey]*cacheEntry) | 
 | 	GlobalDownloader.repos = make(map[string]vendor.RemoteRepo) | 
 | 	GlobalDownloader.reposI = make(map[string]vendor.RemoteRepo) | 
 | } | 
 |  | 
 | // Get returns a cached WorkingCopy, or runs RemoteRepo.Checkout | 
 | func (d *Downloader) Get(repo vendor.RemoteRepo, branch, tag, revision string) (vendor.WorkingCopy, error) { | 
 | 	key := cacheKey{ | 
 | 		url: repo.URL(), repoType: repo.Type(), | 
 | 		branch: branch, tag: tag, revision: revision, | 
 | 	} | 
 | 	d.wcsMu.Lock() | 
 | 	if entry, ok := d.wcs[key]; ok { | 
 | 		d.wcsMu.Unlock() | 
 | 		entry.wg.Wait() | 
 | 		return entry.v, entry.err | 
 | 	} | 
 |  | 
 | 	entry := &cacheEntry{} | 
 | 	entry.wg.Add(1) | 
 | 	d.wcs[key] = entry | 
 | 	d.wcsMu.Unlock() | 
 |  | 
 | 	entry.v, entry.err = repo.Checkout(branch, tag, revision) | 
 | 	entry.wg.Done() | 
 | 	return entry.v, entry.err | 
 | } | 
 |  | 
 | func (d *Downloader) Flush() error { | 
 | 	d.wcsMu.Lock() | 
 | 	defer d.wcsMu.Unlock() | 
 |  | 
 | 	for _, entry := range d.wcs { | 
 | 		entry.wg.Wait() | 
 | 		if entry.err != nil { | 
 | 			continue | 
 | 		} | 
 | 		if err := entry.v.Destroy(); err != nil { | 
 | 			return err | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // DeduceRemoteRepo is a cached version of vendor.DeduceRemoteRepo | 
 | func (d *Downloader) DeduceRemoteRepo(path string, insecure bool) (vendor.RemoteRepo, string, error) { | 
 | 	cache := d.repos | 
 | 	if insecure { | 
 | 		cache = d.reposI | 
 | 	} | 
 |  | 
 | 	d.reposMu.RLock() | 
 | 	for p, repo := range cache { | 
 | 		if path == p || strings.HasPrefix(path, p+"/") { | 
 | 			d.reposMu.RUnlock() | 
 | 			extra := strings.Trim(strings.TrimPrefix(path, p), "/") | 
 | 			return repo, extra, nil | 
 | 		} | 
 | 	} | 
 | 	d.reposMu.RUnlock() | 
 |  | 
 | 	repo, extra, err := vendor.DeduceRemoteRepo(path, insecure) | 
 | 	if err != nil { | 
 | 		return repo, extra, err | 
 | 	} | 
 |  | 
 | 	if !strings.HasSuffix(path, extra) { | 
 | 		// Shouldn't happen, but in case just bypass the cache | 
 | 		return repo, extra, err | 
 | 	} | 
 | 	basePath := strings.Trim(strings.TrimSuffix(path, extra), "/") | 
 | 	d.reposMu.Lock() | 
 | 	cache[basePath] = repo | 
 | 	d.reposMu.Unlock() | 
 |  | 
 | 	return repo, extra, err | 
 | } |