diff --git a/README.md b/README.md index 6b05ce9..df79842 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A wrapper for the "rm" command with soft-deletes, config-based deletion, debug i - `-i` Interactivly prompt before each deletion request - `-I` Prompt if deleting more than the interactive threshold of files (default 3) +- `-r`, `-R`, `--recursive` Recursively delete a directory of files - `--help` Display help information (without deletion) - `--version` Display version information (without deletion) @@ -24,7 +25,6 @@ A wrapper for the "rm" command with soft-deletes, config-based deletion, debug i ## Unsupported command line arguments -- `-r`, `-R`, `--recursive` Recursively delete a directory of files - `-d`, `--dir` Only delete empty directories - `-v`, `--verbose` Emit additional verbose information - `--version` Show version information diff --git a/src/patches/rm.go b/src/patches/rm.go index 512caa4..49184c9 100644 --- a/src/patches/rm.go +++ b/src/patches/rm.go @@ -158,27 +158,33 @@ func deletePaths(paths []string, config models.Config, arguments []string) { shouldHardDelete := isTmp || forceHardDelete || isConfigHardDelete && !isConfigSoftDelete && !forceSoftDelete - deletePath(absolutePath, shouldHardDelete, config, arguments) + deletePath(absolutePath, shouldHardDelete, config) } } -func deletePath(path string, hard bool, config models.Config, arguments []string) { +func deletePath(path string, hard bool, config models.Config) { if hard { hardDelete(path) } else { - // this function will return the default soft delete directory - // if the user has not specified one in their config file - softDeletePath := config.SoftDeleteDir() - softDelete(path, softDeletePath) + softDeleteStart(path, config) } } +func softDeleteStart(filePath string, config models.Config) { + // this function will return the default soft delete directory + // if the user has not specified one in their config file + softDeletePath := config.SoftDeleteDir() + softDelete(filePath, softDeletePath, "") +} + // by default, we want to delete files to /tmp/2rm // however, if the user has specified a different directory in their config file // we use that instead -func softDelete(filePath string, tempDir string) { - deletedTimestamp := time.Now().Format(time.RFC3339) - backupDirectory := tempDir + deletedTimestamp +func softDelete(filePath string, tempDir string, backupDirectory string) { + if backupDirectory == "" { + deletedTimestamp := time.Now().Format(time.RFC3339) + backupDirectory = tempDir + deletedTimestamp + } err := os.MkdirAll(backupDirectory, TRASH_DIR_PERMISSIONS) if err != nil { @@ -197,6 +203,26 @@ func softDelete(filePath string, tempDir string) { } } + isDirectory := util.IsDirectory(filePath) + if isDirectory { + // recursively delete all files in the directory + // before deleting the directory itself + directoryFiles := util.ListFiles(filePath) + + for _, file := range directoryFiles { + softDelete(file, tempDir, backupDirectory) + } + + // hard delete the directory itself + // because we have replicated the directory structure in the trash can + // we don't need to keep the original directory + // + // TODO: we should probably keep the original directory so that the + // same file permissions and other edge cases are carried across + hardDelete(filePath) + return + } + absoluteSrcPath := relativeToAbsolute(filePath) backupFileName := backupFileName(filePath) diff --git a/src/util/files.go b/src/util/files.go index 8013743..013181e 100644 --- a/src/util/files.go +++ b/src/util/files.go @@ -5,7 +5,7 @@ import ( "os" ) -func CopyFile(src, dst string) error { +func CopyFile(src string, dst string) error { sourceFile, err := os.Open(src) if err != nil { return err @@ -21,3 +21,27 @@ func CopyFile(src, dst string) error { _, err = io.Copy(destFile, sourceFile) return err } + +func IsDirectory(path string) bool { + fileInfo, err := os.Stat(path) + if err != nil { + return false + } + + return fileInfo.IsDir() +} + +func ListFiles(directory string) []string { + files, err := os.ReadDir(directory) + if err != nil { + return []string{} + } + + var fileNames []string + for _, file := range files { + relativeName := directory + "/" + file.Name() + fileNames = append(fileNames, relativeName) + } + + return fileNames +}