func (c *Cache) Path(checksum, uri string) string {
	base := BasePath(checksum, uri)
	return filepath.Join(c.root, base)
}
Path to cached object.
package cache
import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"github.com/pkg/errors"
	"github.com/cashapp/hermit/github"
	"github.com/cashapp/hermit/ui"
	"github.com/cashapp/hermit/util"
)
type Cache struct {
	root               string
	httpClient         *http.Client
	gh                 *github.Client
	fastFailHTTPClient *http.Client
}
func BasePath(checksum, uri string) string {
	hash := util.Hash(uri, checksum)
	return filepath.Join(hash[:2], hash+"-"+filepath.Base(uri))
}
func Open(root string, ghClient *github.Client, client *http.Client, fastFailClient *http.Client) (*Cache, error) {
	err := os.MkdirAll(root, os.ModePerm)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return &Cache{
		root:               root,
		httpClient:         client,
		gh:                 ghClient,
		fastFailHTTPClient: fastFailClient,
	}, nil
}
func (c *Cache) Root() string {
	return c.root
}
func (c *Cache) Mkdir(uri string) (string, error) {
	path := c.Path("", uri)
	return path, os.MkdirAll(path, os.ModePerm)
}
func (c *Cache) Create(checksum, uri string) (*os.File, error) {
	path := c.Path(checksum, uri)
	dir := filepath.Dir(path)
	err := os.MkdirAll(dir, os.ModePerm)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return os.Create(path)
}
func (c *Cache) OpenLocal(checksum, uri string) (*os.File, error) {
	source, err := GetSource(uri)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return source.OpenLocal(c, checksum)
}
func (c *Cache) Open(b *ui.Task, checksum, uri string, mirrors ...string) (*os.File, error) {
	cachePath := c.Path(checksum, uri)
	_, err := os.Stat(cachePath)
	if err == nil {
		b.Tracef("returning cached path %s for %s", cachePath, uri)
		return os.Open(cachePath)
	} else if !os.IsNotExist(err) {
		return nil, errors.WithStack(err)
	}
	path, _, err := c.Download(b, checksum, uri, mirrors...)
	if err != nil {
		return nil, err
	}
	return os.Open(path)
}
func (c *Cache) Download(b *ui.Task, checksum, uri string, mirrors ...string) (path string, etag string, err error) {
	uris := append([]string{uri}, mirrors...)
	for _, uri := range uris {
		defer ui.LogElapsed(b, "Download %s", uri)()
		source, err := GetSource(uri)
		if err != nil {
			return "", "", errors.WithStack(err)
		}
		path, etag, err = source.Download(b, c, checksum)
		if err == nil {
			return path, etag, nil
		}
		b.Debugf("%s: %s", uri, err)
	}
	if err == nil {
		return "", "", errors.Errorf("failed to download from any of %s", strings.Join(uris, ", "))
	}
	return "", "", errors.WithStack(err)
}
func (c *Cache) ETag(b *ui.Task, uri string, mirrors ...string) (etag string, err error) {
	for _, uri := range append([]string{uri}, mirrors...) {
		source, err := GetSource(uri)
		if err != nil {
			return "", errors.WithStack(err)
		}
		result, err := source.ETag(b, c)
		if err != nil {
			b.Debugf("%s failed: %s", uri, err)
			continue
		}
		return result, nil
	}
	return "", nil
}
func (c *Cache) IsCached(checksum, uri string) bool {
	_, err := os.Stat(c.Path(checksum, uri))
	return err == nil
}
func (c *Cache) Evict(b *ui.Task, checksum, uri string) error {
	b.SubTask("remove").Debugf("rm -rf %s", c.Path(checksum, uri))
	err := os.RemoveAll(c.Path(checksum, uri))
	if err != nil && !os.IsNotExist(err) {
		return errors.WithStack(err)
	}
	return nil
}
func (c *Cache) Clean() error {
	return os.RemoveAll(c.root)
}