forked from TrueCloudLab/rclone
DirTree.Prune: deletes several directories
This commit is contained in:
parent
7ae7080824
commit
421ba84e12
1 changed files with 81 additions and 0 deletions
81
fs/walk.go
81
fs/walk.go
|
@ -250,6 +250,87 @@ func (dt DirTree) Dirs() (dirNames []string) {
|
|||
return dirNames
|
||||
}
|
||||
|
||||
// Prune remove directories from a directory tree. dirNames contains
|
||||
// all directories to remove as keys, with true as values. dirNames
|
||||
// will be modified in the function.
|
||||
func (dt DirTree) Prune(dirNames map[string]bool) error {
|
||||
// We use map[string]bool to avoid recursion (and potential
|
||||
// stack exhaustion).
|
||||
|
||||
// First we need delete directories from their parents.
|
||||
for dName, remove := range dirNames {
|
||||
if !remove {
|
||||
// Currently all values should be
|
||||
// true, therefore this should not
|
||||
// happen. But this makes function
|
||||
// more predictable.
|
||||
Infof(dName, "Directory in the map for prune, but the value is false")
|
||||
continue
|
||||
}
|
||||
if dName == "" {
|
||||
// if dName is root, do nothing (no parent exist)
|
||||
continue
|
||||
}
|
||||
parent := parentDir(dName)
|
||||
// It may happen that dt does not have a dName key,
|
||||
// since directory was excluded based on a filter. In
|
||||
// such case the loop will be skipped.
|
||||
for i, entry := range dt[parent] {
|
||||
switch x := entry.(type) {
|
||||
case Directory:
|
||||
if x.Remote() == dName {
|
||||
// the slice is not sorted yet
|
||||
// to delete item
|
||||
// a) replace it with the last one
|
||||
dt[parent][i] = dt[parent][len(dt[parent])-1]
|
||||
// b) remove last
|
||||
dt[parent] = dt[parent][:len(dt[parent])-1]
|
||||
// we modify a slice within a loop, but we stop
|
||||
// iterating immediately
|
||||
break
|
||||
}
|
||||
case Object:
|
||||
// do nothing
|
||||
default:
|
||||
return errors.Errorf("unknown object type %T", entry)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for len(dirNames) > 0 {
|
||||
// According to golang specs, if new keys were added
|
||||
// during range iteration, they may be skipped.
|
||||
for dName, remove := range dirNames {
|
||||
if !remove {
|
||||
Infof(dName, "Directory in the map for prune, but the value is false")
|
||||
continue
|
||||
}
|
||||
// First, add all subdirectories to dirNames.
|
||||
|
||||
// It may happen that dt[dName] does not exist.
|
||||
// If so, the loop will be skipped.
|
||||
for _, entry := range dt[dName] {
|
||||
switch x := entry.(type) {
|
||||
case Directory:
|
||||
excludeDir := x.Remote()
|
||||
dirNames[excludeDir] = true
|
||||
case Object:
|
||||
// do nothing
|
||||
default:
|
||||
return errors.Errorf("unknown object type %T", entry)
|
||||
|
||||
}
|
||||
}
|
||||
// Then remove current directory from DirTree
|
||||
delete(dt, dName)
|
||||
// and from dirNames
|
||||
delete(dirNames, dName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String emits a simple representation of the DirTree
|
||||
func (dt DirTree) String() string {
|
||||
out := new(bytes.Buffer)
|
||||
|
|
Loading…
Reference in a new issue