1
2
3
4
5 package modcache
6
7 import (
8 "context"
9 "errors"
10 "fmt"
11 "io/fs"
12 "os"
13 "path/filepath"
14
15 "github.com/rogpeppe/go-internal/lockedfile"
16 "github.com/rogpeppe/go-internal/robustio"
17
18 "cuelang.org/go/mod/module"
19 )
20
21 var errNotCached = fmt.Errorf("not in cache")
22
23
24
25
26
27 func (c *cache) readDiskModFile(ctx context.Context, mv module.Version) (file string, data []byte, err error) {
28 return c.readDiskCache(ctx, mv, "mod")
29 }
30
31
32
33 func (c *cache) writeDiskModFile(ctx context.Context, file string, text []byte) error {
34 return c.writeDiskCache(ctx, file, text)
35 }
36
37
38
39
40
41
42 func (c *cache) readDiskCache(ctx context.Context, mv module.Version, suffix string) (file string, data []byte, err error) {
43 file, err = c.cachePath(ctx, mv, suffix)
44 if err != nil {
45 return "", nil, errNotCached
46 }
47 data, err = robustio.ReadFile(file)
48 if err != nil {
49 return file, nil, errNotCached
50 }
51 return file, data, nil
52 }
53
54
55
56 func (c *cache) writeDiskCache(ctx context.Context, file string, data []byte) error {
57 if file == "" {
58 return nil
59 }
60
61 if err := os.MkdirAll(filepath.Dir(file), 0777); err != nil {
62 return err
63 }
64
65
66
67 f, err := tempFile(ctx, filepath.Dir(file), filepath.Base(file), 0666)
68 if err != nil {
69 return err
70 }
71 defer func() {
72
73
74
75 if err != nil {
76 f.Close()
77 os.Remove(f.Name())
78 }
79 }()
80
81 if _, err := f.Write(data); err != nil {
82 return err
83 }
84 if err := f.Close(); err != nil {
85 return err
86 }
87 if err := robustio.Rename(f.Name(), file); err != nil {
88 return err
89 }
90 return nil
91 }
92
93
94
95
96
97
98 func (c *cache) downloadDir(ctx context.Context, m module.Version) (string, error) {
99 if !m.IsCanonical() {
100 return "", fmt.Errorf("non-semver module version %q", m.Version())
101 }
102 enc, err := module.EscapePath(m.BasePath())
103 if err != nil {
104 return "", err
105 }
106 encVer, err := module.EscapeVersion(m.Version())
107 if err != nil {
108 return "", err
109 }
110
111
112 dir := filepath.Join(c.dir, enc+"@"+encVer)
113 if fi, err := os.Stat(dir); os.IsNotExist(err) {
114 return dir, err
115 } else if err != nil {
116 return dir, &downloadDirPartialError{dir, err}
117 } else if !fi.IsDir() {
118 return dir, &downloadDirPartialError{dir, errors.New("not a directory")}
119 }
120
121
122
123 partialPath, err := c.cachePath(ctx, m, "partial")
124 if err != nil {
125 return dir, err
126 }
127 if _, err := os.Stat(partialPath); err == nil {
128 return dir, &downloadDirPartialError{dir, errors.New("not completely extracted")}
129 } else if !os.IsNotExist(err) {
130 return dir, err
131 }
132 return dir, nil
133 }
134
135 func (c *cache) cachePath(ctx context.Context, m module.Version, suffix string) (string, error) {
136 if !m.IsValid() || m.Version() == "" {
137 return "", fmt.Errorf("non-semver module version %q", m)
138 }
139 esc, err := module.EscapePath(m.BasePath())
140 if err != nil {
141 return "", err
142 }
143 encVer, err := module.EscapeVersion(m.Version())
144 if err != nil {
145 return "", err
146 }
147 return filepath.Join(c.dir, "cache/download", esc, "/@v", encVer+"."+suffix), nil
148 }
149
150
151
152
153
154 type downloadDirPartialError struct {
155 Dir string
156 Err error
157 }
158
159 func (e *downloadDirPartialError) Error() string { return fmt.Sprintf("%s: %v", e.Dir, e.Err) }
160 func (e *downloadDirPartialError) Is(err error) bool { return err == fs.ErrNotExist }
161
162
163
164 func (c *cache) lockVersion(ctx context.Context, mod module.Version) (unlock func(), err error) {
165 path, err := c.cachePath(ctx, mod, "lock")
166 if err != nil {
167 return nil, err
168 }
169 if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
170 return nil, err
171 }
172 return lockedfile.MutexAt(path).Lock()
173 }
174
View as plain text