-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
internal/registrytest: rewrite in terms of io/fs
This way, we can use it in the context of a testscript in cmd/cue; testscript.Setup doesn't give us direct access to the txtar.Archive, but it does extract it to the working directory, so we can use os.DirFS. Signed-off-by: Daniel Martí <[email protected]> Change-Id: I04fe783d6d16e802c1bd62f413877055bd11655b Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1169976 Reviewed-by: Roger Peppe <[email protected]> Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
- Loading branch information
Showing
4 changed files
with
237 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
// Borrowed from https://github.com/golang/tools/pull/289. | ||
// TODO(mvdan): replace by x/tools/txtar once the proposal at | ||
// https://github.com/golang/go/issues/44158 is accepted and implemented. | ||
|
||
// Copyright 2022 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package registrytest | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"io" | ||
"io/fs" | ||
"path" | ||
"strings" | ||
"time" | ||
|
||
"golang.org/x/tools/txtar" | ||
) | ||
|
||
// TxtarFS returns an fs.FS which reads from a txtar.Archive. | ||
func TxtarFS(ar *txtar.Archive) fs.FS { | ||
return archiveFS{ar} | ||
} | ||
|
||
type archiveFS struct { | ||
a *txtar.Archive | ||
} | ||
|
||
// Open implements fs.FS. | ||
func (fsys archiveFS) Open(name string) (fs.File, error) { | ||
if !fs.ValidPath(name) { | ||
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} | ||
} | ||
|
||
for _, f := range fsys.a.Files { | ||
// In case the txtar has weird filenames | ||
cleanName := path.Clean(f.Name) | ||
if name == cleanName { | ||
return newOpenFile(f), nil | ||
} | ||
} | ||
var entries []fileInfo | ||
dirs := make(map[string]bool) | ||
prefix := name + "/" | ||
if name == "." { | ||
prefix = "" | ||
} | ||
|
||
for _, f := range fsys.a.Files { | ||
cleanName := path.Clean(f.Name) | ||
if !strings.HasPrefix(cleanName, prefix) { | ||
continue | ||
} | ||
felem := cleanName[len(prefix):] | ||
i := strings.Index(felem, "/") | ||
if i < 0 { | ||
entries = append(entries, newFileInfo(f)) | ||
} else { | ||
dirs[felem[:i]] = true | ||
} | ||
} | ||
// If there are no children of the name, | ||
// then the directory is treated as not existing | ||
// unless the directory is "." | ||
if len(entries) == 0 && len(dirs) == 0 && name != "." { | ||
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} | ||
} | ||
|
||
for name := range dirs { | ||
entries = append(entries, newDirInfo(name)) | ||
} | ||
|
||
return &openDir{newDirInfo(name), entries, 0}, nil | ||
} | ||
|
||
var _ fs.ReadFileFS = archiveFS{} | ||
|
||
// ReadFile implements fs.ReadFileFS. | ||
func (fsys archiveFS) ReadFile(name string) ([]byte, error) { | ||
if !fs.ValidPath(name) { | ||
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} | ||
} | ||
if name == "." { | ||
return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("is a directory")} | ||
} | ||
prefix := name + "/" | ||
for _, f := range fsys.a.Files { | ||
if cleanName := path.Clean(f.Name); name == cleanName { | ||
return append(([]byte)(nil), f.Data...), nil | ||
} | ||
// It's a directory | ||
if strings.HasPrefix(f.Name, prefix) { | ||
return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("is a directory")} | ||
} | ||
} | ||
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} | ||
} | ||
|
||
var ( | ||
_ fs.File = (*openFile)(nil) | ||
_ io.ReadSeekCloser = (*openFile)(nil) | ||
_ io.ReaderAt = (*openFile)(nil) | ||
_ io.WriterTo = (*openFile)(nil) | ||
) | ||
|
||
type openFile struct { | ||
bytes.Reader | ||
fi fileInfo | ||
} | ||
|
||
func newOpenFile(f txtar.File) *openFile { | ||
var o openFile | ||
o.Reader.Reset(f.Data) | ||
o.fi = fileInfo{f, 0444} | ||
return &o | ||
} | ||
|
||
func (o *openFile) Stat() (fs.FileInfo, error) { return o.fi, nil } | ||
|
||
func (o *openFile) Close() error { return nil } | ||
|
||
var _ fs.FileInfo = fileInfo{} | ||
|
||
type fileInfo struct { | ||
f txtar.File | ||
m fs.FileMode | ||
} | ||
|
||
func newFileInfo(f txtar.File) fileInfo { | ||
return fileInfo{f, 0444} | ||
} | ||
|
||
func newDirInfo(name string) fileInfo { | ||
return fileInfo{txtar.File{Name: name}, fs.ModeDir | 0555} | ||
} | ||
|
||
func (f fileInfo) Name() string { return path.Base(f.f.Name) } | ||
func (f fileInfo) Size() int64 { return int64(len(f.f.Data)) } | ||
func (f fileInfo) Mode() fs.FileMode { return f.m } | ||
func (f fileInfo) Type() fs.FileMode { return f.m.Type() } | ||
func (f fileInfo) ModTime() time.Time { return time.Time{} } | ||
func (f fileInfo) IsDir() bool { return f.m.IsDir() } | ||
func (f fileInfo) Sys() interface{} { return f.f } | ||
func (f fileInfo) Info() (fs.FileInfo, error) { return f, nil } | ||
|
||
type openDir struct { | ||
dirInfo fileInfo | ||
entries []fileInfo | ||
offset int | ||
} | ||
|
||
func (d *openDir) Stat() (fs.FileInfo, error) { return &d.dirInfo, nil } | ||
func (d *openDir) Close() error { return nil } | ||
func (d *openDir) Read(b []byte) (int, error) { | ||
return 0, &fs.PathError{Op: "read", Path: d.dirInfo.f.Name, Err: errors.New("is a directory")} | ||
} | ||
|
||
func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) { | ||
n := len(d.entries) - d.offset | ||
if count > 0 && n > count { | ||
n = count | ||
} | ||
if n == 0 && count > 0 { | ||
return nil, io.EOF | ||
} | ||
entries := make([]fs.DirEntry, n) | ||
for i := range entries { | ||
entries[i] = &d.entries[d.offset+i] | ||
} | ||
d.offset += n | ||
return entries, nil | ||
} | ||
|
||
// From constructs an Archive with the contents of fsys and an empty Comment. | ||
// Subsequent changes to fsys are not reflected in the returned archive. | ||
// | ||
// The transformation is lossy. | ||
// For example, because directories are implicit in txtar archives, | ||
// empty directories in fsys will be lost, | ||
// and txtar does not represent file mode, mtime, or other file metadata. | ||
// From does not guarantee that a.File[i].Data contains no file marker lines. | ||
// See also warnings on Format. | ||
// In short, it is unwise to use txtar as a generic filesystem serialization mechanism. | ||
func From(fsys fs.FS) (*txtar.Archive, error) { | ||
ar := new(txtar.Archive) | ||
walkfn := func(path string, d fs.DirEntry, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if d.IsDir() { | ||
// Directories in txtar are implicit. | ||
return nil | ||
} | ||
data, err := fs.ReadFile(fsys, path) | ||
if err != nil { | ||
return err | ||
} | ||
ar.Files = append(ar.Files, txtar.File{Name: path, Data: data}) | ||
return nil | ||
} | ||
|
||
if err := fs.WalkDir(fsys, ".", walkfn); err != nil { | ||
return nil, err | ||
} | ||
return ar, nil | ||
} |