Skip to content
This repository has been archived by the owner on Jan 10, 2023. It is now read-only.

Commit

Permalink
Cleanups to how lists of files are returned from gdrive.
Browse files Browse the repository at this point in the history
Eliminated the Files structure: GetFilesInFolder and GetFilesUnderFolder
now return (sorted-by-filename) arrays of *File structures.

New function PartitionUniquesAndMultiples partitions these arrays into
arrays of unique files and the ones where multiple files have the same
pathname.

Greatly simplified GetFiles, which was unnecessarily complex.
  • Loading branch information
Matt Pharr committed Apr 7, 2015
1 parent 13bf038 commit d6085f3
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 135 deletions.
2 changes: 1 addition & 1 deletion download.go
Expand Up @@ -141,7 +141,7 @@ func syncHierarchyDown(driveBasePath string, localBasePath string, trustTimes bo
// We won't download files where there are multiple versions of the
// file with the same name on Drive. Issue warnings about any dupes
// here.
uniqueDriveFiles, dupes := filesOnDrive.GetSortedUnique()
uniqueDriveFiles, dupes := gdrive.PartitionUniquesAndMultiples(filesOnDrive)
nDownloadErrors := int32(len(dupes))
for _, f := range dupes {
fmt.Fprintf(os.Stderr, "skicka: %s: skipping download of duplicate "+
Expand Down
4 changes: 1 addition & 3 deletions du.go
Expand Up @@ -44,16 +44,14 @@ func du(args []string) int {
continue
}

sorted := files.GetSorted()

// folderSize keeps track of the size in bytes of each folder in
// the hierarchy.
folderSize := make(map[string]int64)
// dirNames tracks all of the names of directories seen so far.
var dirNames []string
totalSize := int64(0)

for _, f := range sorted {
for _, f := range files {
if f.IsFolder() {
dirNames = append(dirNames, f.Path)
} else {
Expand Down
2 changes: 1 addition & 1 deletion fsck.go
Expand Up @@ -72,7 +72,7 @@ mistake, it should be possible to salvage the file from the trash.
}

errs := 0
uniques, dupes := files.GetSortedUnique()
uniques, dupes := gdrive.PartitionUniquesAndMultiples(files)
for _, f := range uniques {
errs += checkFile(f)
}
Expand Down
116 changes: 32 additions & 84 deletions gdrive/gdrive.go
Expand Up @@ -744,9 +744,8 @@ func filesEqual(fa, fb *File) bool {
// (Note that File is used to represent both files and folders in Google
// Drive.)
func (gd *GDrive) GetFile(path string) (*File, error) {
path = canonicalPath(path)
files, ok := gd.pathToFile[path]
if !ok {
files := gd.GetFiles(path)
if len(files) == 0 {
return nil, ErrNotExist
} else if len(files) > 1 {
return nil, ErrMultipleFiles
Expand Down Expand Up @@ -774,101 +773,48 @@ func canonicalPath(path string) string {
// Note: an error is not returned if the file doesn't exist; the caller
// should detect that case by checking for a zero-length returned array.
func (gd *GDrive) GetFiles(path string) []*File {
path = canonicalPath(path)
var files []*File
if path == "." {
files = append(files, gd.pathToFile[path][0])
} else {
d := filepath.Dir(path)
for _, f := range gd.dirToFiles[d] {
if filepath.Base(f.Path) == filepath.Base(path) {
files = append(files, f)
}
}
}
return files
return gd.pathToFile[canonicalPath(path)]
}

// GetFilesInFolder returns a Files object representing the files in the
// given folder with the given name.
func (gd *GDrive) GetFilesInFolder(path string) (Files, error) {
files := newFiles()
dirFiles, ok := gd.dirToFiles[canonicalPath(path)]
if !ok {
return files, ErrNotExist
}

for _, f := range dirFiles {
files.add(f)
// GetFilesInFolder returns a *File array representing the files in the
// given folder with the given name. The files are sorted by pathname.
func (gd *GDrive) GetFilesInFolder(path string) ([]*File, error) {
if dirFiles, ok := gd.dirToFiles[canonicalPath(path)]; ok {
sort.Sort(byPath(dirFiles))
return dirFiles, nil
}
return files, nil
}

// Files represents a cached mapping between pathnames and files stored in
// Google Drive.
type Files struct {
files map[string][]*File
return nil, ErrNotExist
}

func newFiles() Files {
var f Files
f.files = make(map[string][]*File)
return f
}

// Add takes a pathname and a File and records that the given file lives at
// the given path in Google Drive.
func (f Files) add(file *File) {
if file.Path != canonicalPath(file.Path) {
panic(fmt.Sprintf("unclean path: %s", file.Path))
}

f.files[file.Path] = append(f.files[file.Path], file)
}

// GetSorted returns a array of File structures, one for each Google Drive
// file represented by the Files object. The array is sorted by the files
// pathnames.
func (f Files) GetSorted() []*File {
var files []*File
for _, fileArray := range f.files {
for _, f := range fileArray {
files = append(files, f)
}
}
// PartitionUniquesAndMultiples partitions all of the files by path name
// and then returns an array of File structures for the uniquely-named
// files and a map from path names to arrays of files for cases where more
// than one file has the same pathname.
func PartitionUniquesAndMultiples(files []*File) ([]*File, map[string][]*File) {
sort.Sort(byPath(files))
return files
}

// GetSortedUnique sorts all of the files by path name and then returns two
// arrays of File structures. The first includes all unique files--ones
// that only have a single file with that pathname on Drive. The second has
// all files where there are two or more files with that name on Drive.
func (f Files) GetSortedUnique() ([]*File, map[string][]*File) {
allFiles := f.GetSorted()

var files []*File
dupes := make(map[string][]*File)
for i, f := range allFiles {
var uniques []*File
multiples := make(map[string][]*File)
for i, f := range files {
// Non-duplicated files are different than their neighbors on both
// sides (if present).
if (i == 0 || f.Path != allFiles[i-1].Path) &&
(i == len(allFiles)-1 || f.Path != allFiles[i+1].Path) {
files = append(files, f)
if (i == 0 || f.Path != files[i-1].Path) &&
(i == len(files)-1 || f.Path != files[i+1].Path) {
uniques = append(uniques, f)
} else {
dupes[f.Path] = append(dupes[f.Path], f)
multiples[f.Path] = append(multiples[f.Path], f)
}
}

return files, dupes
return uniques, multiples
}

// GetFilesUnderFolder returns a Files object that represents all of the
// GetFilesUnderFolder returns an array of File pointers that represents all of the
// files stored in GoogleDrive under the given path. The 'includeBase'
// parameter indicates whether the file corresponding to the given path's
// folder should be included.
func (gd *GDrive) GetFilesUnderFolder(path string, includeBase bool) (Files, error) {
files := newFiles()
func (gd *GDrive) GetFilesUnderFolder(path string, includeBase bool) ([]*File, error) {
var files []*File

// Start by getting the file or files that correspond to the given
// path.
Expand All @@ -880,19 +826,21 @@ func (gd *GDrive) GetFilesUnderFolder(path string, includeBase bool) (Files, err
for _, f := range pathfiles {
if f.IsFolder() {
if includeBase {
files.add(f)
files = append(files, f)
}
gd.getFolderContentsRecursive(f, &files)
} else {
files.add(f)
files = append(files, f)
}
}

sort.Sort(byPath(files))
return files, nil
}

func (gd *GDrive) getFolderContentsRecursive(parentFolder *File, files *Files) {
func (gd *GDrive) getFolderContentsRecursive(parentFolder *File, files *[]*File) {
for _, f := range gd.dirToFiles[parentFolder.Path] {
files.add(f)
*files = append(*files, f)
if f.IsFolder() {
gd.getFolderContentsRecursive(f, files)
}
Expand Down
41 changes: 10 additions & 31 deletions gdrive/gdrive_test.go
Expand Up @@ -27,42 +27,21 @@
package gdrive

import (
"sort"
"testing"
)

func TestGetSorted(t *testing.T) {
f := newFiles()
f.add(&File{Path: "aaa"})
f.add(&File{Path: "c"})
f.add(&File{Path: "b"})
f.add(&File{Path: "aaa"})
f.add(&File{Path: "b"})
f.add(&File{Path: "z"})

files := f.GetSorted()

expected := []string{"aaa", "aaa", "b", "b", "c", "z"}
if len(files) != len(expected) {
t.Fatalf("Expected %d sorted files, got %v", len(expected), len(files))
}
for i := range expected {
if files[i].Path != expected[i] {
t.Fatalf("Expected \"%s\" for %d'th sorted file, got %v", expected[i],
i, files[i])
}
}
}

func TestGetSortedUnique(t *testing.T) {
f := newFiles()
f.add(&File{Path: "aaa"})
f.add(&File{Path: "c"})
f.add(&File{Path: "b"})
f.add(&File{Path: "aaa"})
f.add(&File{Path: "b"})
f.add(&File{Path: "z"})
var f []*File
f = append(f, &File{Path: "aaa"})
f = append(f, &File{Path: "c"})
f = append(f, &File{Path: "b"})
f = append(f, &File{Path: "aaa"})
f = append(f, &File{Path: "b"})
f = append(f, &File{Path: "z"})
sort.Sort(byPath(f))

files, dupes := f.GetSortedUnique()
files, dupes := PartitionUniquesAndMultiples(f)
if len(files) != 2 {
t.Fatalf("Expected 2 unique files, got %v: %v", len(files), files)
}
Expand Down
7 changes: 2 additions & 5 deletions ls.go
Expand Up @@ -115,7 +115,7 @@ func ls(args []string) int {
// all files under the folder, depending on whether the
// recursive option was specified.
includeBase := false
var files gdrive.Files
var files []*gdrive.File
var err error
if recursive {
files, err = gd.GetFilesUnderFolder(drivePath, includeBase)
Expand All @@ -129,10 +129,7 @@ func ls(args []string) int {
continue
}

// Sort the individual filenames returned.
sorted := files.GetSorted()

for _, f := range sorted {
for _, f := range files {
lsFile(f, recursive, long, longlong)
}

Expand Down
18 changes: 8 additions & 10 deletions upload.go
Expand Up @@ -71,14 +71,13 @@ func upload(args []string) int {
printErrorAndExit(err)
}

includeBase := true
driveFiles, err := gd.GetFilesUnderFolder(drivePath, includeBase)
if err != nil && err != gdrive.ErrNotExist {
printErrorAndExit(err)
f := gd.GetFiles(drivePath)
if len(f) > 1 {
printErrorAndExit(fmt.Errorf("%s: multiple files exist", drivePath))
}

syncStartTime = time.Now()
errs := syncHierarchyUp(localPath, drivePath, driveFiles, encrypt, trustTimes)
errs := syncHierarchyUp(localPath, drivePath, encrypt, trustTimes)
printFinalStats()

return errs
Expand Down Expand Up @@ -248,14 +247,13 @@ func uploadFileContents(localPath string, driveFile *gdrive.File, encrypt bool,
// Synchronize a local directory hierarchy with Google Drive.
// localPath is the file or directory to start with, driveRoot is
// the directory into which the file/directory will be sent
func syncHierarchyUp(localPath string, driveRoot string,
existingFiles gdrive.Files, encrypt bool, trustTimes bool) int {
func syncHierarchyUp(localPath string, driveRoot string, encrypt bool, trustTimes bool) int {
if encrypt && key == nil {
key = decryptEncryptionKey()
}

fileMappings, nUploadErrors := compileUploadFileTree(localPath, driveRoot,
existingFiles, encrypt, trustTimes)
encrypt, trustTimes)
if len(fileMappings) == 0 {
fmt.Fprintln(os.Stderr, "skicka: No files to be uploaded.")
return 0
Expand Down Expand Up @@ -570,8 +568,8 @@ func fileNeedsUpload(localPath, drivePath string, stat os.FileInfo,
return false, gd.UpdateModificationTime(driveFile, stat.ModTime())
}

func compileUploadFileTree(localPath, drivePath string, existingFiles gdrive.Files,
encrypt bool, trustTimes bool) ([]localToRemoteFileMapping, int32) {
func compileUploadFileTree(localPath, drivePath string,
encrypt, trustTimes bool) ([]localToRemoteFileMapping, int32) {
// Walk the local directory hierarchy starting at 'localPath' and build
// an array of files that may need to be synchronized.
var fileMappings []localToRemoteFileMapping
Expand Down

0 comments on commit d6085f3

Please sign in to comment.