/
modcache.go
120 lines (106 loc) · 3.25 KB
/
modcache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package modcache
import (
"errors"
"fmt"
"go/build"
"os"
"path/filepath"
"strings"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
// Default loads a module cache from the default location
func Default() *Cache {
return New(getModDir())
}
// New module cache relative to the cache directory
func New(cacheDir string) *Cache {
return &Cache{cacheDir}
}
type Cache struct {
cacheDir string
}
// Directory returns the cache directory joined with optional subpaths
func (c *Cache) Directory(subpaths ...string) string {
return filepath.Join(append([]string{c.cacheDir}, subpaths...)...)
}
// SplitPathVersion splits a path@version into path & version
func SplitPathVersion(pathVersion string) (path, version string, err error) {
parts := strings.SplitN(pathVersion, "@", 2)
if len(parts) != 2 {
return "", "", fmt.Errorf("modcache: invalid module key for %q", pathVersion)
}
path, version = parts[0], parts[1]
if path == "" {
return "", "", fmt.Errorf("modcache: missing module path in %q", pathVersion)
}
if version == "" {
return "", "", fmt.Errorf("modcache: missing module version in %q", pathVersion)
}
return path, version, nil
}
// ResolveDirectory returns the directory to which m should have been
// downloaded. An error will be returned if the module path or version cannot be
// escaped. An error satisfying errors.Is(err, os.ErrNotExist) will be returned
// along with the directory if the directory does not exist or if the directory
// is not completely populated.
func (c *Cache) ResolveDirectory(modulePath, version string) (string, error) {
dir, err := c.getModuleDirectory(modulePath, version)
if err != nil {
return "", err
}
if fi, err := os.Stat(dir); os.IsNotExist(err) {
return "", err
} else if err != nil {
return "", &downloadDirPartialError{dir, err}
} else if !fi.IsDir() {
return "", &downloadDirPartialError{dir, errors.New("not a directory")}
}
return dir, nil
}
// Cache for faster subsequent requests
var modDir string
// getModDir returns the module cache directory
func getModDir() string {
if modDir != "" {
return modDir
}
env := os.Getenv("GOMODCACHE")
if env != "" {
modDir = env
return env
}
modDir = filepath.Join(build.Default.GOPATH, "pkg", "mod")
return modDir
}
// getModuleDirectory returns an absolute path to the required module.
func (c *Cache) getModuleDirectory(modulePath, version string) (string, error) {
enc, err := module.EscapePath(modulePath)
if err != nil {
return "", err
}
if !semver.IsValid(version) {
return "", fmt.Errorf("non-semver module version %q", version)
}
if module.CanonicalVersion(version) != version {
return "", fmt.Errorf("non-canonical module version %q", version)
}
encVer, err := module.EscapeVersion(version)
if err != nil {
return "", err
}
dir := filepath.Join(c.cacheDir, enc+"@"+encVer)
return dir, nil
}
// downloadDirPartialError is returned by DownloadDir if a module directory
// exists but was not completely populated.
//
// downloadDirPartialError is equivalent to os.ErrNotExist.
type downloadDirPartialError struct {
Dir string
Err error
}
// Error fn
func (e *downloadDirPartialError) Error() string { return fmt.Sprintf("%s: %v", e.Dir, e.Err) }
// Is fn
func (e *downloadDirPartialError) Is(err error) bool { return err == os.ErrNotExist }