blob: 59a74dd3943f4a19e4136a82074ef99bfa7fa408 [file] [log] [blame]
package gitiles
import (
"bytes"
"context"
"io"
"os"
"go.skia.org/infra/go/git"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/vfs"
)
// VFS returns a vfs.FS using Gitiles at the given revision.
func (r *Repo) VFS(ctx context.Context, ref string) (*FS, error) {
hash, err := r.ResolveRef(ctx, ref)
if err != nil {
return nil, skerr.Wrap(err)
}
return &FS{
repo: r,
hash: hash,
}, nil
}
// FS implements vfs.FS using Gitiles for a particular revision.
type FS struct {
repo *Repo
hash string
}
// Open implements vfs.FS.
func (fs *FS) Open(_ context.Context, name string) (vfs.File, error) {
return &File{
repo: fs.repo,
hash: fs.hash,
name: name,
}, nil
}
// Close implements vfs.FS.
func (fs *FS) Close(_ context.Context) error {
return nil
}
// Ensure that gitilesFS implements vfs.FS.
var _ vfs.FS = &FS{}
// File implements vfs.File using Gitiles for a particular revision.
type File struct {
repo *Repo
hash string
name string
// These are cached to avoid repeated requests.
cachedFileInfo os.FileInfo
cachedContents []byte
// reader is used for repeated calls to Read().
reader io.Reader
}
// get retrieves the contents and FileInfo for this File if they are not already
// cached.
func (f *File) get(ctx context.Context) (os.FileInfo, []byte, error) {
if f.cachedFileInfo == nil || f.cachedContents == nil {
fi, contents, err := f.repo.ReadObject(ctx, f.name, f.hash)
if err != nil {
return nil, nil, skerr.Wrap(err)
}
f.cachedFileInfo = fi
f.cachedContents = contents
}
return f.cachedFileInfo, f.cachedContents, nil
}
// Close implements vfs.File.
func (f *File) Close(_ context.Context) error {
return nil
}
// Read implements vfs.File.
func (f *File) Read(ctx context.Context, buf []byte) (int, error) {
if f.reader == nil {
_, contents, err := f.get(ctx)
if err != nil {
return 0, skerr.Wrap(err)
}
f.reader = bytes.NewReader(contents)
}
n, err := f.reader.Read(buf)
if err == io.EOF {
f.reader = nil
return n, err
}
return n, skerr.Wrap(err)
}
// Stat implements vfs.File.
func (f *File) Stat(ctx context.Context) (os.FileInfo, error) {
rv, _, err := f.get(ctx)
return rv, skerr.Wrap(err)
}
// ReadDir implements vfs.File.
func (f *File) ReadDir(ctx context.Context, n int) ([]os.FileInfo, error) {
_, contents, err := f.get(ctx)
if err != nil {
return nil, skerr.Wrap(err)
}
rv, err := git.ParseDir(contents)
if err != nil {
return nil, skerr.Wrap(err)
}
if n > 0 {
rv = rv[:n]
}
return rv, nil
}
// Ensure that gitilesFile implements vfs.File.
var _ vfs.File = &File{}