mirror of
https://github.com/kopia/kopia.git
synced 2026-10-20 13:14:45 -04:00
fs: added Directory.Child() method to look up children by name without necessarily loading all of them
This commit is contained in:
1 parent
194680adfc
commit
503fa74be5
10 files changed
+123
No files matched your search
@@ -21,6 +21,15 @@ type directory struct {
|
||||
fs.Directory
|
||||
}
|
||||
|
||||
func (d *directory) Child(ctx context.Context, name string) (fs.Entry, error) {
|
||||
e, err := d.Directory.Child(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return wrapWithContext(e, d.ctx), nil
|
||||
}
|
||||
|
||||
func (d *directory) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
entries, err := d.ctx.cacher.Readdir(ctx, d.Directory)
|
||||
if err != nil {
|
||||
|
||||
+21
@@ -2,6 +2,7 @@ package fs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
@@ -40,10 +41,30 @@ type File interface {
|
||||
// Directory represents contents of a directory.
|
||||
type Directory interface {
|
||||
Entry
|
||||
Child(ctx context.Context, name string) (Entry, error)
|
||||
Readdir(ctx context.Context) (Entries, error)
|
||||
Summary() *DirectorySummary
|
||||
}
|
||||
|
||||
// ErrEntryNotFound is returned when an entry is not found.
|
||||
var ErrEntryNotFound = errors.New("entry not found")
|
||||
|
||||
// ReadDirAndFindChild reads all entries from a directory and returns one by name.
|
||||
// This is a convenience function that may be helpful in implementations of Directory.Child()
|
||||
func ReadDirAndFindChild(ctx context.Context, d Directory, name string) (Entry, error) {
|
||||
children, err := d.Readdir(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e := children.FindByName(name)
|
||||
if e == nil {
|
||||
return nil, ErrEntryNotFound
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// DirectorySummary represents summary information about a directory.
|
||||
type DirectorySummary struct {
|
||||
TotalFileSize int64 `json:"size"`
|
||||
|
||||
@@ -101,6 +101,21 @@ func (fsd *filesystemDirectory) Summary() *fs.DirectorySummary {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fsd *filesystemDirectory) Child(ctx context.Context, name string) (fs.Entry, error) {
|
||||
fullPath := fsd.fullPath()
|
||||
|
||||
st, err := os.Stat(filepath.Join(fullPath, name))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fs.ErrEntryNotFound
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(err, "unable to get child")
|
||||
}
|
||||
|
||||
return entryFromChildFileInfo(st, fullPath)
|
||||
}
|
||||
|
||||
func (fsd *filesystemDirectory) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
fullPath := fsd.fullPath()
|
||||
|
||||
|
||||
@@ -97,6 +97,47 @@ func TestFiles(t *testing.T) {
|
||||
t.Logf("e[%v] = %v %v %v", i, e.Name(), e.Size(), e.Mode())
|
||||
}
|
||||
}
|
||||
|
||||
verifyChild(t, dir)
|
||||
}
|
||||
|
||||
func verifyChild(t *testing.T, dir fs.Directory) {
|
||||
ctx := context.Background()
|
||||
|
||||
child, err := dir.Child(ctx, "f3")
|
||||
if err != nil {
|
||||
t.Errorf("child error: %v", err)
|
||||
}
|
||||
|
||||
if _, err = dir.Child(ctx, "f4"); err != fs.ErrEntryNotFound {
|
||||
t.Errorf("unexpected child error: %v", err)
|
||||
}
|
||||
|
||||
if got, want := child.Name(), "f3"; got != want {
|
||||
t.Errorf("unexpected child name: %v, want %v", got, want)
|
||||
}
|
||||
|
||||
if got, want := child.Size(), int64(3); got != want {
|
||||
t.Errorf("unexpected child size: %v, want %v", got, want)
|
||||
}
|
||||
|
||||
if _, err = fs.ReadDirAndFindChild(ctx, dir, "f4"); err != fs.ErrEntryNotFound {
|
||||
t.Errorf("unexpected child error: %v", err)
|
||||
}
|
||||
|
||||
// read child again, this time using ReadAndFindChild
|
||||
child2, err := fs.ReadDirAndFindChild(ctx, dir, "f3")
|
||||
if err != nil {
|
||||
t.Errorf("child2 error: %v", err)
|
||||
}
|
||||
|
||||
if got, want := child2.Name(), "f3"; got != want {
|
||||
t.Errorf("unexpected child2 name: %v, want %v", got, want)
|
||||
}
|
||||
|
||||
if got, want := child2.Size(), int64(3); got != want {
|
||||
t.Errorf("unexpected child2 size: %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func assertNoError(t *testing.T, err error) {
|
||||
|
||||
@@ -22,6 +22,19 @@ type loggingDirectory struct {
|
||||
fs.Directory
|
||||
}
|
||||
|
||||
func (ld *loggingDirectory) Child(ctx context.Context, name string) (fs.Entry, error) {
|
||||
t0 := time.Now()
|
||||
entry, err := ld.Directory.Child(ctx, name)
|
||||
dt := time.Since(t0)
|
||||
ld.options.printf(ld.options.prefix+"Child(%v) took %v and returned %v", ld.relativePath, dt, err)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return wrapWithOptions(entry, ld.options, ld.relativePath+"/"+entry.Name()), nil
|
||||
}
|
||||
|
||||
func (ld *loggingDirectory) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
t0 := time.Now()
|
||||
entries, err := ld.Directory.Readdir(ctx)
|
||||
|
||||
@@ -173,6 +173,10 @@ func (imd *Directory) FailReaddir(err error) {
|
||||
imd.readdirError = err
|
||||
}
|
||||
|
||||
func (imd *Directory) Child(ctx context.Context, name string) (fs.Entry, error) {
|
||||
return fs.ReadDirAndFindChild(ctx, imd, name)
|
||||
}
|
||||
|
||||
// Readdir gets the contents of a directory.
|
||||
func (imd *Directory) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
if imd.readdirError != nil {
|
||||
|
||||
@@ -47,6 +47,10 @@ func (s *repositoryAllSources) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *repositoryAllSources) Child(ctx context.Context, name string) (fs.Entry, error) {
|
||||
return fs.ReadDirAndFindChild(ctx, s, name)
|
||||
}
|
||||
|
||||
func (s *repositoryAllSources) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
srcs, err := snapshot.ListSources(ctx, s.rep)
|
||||
if err != nil {
|
||||
|
||||
@@ -79,6 +79,10 @@ func (rd *repositoryDirectory) Summary() *fs.DirectorySummary {
|
||||
return rd.summary
|
||||
}
|
||||
|
||||
func (rd *repositoryDirectory) Child(ctx context.Context, name string) (fs.Entry, error) {
|
||||
return fs.ReadDirAndFindChild(ctx, rd, name)
|
||||
}
|
||||
|
||||
func (rd *repositoryDirectory) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
r, err := rd.repo.Objects.Open(ctx, rd.metadata.ObjectID)
|
||||
if err != nil {
|
||||
|
||||
@@ -47,6 +47,10 @@ func (s *sourceDirectories) Owner() fs.OwnerInfo {
|
||||
return fs.OwnerInfo{}
|
||||
}
|
||||
|
||||
func (s *sourceDirectories) Child(ctx context.Context, name string) (fs.Entry, error) {
|
||||
return fs.ReadDirAndFindChild(ctx, s, name)
|
||||
}
|
||||
|
||||
func (s *sourceDirectories) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
sources, err := snapshot.ListSources(ctx, s.rep)
|
||||
if err != nil {
|
||||
@@ -67,3 +71,5 @@ func (s *sourceDirectories) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var _ fs.Directory = (*sourceDirectories)(nil)
|
||||
@@ -56,6 +56,10 @@ func (s *sourceSnapshots) Summary() *fs.DirectorySummary {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sourceSnapshots) Child(ctx context.Context, name string) (fs.Entry, error) {
|
||||
return fs.ReadDirAndFindChild(ctx, s, name)
|
||||
}
|
||||
|
||||
func (s *sourceSnapshots) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
manifests, err := snapshot.ListSnapshots(ctx, s.rep, s.src)
|
||||
if err != nil {
|
||||
@@ -94,3 +98,5 @@ func (s *sourceSnapshots) Readdir(ctx context.Context) (fs.Entries, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var _ fs.Directory = (*sourceSnapshots)(nil)
|
||||
Reference in new issue
Block a user