From e7d04fc1036260f8653989472972548807f4aa96 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 30 Jun 2017 13:37:29 +0100 Subject: [PATCH] Create fs.Directory interface and use it everywhere --- amazonclouddrive/amazonclouddrive.go | 8 +-- b2/b2.go | 12 +--- cmd/ls2/ls2.go | 2 +- cmd/lsjson/lsjson.go | 2 +- cmd/mount/dir.go | 2 +- cmd/mountlib/dir.go | 33 ++++----- cmd/mountlib/fs.go | 5 +- cmd/ncdu/scan/scan.go | 2 +- crypt/crypt.go | 16 ++--- drive/drive.go | 8 +-- dropbox/dropbox.go | 8 +-- fs/dir.go | 85 ++++++++++++++++++++++ fs/fs.go | 91 +++--------------------- fs/object.go | 50 +++++++++++++ fs/operations.go | 22 +++--- fs/operations_test.go | 4 +- fs/sync.go | 8 +-- fs/walk.go | 15 ++-- fs/walk_test.go | 4 +- fstest/fstest.go | 4 +- fstest/fstests/fstests.go | 6 +- ftp/ftp.go | 7 +- googlecloudstorage/googlecloudstorage.go | 12 +--- http/http.go | 7 +- http/http_internal_test.go | 8 +-- local/local.go | 7 +- onedrive/onedrive.go | 9 +-- s3/s3.go | 13 +--- sftp/sftp.go | 7 +- swift/swift.go | 12 +--- yandex/yandex.go | 7 +- 31 files changed, 226 insertions(+), 250 deletions(-) create mode 100644 fs/dir.go create mode 100644 fs/object.go diff --git a/amazonclouddrive/amazonclouddrive.go b/amazonclouddrive/amazonclouddrive.go index 3b21b5a18..3fd21909c 100644 --- a/amazonclouddrive/amazonclouddrive.go +++ b/amazonclouddrive/amazonclouddrive.go @@ -426,12 +426,8 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { case folderKind: // cache the directory ID for later lookups f.dirCache.Put(remote, *node.Id) - d := &fs.Dir{ - Name: remote, - Bytes: -1, - Count: -1, - } - d.When, _ = time.Parse(timeFormat, *node.ModifiedDate) // FIXME + when, _ := time.Parse(timeFormat, *node.ModifiedDate) // FIXME + d := fs.NewDir(remote, when) entries = append(entries, d) case fileKind: o, err := f.newObjectWithInfo(remote, node) diff --git a/b2/b2.go b/b2/b2.go index 5a060f451..a25f4f8f9 100644 --- a/b2/b2.go +++ b/b2/b2.go @@ -521,11 +521,7 @@ func (f *Fs) list(dir string, recurse bool, prefix string, limit int, hidden boo // Convert a list item into a DirEntry func (f *Fs) itemToDirEntry(remote string, object *api.File, isDirectory bool, last *string) (fs.DirEntry, error) { if isDirectory { - d := &fs.Dir{ - Name: remote, - Bytes: -1, - Count: -1, - } + d := fs.NewDir(remote, time.Time{}) return d, nil } if remote == *last { @@ -569,11 +565,7 @@ func (f *Fs) listBuckets(dir string) (entries fs.DirEntries, err error) { return nil, fs.ErrorListBucketRequired } err = f.listBucketsToFn(func(bucket *api.Bucket) error { - d := &fs.Dir{ - Name: bucket.Name, - Bytes: -1, - Count: -1, - } + d := fs.NewDir(bucket.Name, time.Time{}) entries = append(entries, d) return nil }) diff --git a/cmd/ls2/ls2.go b/cmd/ls2/ls2.go index 473933497..36536731e 100644 --- a/cmd/ls2/ls2.go +++ b/cmd/ls2/ls2.go @@ -32,7 +32,7 @@ var commandDefintion = &cobra.Command{ return nil } for _, entry := range entries { - _, isDir := entry.(*fs.Dir) + _, isDir := entry.(fs.Directory) if isDir { fmt.Println(entry.Remote() + "/") } else { diff --git a/cmd/lsjson/lsjson.go b/cmd/lsjson/lsjson.go index 79ff3ea26..b331c4b39 100644 --- a/cmd/lsjson/lsjson.go +++ b/cmd/lsjson/lsjson.go @@ -99,7 +99,7 @@ can be processed line by line as each item is written one to a line. item.ModTime = Timestamp(entry.ModTime()) } switch x := entry.(type) { - case *fs.Dir: + case fs.Directory: item.IsDir = true case fs.Object: item.IsDir = false diff --git a/cmd/mount/dir.go b/cmd/mount/dir.go index cba5ce8cb..0d1197ca0 100644 --- a/cmd/mount/dir.go +++ b/cmd/mount/dir.go @@ -118,7 +118,7 @@ func (d *Dir) ReadDirAll(ctx context.Context) (dirents []fuse.Dirent, err error) Type: fuse.DT_File, Name: path.Base(x.Remote()), } - case *fs.Dir: + case fs.Directory: dirent = fuse.Dirent{ // Inode FIXME ??? Type: fuse.DT_Dir, diff --git a/cmd/mountlib/dir.go b/cmd/mountlib/dir.go index 2f58adc6c..110f20e7f 100644 --- a/cmd/mountlib/dir.go +++ b/cmd/mountlib/dir.go @@ -32,12 +32,12 @@ type Dir struct { items map[string]*DirEntry } -func newDir(fsys *FS, f fs.Fs, fsDir *fs.Dir) *Dir { +func newDir(fsys *FS, f fs.Fs, fsDir fs.Directory) *Dir { return &Dir{ fsys: fsys, f: f, - path: fsDir.Name, - modTime: fsDir.When, + path: fsDir.Remote(), + modTime: fsDir.ModTime(), inode: NewInode(), } } @@ -113,10 +113,10 @@ func (d *Dir) walk(absPath string, fun func(*Dir)) { // // Reset the directory to new state, discarding all the objects and // reading everything again -func (d *Dir) rename(newParent *Dir, fsDir *fs.Dir) { +func (d *Dir) rename(newParent *Dir, fsDir fs.Directory) { d.ForgetAll() - d.path = fsDir.Name - d.modTime = fsDir.When + d.path = fsDir.Remote() + d.modTime = fsDir.ModTime() d.read = time.Time{} } @@ -180,12 +180,12 @@ func (d *Dir) readDir() error { Obj: obj, Node: nil, } - case *fs.Dir: + case fs.Directory: dir := item name := path.Base(dir.Remote()) // Use old dir value if it exists if oldItem, ok := oldItems[name]; ok { - if _, ok := oldItem.Obj.(*fs.Dir); ok { + if _, ok := oldItem.Obj.(fs.Directory); ok { d.items[name] = oldItem continue } @@ -262,7 +262,7 @@ func (d *Dir) lookupNode(leaf string) (item *DirEntry, err error) { switch x := item.Obj.(type) { case fs.Object: node, err = newFile(d, x, leaf), nil - case *fs.Dir: + case fs.Directory: node, err = newDir(d.fsys, d.f, x), nil default: err = errors.Errorf("unknown type %T", item) @@ -342,10 +342,7 @@ func (d *Dir) Mkdir(name string) (*Dir, error) { fs.Errorf(path, "Dir.Mkdir failed to create directory: %v", err) return nil, err } - fsDir := &fs.Dir{ - Name: path, - When: time.Now(), - } + fsDir := fs.NewDir(path, time.Now()) dir := newDir(d.fsys, d.f, fsDir) d.addObject(fsDir, dir) // fs.Debugf(path, "Dir.Mkdir OK") @@ -373,7 +370,7 @@ func (d *Dir) Remove(name string) error { fs.Errorf(path, "Dir.Remove file error: %v", err) return err } - case *fs.Dir: + case fs.Directory: // Check directory is empty first dir := item.Node.(*Dir) empty, err := dir.isEmpty() @@ -440,23 +437,21 @@ func (d *Dir) Rename(oldName, newName string, destDir *Dir) error { oldFile.rename(destDir, newObject) } } - case *fs.Dir: + case fs.Directory: doDirMove := d.f.Features().DirMove if doDirMove == nil { err := errors.Errorf("Fs %q can't rename directories (no DirMove)", d.f) fs.Errorf(oldPath, "Dir.Rename error: %v", err) return err } - srcRemote := x.Name + srcRemote := x.Remote() dstRemote := newPath err = doDirMove(d.f, srcRemote, dstRemote) if err != nil { fs.Errorf(oldPath, "Dir.Rename error: %v", err) return err } - newDir := new(fs.Dir) - *newDir = *x - newDir.Name = newPath + newDir := fs.NewDirCopy(x).SetRemote(newPath) newObj = newDir // Update the node with the new details if oldNode != nil { diff --git a/cmd/mountlib/fs.go b/cmd/mountlib/fs.go index d79b011ad..d41e77232 100644 --- a/cmd/mountlib/fs.go +++ b/cmd/mountlib/fs.go @@ -45,10 +45,7 @@ type FS struct { // NewFS creates a new filing system and root directory func NewFS(f fs.Fs) *FS { - fsDir := &fs.Dir{ - Name: "", - When: time.Now(), - } + fsDir := fs.NewDir("", time.Now()) fsys := &FS{ f: f, } diff --git a/cmd/ncdu/scan/scan.go b/cmd/ncdu/scan/scan.go index 2449c8d9d..3b6883821 100644 --- a/cmd/ncdu/scan/scan.go +++ b/cmd/ncdu/scan/scan.go @@ -80,7 +80,7 @@ func (d *Dir) Entries() fs.DirEntries { // Call with d.mu held func (d *Dir) getDir(i int) (subDir *Dir, isDir bool) { obj := d.entries[i] - dir, ok := obj.(*fs.Dir) + dir, ok := obj.(fs.Directory) if !ok { return nil, false } diff --git a/crypt/crypt.go b/crypt/crypt.go index 571a857bf..84e660105 100644 --- a/crypt/crypt.go +++ b/crypt/crypt.go @@ -157,8 +157,8 @@ func (f *Fs) add(entries *fs.DirEntries, obj fs.Object) { } // Encrypt an directory file name to entries. -func (f *Fs) addDir(entries *fs.DirEntries, dir *fs.Dir) { - remote := dir.Name +func (f *Fs) addDir(entries *fs.DirEntries, dir fs.Directory) { + remote := dir.Remote() decryptedRemote, err := f.cipher.DecryptDirName(remote) if err != nil { fs.Debugf(remote, "Skipping undecryptable dir name: %v", err) @@ -177,7 +177,7 @@ func (f *Fs) encryptEntries(entries fs.DirEntries) (newEntries fs.DirEntries, er switch x := entry.(type) { case fs.Object: f.add(&newEntries, x) - case *fs.Dir: + case fs.Directory: f.addDir(&newEntries, x) default: return nil, errors.Errorf("Unknown object type %T", entry) @@ -545,16 +545,16 @@ func (o *Object) Update(in io.Reader, src fs.ObjectInfo, options ...fs.OpenOptio } // newDir returns a dir with the Name decrypted -func (f *Fs) newDir(dir *fs.Dir) *fs.Dir { - new := *dir - remote := dir.Name +func (f *Fs) newDir(dir fs.Directory) fs.Directory { + new := fs.NewDirCopy(dir) + remote := dir.Remote() decryptedRemote, err := f.cipher.DecryptDirName(remote) if err != nil { fs.Debugf(remote, "Undecryptable dir name: %v", err) } else { - new.Name = decryptedRemote + new.SetRemote(decryptedRemote) } - return &new + return new } // ObjectInfo describes a wrapped fs.ObjectInfo for being the source diff --git a/drive/drive.go b/drive/drive.go index 5a02423d5..5e32f2bed 100644 --- a/drive/drive.go +++ b/drive/drive.go @@ -586,12 +586,8 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { case item.MimeType == driveFolderType: // cache the directory ID for later lookups f.dirCache.Put(remote, item.Id) - d := &fs.Dir{ - Name: remote, - Bytes: -1, - Count: -1, - } - d.When, _ = time.Parse(timeFormatIn, item.ModifiedDate) + when, _ := time.Parse(timeFormatIn, item.ModifiedDate) + d := fs.NewDir(remote, when) entries = append(entries, d) case item.Md5Checksum != "" || item.FileSize > 0: // If item has MD5 sum or a length it is a file stored on drive diff --git a/dropbox/dropbox.go b/dropbox/dropbox.go index da67fbeeb..b9dd180fc 100644 --- a/dropbox/dropbox.go +++ b/dropbox/dropbox.go @@ -392,13 +392,7 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { } name = strings.Trim(name, "/") if name != "" && name != dir { - d := &fs.Dir{ - Name: name, - When: time.Now(), - //When: folderInfo.ClientMtime, - //Bytes: folderInfo.Bytes, - //Count: -1, - } + d := fs.NewDir(name, time.Now()) entries = append(entries, d) } } else if fileInfo != nil { diff --git a/fs/dir.go b/fs/dir.go new file mode 100644 index 000000000..d95a0f5e8 --- /dev/null +++ b/fs/dir.go @@ -0,0 +1,85 @@ +package fs + +import "time" + +// Dir describes an unspecialized directory for directory/container/bucket lists +type Dir struct { + remote string // name of the directory + modTime time.Time // modification or creation time - IsZero for unknown + size int64 // size of directory and contents or -1 if unknown + items int64 // number of objects or -1 for unknown +} + +// NewDir creates an unspecialized Directory object +func NewDir(remote string, modTime time.Time) *Dir { + return &Dir{ + remote: remote, + modTime: modTime, + size: -1, + items: -1, + } +} + +// NewDirCopy creates an unspecialized copy of the Directory object passed in +func NewDirCopy(d Directory) *Dir { + return &Dir{ + remote: d.Remote(), + modTime: d.ModTime(), + size: d.Size(), + items: d.Items(), + } +} + +// String returns the name +func (d *Dir) String() string { + return d.remote +} + +// Remote returns the remote path +func (d *Dir) Remote() string { + return d.remote +} + +// SetRemote sets the remote +func (d *Dir) SetRemote(remote string) *Dir { + d.remote = remote + return d +} + +// ModTime returns the modification date of the file +// It should return a best guess if one isn't available +func (d *Dir) ModTime() time.Time { + if !d.modTime.IsZero() { + return d.modTime + } + return time.Now() +} + +// Size returns the size of the file +func (d *Dir) Size() int64 { + return d.size +} + +// SetSize sets the size of the directory +func (d *Dir) SetSize(size int64) *Dir { + d.size = size + return d +} + +// Items returns the count of items in this directory or this +// directory and subdirectories if known, -1 for unknown +func (d *Dir) Items() int64 { + return d.items +} + +// SetItems sets the number of items in the directory +func (d *Dir) SetItems(items int64) *Dir { + d.items = items + return d +} + +// Check interfaces +var ( + _ DirEntry = (*Dir)(nil) + _ Directory = (*Dir)(nil) +) diff --git a/fs/fs.go b/fs/fs.go index 4ba918190..5e385b3ba 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -209,6 +209,15 @@ type DirEntry interface { Size() int64 } +// Directory is a filesystem like directory provided by an Fs +type Directory interface { + DirEntry + + // Items returns the count of items in this directory or this + // directory and subdirectories if known, -1 for unknown + Items() int64 +} + // MimeTyper is an optional interface for Object type MimeTyper interface { // MimeType returns the content type of the Object if @@ -544,41 +553,6 @@ type ObjectPair struct { // ObjectPairChan is a channel of ObjectPair type ObjectPairChan chan ObjectPair -// Dir describes a directory for directory/container/bucket lists -type Dir struct { - Name string // name of the directory - When time.Time // modification or creation time - IsZero for unknown - Bytes int64 // size of directory and contents -1 for unknown - Count int64 // number of objects -1 for unknown -} - -// String returns the name -func (d *Dir) String() string { - return d.Name -} - -// Remote returns the remote path -func (d *Dir) Remote() string { - return d.Name -} - -// ModTime returns the modification date of the file -// It should return a best guess if one isn't available -func (d *Dir) ModTime() time.Time { - if !d.When.IsZero() { - return d.When - } - return time.Now() -} - -// Size returns the size of the file -func (d *Dir) Size() int64 { - return d.Bytes -} - -// Check interface -var _ DirEntry = (*Dir)(nil) - // Find looks for an Info object for the name passed in // // Services are looked up in the config file @@ -651,50 +625,3 @@ func CheckClose(c io.Closer, err *error) { *err = cerr } } - -// NewStaticObjectInfo returns a static ObjectInfo -// If hashes is nil and fs is not nil, the hash map will be replaced with -// empty hashes of the types supported by the fs. -func NewStaticObjectInfo(remote string, modTime time.Time, size int64, storable bool, hashes map[HashType]string, fs Info) ObjectInfo { - info := &staticObjectInfo{ - remote: remote, - modTime: modTime, - size: size, - storable: storable, - hashes: hashes, - fs: fs, - } - if fs != nil && hashes == nil { - set := fs.Hashes().Array() - info.hashes = make(map[HashType]string) - for _, ht := range set { - info.hashes[ht] = "" - } - } - return info -} - -type staticObjectInfo struct { - remote string - modTime time.Time - size int64 - storable bool - hashes map[HashType]string - fs Info -} - -func (i *staticObjectInfo) Fs() Info { return i.fs } -func (i *staticObjectInfo) Remote() string { return i.remote } -func (i *staticObjectInfo) String() string { return i.remote } -func (i *staticObjectInfo) ModTime() time.Time { return i.modTime } -func (i *staticObjectInfo) Size() int64 { return i.size } -func (i *staticObjectInfo) Storable() bool { return i.storable } -func (i *staticObjectInfo) Hash(h HashType) (string, error) { - if len(i.hashes) == 0 { - return "", ErrHashUnsupported - } - if hash, ok := i.hashes[h]; ok { - return hash, nil - } - return "", ErrHashUnsupported -} diff --git a/fs/object.go b/fs/object.go new file mode 100644 index 000000000..5cb64a3ac --- /dev/null +++ b/fs/object.go @@ -0,0 +1,50 @@ +package fs + +import "time" + +// NewStaticObjectInfo returns a static ObjectInfo +// If hashes is nil and fs is not nil, the hash map will be replaced with +// empty hashes of the types supported by the fs. +func NewStaticObjectInfo(remote string, modTime time.Time, size int64, storable bool, hashes map[HashType]string, fs Info) ObjectInfo { + info := &staticObjectInfo{ + remote: remote, + modTime: modTime, + size: size, + storable: storable, + hashes: hashes, + fs: fs, + } + if fs != nil && hashes == nil { + set := fs.Hashes().Array() + info.hashes = make(map[HashType]string) + for _, ht := range set { + info.hashes[ht] = "" + } + } + return info +} + +type staticObjectInfo struct { + remote string + modTime time.Time + size int64 + storable bool + hashes map[HashType]string + fs Info +} + +func (i *staticObjectInfo) Fs() Info { return i.fs } +func (i *staticObjectInfo) Remote() string { return i.remote } +func (i *staticObjectInfo) String() string { return i.remote } +func (i *staticObjectInfo) ModTime() time.Time { return i.modTime } +func (i *staticObjectInfo) Size() int64 { return i.size } +func (i *staticObjectInfo) Storable() bool { return i.storable } +func (i *staticObjectInfo) Hash(h HashType) (string, error) { + if len(i.hashes) == 0 { + return "", ErrHashUnsupported + } + if hash, ok := i.hashes[h]; ok { + return hash, nil + } + return "", ErrHashUnsupported +} diff --git a/fs/operations.go b/fs/operations.go index 2377484dd..a7b7c0640 100644 --- a/fs/operations.go +++ b/fs/operations.go @@ -566,20 +566,20 @@ func (ds DirEntries) ForObjectError(fn func(o Object) error) error { return nil } -// ForDir runs the function supplied on every object in the entries -func (ds DirEntries) ForDir(fn func(dir *Dir)) { +// ForDir runs the function supplied on every Directory in the entries +func (ds DirEntries) ForDir(fn func(dir Directory)) { for _, entry := range ds { - dir, ok := entry.(*Dir) + dir, ok := entry.(Directory) if ok { fn(dir) } } } -// ForDirError runs the function supplied on every object in the entries -func (ds DirEntries) ForDirError(fn func(dir *Dir) error) error { +// ForDirError runs the function supplied on every Directory in the entries +func (ds DirEntries) ForDirError(fn func(dir Directory) error) error { for _, entry := range ds { - dir, ok := entry.(*Dir) + dir, ok := entry.(Directory) if ok { err := fn(dir) if err != nil { @@ -617,7 +617,7 @@ func ListDirSorted(fs Fs, includeAll bool, dir string) (entries DirEntries, err } else { Debugf(x, "Excluded from sync (and deletion)") } - case *Dir: + case Directory: if Config.Filter.IncludeDirectory(x.Remote()) { newEntries = append(newEntries, entry) } else { @@ -1052,9 +1052,9 @@ func ListDir(f Fs, w io.Writer) error { // FIXME count errors and carry on for listing return err } - entries.ForDir(func(dir *Dir) { + entries.ForDir(func(dir Directory) { if dir != nil { - syncFprintf(w, "%12d %13s %9d %s\n", dir.Bytes, dir.When.Format("2006-01-02 15:04:05"), dir.Count, dir.Name) + syncFprintf(w, "%12d %13s %9d %s\n", dir.Size(), dir.ModTime().Format("2006-01-02 15:04:05"), dir.Items(), dir.Remote()) } }) return nil @@ -1478,9 +1478,9 @@ func Rmdirs(f Fs, dir string) error { } for _, entry := range entries { switch x := entry.(type) { - case *Dir: + case Directory: // add a new directory as empty - dir := x.Name + dir := x.Remote() _, found := dirEmpty[dir] if !found { dirEmpty[dir] = true diff --git a/fs/operations_test.go b/fs/operations_test.go index 4fa7c07e6..ca9774306 100644 --- a/fs/operations_test.go +++ b/fs/operations_test.go @@ -172,7 +172,7 @@ func NewRun(t *testing.T) *Run { if err != nil { t.Errorf("Error removing file %q: %v", x.Remote(), err) } - case *fs.Dir: + case fs.Directory: toDelete = append(toDelete, x.Remote()) } } @@ -959,7 +959,7 @@ func TestListDirSorted(t *testing.T) { name := item.Remote() switch item.(type) { case fs.Object: - case *fs.Dir: + case fs.Directory: name += "/" default: t.Fatalf("Unknown type %+v", item) diff --git a/fs/sync.go b/fs/sync.go index 61f676759..9ddd755c2 100644 --- a/fs/sync.go +++ b/fs/sync.go @@ -755,7 +755,7 @@ func (s *syncCopyMove) dstOnly(dst DirEntry, job listDirJob, jobs *[]listDirJob) default: panic(fmt.Sprintf("unexpected delete mode %d", s.deleteMode)) } - case *Dir: + case Directory: // Do the same thing to the entire contents of the directory if job.dstDepth > 0 { *jobs = append(*jobs, listDirJob{ @@ -784,7 +784,7 @@ func (s *syncCopyMove) srcOnly(src DirEntry, job listDirJob, jobs *[]listDirJob) // No need to check since doesn't exist s.toBeUploaded <- ObjectPair{x, nil} } - case *Dir: + case Directory: // Do the same thing to the entire contents of the directory if job.srcDepth > 0 { *jobs = append(*jobs, listDirJob{ @@ -814,9 +814,9 @@ func (s *syncCopyMove) transfer(dst, src DirEntry, job listDirJob, jobs *[]listD Errorf(dst, "%v", err) s.processError(err) } - case *Dir: + case Directory: // Do the same thing to the entire contents of the directory - _, ok := dst.(*Dir) + _, ok := dst.(Directory) if ok { if job.srcDepth > 0 && job.dstDepth > 0 { *jobs = append(*jobs, listDirJob{ diff --git a/fs/walk.go b/fs/walk.go index c3ada147e..7193c5590 100644 --- a/fs/walk.go +++ b/fs/walk.go @@ -9,6 +9,7 @@ import ( "sort" "strings" "sync" + "time" "github.com/pkg/errors" ) @@ -119,7 +120,7 @@ func walk(f Fs, path string, includeAll bool, maxLevel int, fn WalkFunc, listDir entries, err := listDir(f, includeAll, job.remote) var jobs []listJob if err == nil && job.depth != 0 { - entries.ForDir(func(dir *Dir) { + entries.ForDir(func(dir Directory) { // Recurse for the directory jobs = append(jobs, listJob{ remote: dir.Remote(), @@ -214,9 +215,7 @@ func (dt DirTree) checkParent(root, dirPath string) { return } } - dt[parentPath] = append(entries, &Dir{ - Name: dirPath, - }) + dt[parentPath] = append(entries, NewDir(dirPath, time.Now())) dt.checkParent(root, parentPath) } @@ -250,7 +249,7 @@ func (dt DirTree) String() string { fmt.Fprintf(out, "%s/\n", dir) for _, entry := range dt[dir] { flag := "" - if _, ok := entry.(*Dir); ok { + if _, ok := entry.(Directory); ok { flag = "/" } fmt.Fprintf(out, " %s%s\n", path.Base(entry.Remote()), flag) @@ -284,7 +283,7 @@ func walkRDirTree(f Fs, path string, includeAll bool, maxLevel int, listR ListRF } else { Debugf(x, "Excluded from sync (and deletion)") } - case *Dir: + case Directory: if includeAll || Config.Filter.IncludeDirectory(x.Remote()) { if maxLevel < 0 || slashes <= maxLevel-1 { if slashes == maxLevel-1 { @@ -359,7 +358,7 @@ func walkR(f Fs, path string, includeAll bool, maxLevel int, fn WalkFunc, listR } // WalkGetAll runs Walk getting all the results -func WalkGetAll(f Fs, path string, includeAll bool, maxLevel int) (objs []Object, dirs []*Dir, err error) { +func WalkGetAll(f Fs, path string, includeAll bool, maxLevel int) (objs []Object, dirs []Directory, err error) { err = Walk(f, path, includeAll, maxLevel, func(dirPath string, entries DirEntries, err error) error { if err != nil { return err @@ -368,7 +367,7 @@ func WalkGetAll(f Fs, path string, includeAll bool, maxLevel int) (objs []Object switch x := entry.(type) { case Object: objs = append(objs, x) - case *Dir: + case Directory: dirs = append(dirs, x) } } diff --git a/fs/walk_test.go b/fs/walk_test.go index 4a13f1fef..3c934d2d6 100644 --- a/fs/walk_test.go +++ b/fs/walk_test.go @@ -177,8 +177,8 @@ func (ls *listDirs) WalkR() { } } -func newDir(name string) *Dir { - return &Dir{Name: name} +func newDir(name string) Directory { + return NewDir(name, time.Time{}) } func testWalkEmpty(t *testing.T) *listDirs { diff --git a/fstest/fstest.go b/fstest/fstest.go index 9392f4091..5cbc164df 100644 --- a/fstest/fstest.go +++ b/fstest/fstest.go @@ -184,7 +184,7 @@ func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, expectedDirs is := NewItems(items) oldErrors := fs.Stats.GetErrors() var objs []fs.Object - var dirs []*fs.Dir + var dirs []fs.Directory var err error var retries = *ListRetries sleep := time.Second / 2 @@ -231,7 +231,7 @@ func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, expectedDirs if expectedDirs != nil && len(dirs) != 0 { actualDirs := []string{} for _, dir := range dirs { - actualDirs = append(actualDirs, dir.Name) + actualDirs = append(actualDirs, dir.Remote()) } sort.Strings(actualDirs) sort.Strings(expectedDirs) diff --git a/fstest/fstests/fstests.go b/fstest/fstests/fstests.go index 75b49f2b2..dea99276a 100644 --- a/fstest/fstests/fstests.go +++ b/fstest/fstests/fstests.go @@ -183,10 +183,10 @@ func winPath(s string) string { } // dirsToNames returns a sorted list of names -func dirsToNames(dirs []*fs.Dir) []string { +func dirsToNames(dirs []fs.Directory) []string { names := []string{} for _, dir := range dirs { - names = append(names, winPath(dir.Name)) + names = append(names, winPath(dir.Remote())) } sort.Strings(names) return names @@ -399,7 +399,7 @@ func TestFsListSubdir(t *testing.T) { fileName := file2.Path var err error var objs []fs.Object - var dirs []*fs.Dir + var dirs []fs.Directory for i := 0; i < 2; i++ { dir, _ := path.Split(fileName) dir = dir[:len(dir)-1] diff --git a/ftp/ftp.go b/ftp/ftp.go index cc661ab75..aedf572bb 100644 --- a/ftp/ftp.go +++ b/ftp/ftp.go @@ -324,12 +324,7 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { if object.Name == "." || object.Name == ".." { continue } - d := &fs.Dir{ - Name: newremote, - When: object.Time, - Bytes: 0, - Count: -1, - } + d := fs.NewDir(newremote, object.Time) entries = append(entries, d) default: o := &Object{ diff --git a/googlecloudstorage/googlecloudstorage.go b/googlecloudstorage/googlecloudstorage.go index 00a3d7d86..d8175a6af 100644 --- a/googlecloudstorage/googlecloudstorage.go +++ b/googlecloudstorage/googlecloudstorage.go @@ -363,11 +363,7 @@ func (f *Fs) list(dir string, recurse bool, fn listFn) error { // Convert a list item into a DirEntry func (f *Fs) itemToDirEntry(remote string, object *storage.Object, isDirectory bool) (fs.DirEntry, error) { if isDirectory { - d := &fs.Dir{ - Name: remote, - Bytes: int64(object.Size), - Count: 0, - } + d := fs.NewDir(remote, time.Time{}).SetSize(int64(object.Size)) return d, nil } o, err := f.newObjectWithInfo(remote, object) @@ -411,11 +407,7 @@ func (f *Fs) listBuckets(dir string) (entries fs.DirEntries, err error) { return nil, err } for _, bucket := range buckets.Items { - d := &fs.Dir{ - Name: bucket.Name, - Bytes: 0, - Count: 0, - } + d := fs.NewDir(bucket.Name, time.Time{}) entries = append(entries, d) } if buckets.NextPageToken == "" { diff --git a/http/http.go b/http/http.go index b10373b85..57bc53e95 100644 --- a/http/http.go +++ b/http/http.go @@ -307,12 +307,7 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { name = strings.TrimRight(name, "/") remote := path.Join(dir, name) if isDir { - dir := &fs.Dir{ - Name: remote, - When: timeUnset, - Bytes: 0, - Count: 0, - } + dir := fs.NewDir(remote, timeUnset) entries = append(entries, dir) } else { file := &Object{ diff --git a/http/http_internal_test.go b/http/http_internal_test.go index 1cc8c8fcb..8202fcd58 100644 --- a/http/http_internal_test.go +++ b/http/http_internal_test.go @@ -67,8 +67,8 @@ func testListRoot(t *testing.T, f fs.Fs) { e := entries[0] assert.Equal(t, "four", e.Remote()) - assert.Equal(t, int64(0), e.Size()) - _, ok := e.(*fs.Dir) + assert.Equal(t, int64(-1), e.Size()) + _, ok := e.(fs.Directory) assert.True(t, ok) e = entries[1] @@ -79,8 +79,8 @@ func testListRoot(t *testing.T, f fs.Fs) { e = entries[2] assert.Equal(t, "three", e.Remote()) - assert.Equal(t, int64(0), e.Size()) - _, ok = e.(*fs.Dir) + assert.Equal(t, int64(-1), e.Size()) + _, ok = e.(fs.Directory) assert.True(t, ok) e = entries[3] diff --git a/local/local.go b/local/local.go index 7536a8c42..1f0ca46d6 100644 --- a/local/local.go +++ b/local/local.go @@ -236,12 +236,7 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { // Ignore directories which are symlinks. These are junction points under windows which // are kind of a souped up symlink. Unix doesn't have directories which are symlinks. if (mode&os.ModeSymlink) == 0 && f.dev == readDevice(fi) { - d := &fs.Dir{ - Name: f.dirNames.Save(newRemote, f.cleanRemote(newRemote)), - When: fi.ModTime(), - Bytes: 0, - Count: 0, - } + d := fs.NewDir(f.dirNames.Save(newRemote, f.cleanRemote(newRemote)), fi.ModTime()) entries = append(entries, d) } } else { diff --git a/onedrive/onedrive.go b/onedrive/onedrive.go index c0ae1ea24..c47f46aca 100644 --- a/onedrive/onedrive.go +++ b/onedrive/onedrive.go @@ -423,14 +423,9 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { if info.Folder != nil { // cache the directory ID for later lookups f.dirCache.Put(remote, info.ID) - d := &fs.Dir{ - Name: remote, - Bytes: -1, - Count: -1, - When: time.Time(info.LastModifiedDateTime), - } + d := fs.NewDir(remote, time.Time(info.LastModifiedDateTime)) if info.Folder != nil { - d.Count = info.Folder.ChildCount + d.SetItems(info.Folder.ChildCount) } entries = append(entries, d) } else { diff --git a/s3/s3.go b/s3/s3.go index 5166b4437..9d322c7ae 100644 --- a/s3/s3.go +++ b/s3/s3.go @@ -555,11 +555,7 @@ func (f *Fs) itemToDirEntry(remote string, object *s3.Object, isDirectory bool) if object.Size != nil { size = *object.Size } - d := &fs.Dir{ - Name: remote, - Bytes: size, - Count: 0, - } + d := fs.NewDir(remote, time.Time{}).SetSize(size) return d, nil } o, err := f.newObjectWithInfo(remote, object) @@ -599,12 +595,7 @@ func (f *Fs) listBuckets(dir string) (entries fs.DirEntries, err error) { return nil, err } for _, bucket := range resp.Buckets { - d := &fs.Dir{ - Name: aws.StringValue(bucket.Name), - When: aws.TimeValue(bucket.CreationDate), - Bytes: -1, - Count: -1, - } + d := fs.NewDir(aws.StringValue(bucket.Name), aws.TimeValue(bucket.CreationDate)) entries = append(entries, d) } return entries, nil diff --git a/sftp/sftp.go b/sftp/sftp.go index a0f6254b6..aac216ab3 100644 --- a/sftp/sftp.go +++ b/sftp/sftp.go @@ -264,12 +264,7 @@ func (f *Fs) List(dir string) (entries fs.DirEntries, err error) { for _, info := range infos { remote := path.Join(dir, info.Name()) if info.IsDir() { - d := &fs.Dir{ - Name: remote, - When: info.ModTime(), - Bytes: -1, - Count: -1, - } + d := fs.NewDir(remote, info.ModTime()) entries = append(entries, d) } else { o := &Object{ diff --git a/swift/swift.go b/swift/swift.go index b362f72ba..f8ab4563f 100644 --- a/swift/swift.go +++ b/swift/swift.go @@ -321,11 +321,7 @@ type addEntryFn func(fs.DirEntry) error func (f *Fs) list(dir string, recurse bool, fn addEntryFn) error { return f.listContainerRoot(f.container, f.root, dir, recurse, func(remote string, object *swift.Object, isDirectory bool) (err error) { if isDirectory { - d := &fs.Dir{ - Name: remote, - Bytes: object.Bytes, - Count: 0, - } + d := fs.NewDir(remote, time.Time{}).SetSize(object.Bytes) err = fn(d) } else { o, err := f.newObjectWithInfo(remote, object) @@ -370,11 +366,7 @@ func (f *Fs) listContainers(dir string) (entries fs.DirEntries, err error) { return nil, errors.Wrap(err, "container listing failed") } for _, container := range containers { - d := &fs.Dir{ - Name: container.Name, - Bytes: container.Bytes, - Count: container.Count, - } + d := fs.NewDir(container.Name, time.Time{}).SetSize(container.Bytes).SetItems(container.Count) entries = append(entries, d) } return entries, nil diff --git a/yandex/yandex.go b/yandex/yandex.go index 9d30724dd..f71a480c6 100644 --- a/yandex/yandex.go +++ b/yandex/yandex.go @@ -174,12 +174,7 @@ func (f *Fs) itemToDirEntry(remote string, object *yandex.ResourceInfoResponse) if err != nil { return nil, errors.Wrap(err, "error parsing time in directory item") } - d := &fs.Dir{ - Name: remote, - When: t, - Bytes: int64(object.Size), - Count: -1, - } + d := fs.NewDir(remote, t).SetSize(int64(object.Size)) return d, nil case "file": o, err := f.newObjectWithInfo(remote, object)