1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package load
16
17 import (
18 "bytes"
19 "fmt"
20 "io"
21 iofs "io/fs"
22 "os"
23 "path/filepath"
24 "sort"
25 "strings"
26 "time"
27
28 "cuelang.org/go/cue/ast"
29 "cuelang.org/go/cue/errors"
30 "cuelang.org/go/cue/token"
31 "cuelang.org/go/mod/module"
32 )
33
34 type overlayFile struct {
35 basename string
36 contents []byte
37 file *ast.File
38 modtime time.Time
39 isDir bool
40 }
41
42 func (f *overlayFile) Name() string { return f.basename }
43 func (f *overlayFile) Size() int64 { return int64(len(f.contents)) }
44 func (f *overlayFile) Mode() iofs.FileMode {
45 if f.isDir {
46 return iofs.ModeDir | 0o555
47 }
48 return 0o444
49 }
50 func (f *overlayFile) ModTime() time.Time { return f.modtime }
51 func (f *overlayFile) IsDir() bool { return f.isDir }
52 func (f *overlayFile) Sys() interface{} { return nil }
53
54
55 type fileSystem struct {
56 overlayDirs map[string]map[string]*overlayFile
57 cwd string
58 }
59
60 func (fs *fileSystem) getDir(dir string, create bool) map[string]*overlayFile {
61 dir = filepath.Clean(dir)
62 m, ok := fs.overlayDirs[dir]
63 if !ok && create {
64 m = map[string]*overlayFile{}
65 fs.overlayDirs[dir] = m
66 }
67 return m
68 }
69
70
71
72
73
74
75
76
77
78
79
80
81
82 func (fs *fileSystem) ioFS(root string) iofs.FS {
83 dir := fs.getDir(root, false)
84 if dir == nil {
85 return module.OSDirFS(root)
86 }
87 return &ioFS{
88 fs: fs,
89 root: root,
90 }
91 }
92
93 func (fs *fileSystem) init(cwd string, overlay map[string]Source) error {
94 fs.cwd = cwd
95 fs.overlayDirs = map[string]map[string]*overlayFile{}
96
97
98 for filename, src := range overlay {
99 if !filepath.IsAbs(filename) {
100 return fmt.Errorf("non-absolute file path %q in overlay", filename)
101 }
102
103
104 dir, base := filepath.Split(filename)
105 m := fs.getDir(dir, true)
106 b, file, err := src.contents()
107 if err != nil {
108 return err
109 }
110 m[base] = &overlayFile{
111 basename: base,
112 contents: b,
113 file: file,
114 modtime: time.Now(),
115 }
116
117 for {
118 prevdir := dir
119 dir, base = filepath.Split(filepath.Dir(dir))
120 if dir == prevdir || dir == "" {
121 break
122 }
123 m := fs.getDir(dir, true)
124 if m[base] == nil {
125 m[base] = &overlayFile{
126 basename: base,
127 modtime: time.Now(),
128 isDir: true,
129 }
130 }
131 }
132 }
133 return nil
134 }
135
136 func (fs *fileSystem) joinPath(elem ...string) string {
137 return filepath.Join(elem...)
138 }
139
140 func (fs *fileSystem) makeAbs(path string) string {
141 if filepath.IsAbs(path) {
142 return path
143 }
144 return filepath.Join(fs.cwd, path)
145 }
146
147 func (fs *fileSystem) isDir(path string) bool {
148 path = fs.makeAbs(path)
149 if fs.getDir(path, false) != nil {
150 return true
151 }
152 fi, err := os.Stat(path)
153 return err == nil && fi.IsDir()
154 }
155
156 func (fs *fileSystem) readDir(path string) ([]iofs.DirEntry, errors.Error) {
157 path = fs.makeAbs(path)
158 m := fs.getDir(path, false)
159 items, err := os.ReadDir(path)
160 if err != nil {
161 if !os.IsNotExist(err) || m == nil {
162 return nil, errors.Wrapf(err, token.NoPos, "readDir")
163 }
164 }
165 if m == nil {
166 return items, nil
167 }
168 done := map[string]bool{}
169 for i, fi := range items {
170 done[fi.Name()] = true
171 if o := m[fi.Name()]; o != nil {
172 items[i] = iofs.FileInfoToDirEntry(o)
173 }
174 }
175 for _, o := range m {
176 if !done[o.Name()] {
177 items = append(items, iofs.FileInfoToDirEntry(o))
178 }
179 }
180 sort.Slice(items, func(i, j int) bool {
181 return items[i].Name() < items[j].Name()
182 })
183 return items, nil
184 }
185
186 func (fs *fileSystem) getOverlay(path string) *overlayFile {
187 dir, base := filepath.Split(path)
188 if m := fs.getDir(dir, false); m != nil {
189 return m[base]
190 }
191 return nil
192 }
193
194 func (fs *fileSystem) stat(path string) (iofs.FileInfo, errors.Error) {
195 path = fs.makeAbs(path)
196 if fi := fs.getOverlay(path); fi != nil {
197 return fi, nil
198 }
199 fi, err := os.Stat(path)
200 if err != nil {
201 return nil, errors.Wrapf(err, token.NoPos, "stat")
202 }
203 return fi, nil
204 }
205
206 func (fs *fileSystem) lstat(path string) (iofs.FileInfo, errors.Error) {
207 path = fs.makeAbs(path)
208 if fi := fs.getOverlay(path); fi != nil {
209 return fi, nil
210 }
211 fi, err := os.Lstat(path)
212 if err != nil {
213 return nil, errors.Wrapf(err, token.NoPos, "stat")
214 }
215 return fi, nil
216 }
217
218 func (fs *fileSystem) openFile(path string) (io.ReadCloser, errors.Error) {
219 path = fs.makeAbs(path)
220 if fi := fs.getOverlay(path); fi != nil {
221 return io.NopCloser(bytes.NewReader(fi.contents)), nil
222 }
223
224 f, err := os.Open(path)
225 if err != nil {
226 return nil, errors.Wrapf(err, token.NoPos, "load")
227 }
228 return f, nil
229 }
230
231 var skipDir = errors.Newf(token.NoPos, "skip directory")
232
233 type walkFunc func(path string, entry iofs.DirEntry, err errors.Error) errors.Error
234
235 func (fs *fileSystem) walk(root string, f walkFunc) error {
236 info, err := fs.lstat(root)
237 entry := iofs.FileInfoToDirEntry(info)
238 if err != nil {
239 err = f(root, entry, err)
240 } else if !info.IsDir() {
241 return errors.Newf(token.NoPos, "path %q is not a directory", root)
242 } else {
243 err = fs.walkRec(root, entry, f)
244 }
245 if err == skipDir {
246 return nil
247 }
248 return err
249
250 }
251
252 func (fs *fileSystem) walkRec(path string, entry iofs.DirEntry, f walkFunc) errors.Error {
253 if !entry.IsDir() {
254 return f(path, entry, nil)
255 }
256
257 dir, err := fs.readDir(path)
258 err1 := f(path, entry, err)
259
260
261
262
263 if err != nil || err1 != nil {
264
265
266
267
268 return err1
269 }
270
271 for _, entry := range dir {
272 filename := fs.joinPath(path, entry.Name())
273 err = fs.walkRec(filename, entry, f)
274 if err != nil {
275 if !entry.IsDir() || err != skipDir {
276 return err
277 }
278 }
279 }
280 return nil
281 }
282
283 var _ interface {
284 iofs.FS
285 iofs.ReadDirFS
286 iofs.ReadFileFS
287 module.OSRootFS
288 } = (*ioFS)(nil)
289
290 type ioFS struct {
291 fs *fileSystem
292 root string
293 }
294
295 func (fs *ioFS) OSRoot() string {
296 return fs.root
297 }
298
299 func (fs *ioFS) Open(name string) (iofs.File, error) {
300 fpath, err := fs.absPathFromFSPath(name)
301 if err != nil {
302 return nil, err
303 }
304 r, err := fs.fs.openFile(fpath)
305 if err != nil {
306 return nil, err
307 }
308 return &ioFSFile{
309 fs: fs.fs,
310 path: fpath,
311 rc: r,
312 }, nil
313 }
314
315 func (fs *ioFS) absPathFromFSPath(name string) (string, error) {
316 if !iofs.ValidPath(name) {
317 return "", fmt.Errorf("invalid io/fs path %q", name)
318 }
319
320
321
322 if strings.ContainsAny(name, ":\\") {
323 return "", fmt.Errorf("invalid io/fs path %q", name)
324 }
325 return filepath.Join(fs.root, name), nil
326 }
327
328
329 func (fs *ioFS) ReadDir(name string) ([]iofs.DirEntry, error) {
330 fpath, err := fs.absPathFromFSPath(name)
331 if err != nil {
332 return nil, err
333 }
334 return fs.fs.readDir(fpath)
335 }
336
337
338 func (fs *ioFS) ReadFile(name string) ([]byte, error) {
339 fpath, err := fs.absPathFromFSPath(name)
340 if err != nil {
341 return nil, err
342 }
343 if fi := fs.fs.getOverlay(fpath); fi != nil {
344 return bytes.Clone(fi.contents), nil
345 }
346 return os.ReadFile(fpath)
347 }
348
349
350 type ioFSFile struct {
351 fs *fileSystem
352 path string
353 rc io.ReadCloser
354 entries []iofs.DirEntry
355 }
356
357 var _ interface {
358 iofs.File
359 iofs.ReadDirFile
360 } = (*ioFSFile)(nil)
361
362 func (f *ioFSFile) Stat() (iofs.FileInfo, error) {
363 return f.fs.stat(f.path)
364 }
365
366 func (f *ioFSFile) Read(buf []byte) (int, error) {
367 return f.rc.Read(buf)
368 }
369
370 func (f *ioFSFile) Close() error {
371 return f.rc.Close()
372 }
373
374 func (f *ioFSFile) ReadDir(n int) ([]iofs.DirEntry, error) {
375 if f.entries == nil {
376 entries, err := f.fs.readDir(f.path)
377 if err != nil {
378 return entries, err
379 }
380 if entries == nil {
381 entries = []iofs.DirEntry{}
382 }
383 f.entries = entries
384 }
385 if n <= 0 {
386 entries := f.entries
387 f.entries = f.entries[len(f.entries):]
388 return entries, nil
389 }
390 var err error
391 if n >= len(f.entries) {
392 n = len(f.entries)
393 err = io.EOF
394 }
395 entries := f.entries[:n]
396 f.entries = f.entries[n:]
397 return entries, err
398 }
399
View as plain text