github.com/artpar/rclone@v1.67.3/fs/list/list.go (about) 1 // Package list contains list functions 2 package list 3 4 import ( 5 "context" 6 "fmt" 7 "sort" 8 "strings" 9 10 "github.com/artpar/rclone/fs" 11 "github.com/artpar/rclone/fs/filter" 12 ) 13 14 // DirSorted reads Object and *Dir into entries for the given Fs. 15 // 16 // dir is the start directory, "" for root 17 // 18 // If includeAll is specified all files will be added, otherwise only 19 // files and directories passing the filter will be added. 20 // 21 // Files will be returned in sorted order 22 func DirSorted(ctx context.Context, f fs.Fs, includeAll bool, dir string) (entries fs.DirEntries, err error) { 23 // Get unfiltered entries from the fs 24 entries, err = f.List(ctx, dir) 25 if err != nil { 26 return nil, err 27 } 28 // This should happen only if exclude files lives in the 29 // starting directory, otherwise ListDirSorted should not be 30 // called. 31 fi := filter.GetConfig(ctx) 32 if !includeAll && fi.ListContainsExcludeFile(entries) { 33 fs.Debugf(dir, "Excluded") 34 return nil, nil 35 } 36 return filterAndSortDir(ctx, entries, includeAll, dir, fi.IncludeObject, fi.IncludeDirectory(ctx, f)) 37 } 38 39 // filter (if required) and check the entries, then sort them 40 func filterAndSortDir(ctx context.Context, entries fs.DirEntries, includeAll bool, dir string, 41 IncludeObject func(ctx context.Context, o fs.Object) bool, 42 IncludeDirectory func(remote string) (bool, error)) (newEntries fs.DirEntries, err error) { 43 newEntries = entries[:0] // in place filter 44 prefix := "" 45 if dir != "" { 46 prefix = dir + "/" 47 } 48 for _, entry := range entries { 49 ok := true 50 // check includes and types 51 switch x := entry.(type) { 52 case fs.Object: 53 // Make sure we don't delete excluded files if not required 54 if !includeAll && !IncludeObject(ctx, x) { 55 ok = false 56 fs.Debugf(x, "Excluded") 57 } 58 case fs.Directory: 59 if !includeAll { 60 include, err := IncludeDirectory(x.Remote()) 61 if err != nil { 62 return nil, err 63 } 64 if !include { 65 ok = false 66 fs.Debugf(x, "Excluded") 67 } 68 } 69 default: 70 return nil, fmt.Errorf("unknown object type %T", entry) 71 } 72 // check remote name belongs in this directory 73 remote := entry.Remote() 74 switch { 75 case !ok: 76 // ignore 77 case !strings.HasPrefix(remote, prefix): 78 ok = false 79 fs.Errorf(entry, "Entry doesn't belong in directory %q (too short) - ignoring", dir) 80 case remote == prefix: 81 ok = false 82 fs.Errorf(entry, "Entry doesn't belong in directory %q (same as directory) - ignoring", dir) 83 case strings.ContainsRune(remote[len(prefix):], '/'): 84 ok = false 85 fs.Errorf(entry, "Entry doesn't belong in directory %q (contains subdir) - ignoring", dir) 86 default: 87 // ok 88 } 89 if ok { 90 newEntries = append(newEntries, entry) 91 } 92 } 93 entries = newEntries 94 95 // Sort the directory entries by Remote 96 // 97 // We use a stable sort here just in case there are 98 // duplicates. Assuming the remote delivers the entries in a 99 // consistent order, this will give the best user experience 100 // in syncing as it will use the first entry for the sync 101 // comparison. 102 sort.Stable(entries) 103 return entries, nil 104 }