diff --git a/source/operations.d b/source/ops.d similarity index 100% rename from source/operations.d rename to source/ops.d diff --git a/source/run.d b/source/run.d index f735bed..ff276d8 100644 --- a/source/run.d +++ b/source/run.d @@ -10,7 +10,7 @@ */ import cli : OPTS, parseOpts; -import operations; +import ops; import util : createMissingFolders, err, log; import ver : COPY_TEXT, VER_TEXT; diff --git a/source/tests.d b/source/tests.d index 2394120..c9ff601 100644 --- a/source/tests.d +++ b/source/tests.d @@ -314,6 +314,42 @@ unittest { test_trash_dir.rmdirRecurse(); } +/** + Trash a directory containing a file from /tmp/ + On most systems (including mine) this is a separate tempfs so this test is + for cross-filesystem trashing +*/ +unittest { + string testdir = "/tmp/tdir"; + testdir.mkdir(); + string testfile = testdir ~ "/test.file"; + testfile.write("hello"); + scope (exit) + testdir.rmdirRecurse(); + assert(testdir.exists()); + assert(testfile.exists()); + auto tinfo = TrashFile(testdir, Clock.currTime()); + + // Trash the file + assert(mini(["-r", testdir]) == 0); + + assert(!testdir.exists()); + assert(!testfile.exists()); + assert(tinfo.file_path.exists()); + assert(tinfo.info_path.exists()); + + // Restore the file + assert(mini(["--restore", "tdir"]) == 0); + assert(testdir.exists()); + assert(testfile.exists()); + assert(!tinfo.file_path.exists()); + assert(!tinfo.info_path.exists()); + + // Cleanup + scope (success) + test_trash_dir.rmdirRecurse(); +} + /** Test trashing a file that does not have write permissions */ diff --git a/source/util.d b/source/util.d index 3d86a3e..9052d7b 100644 --- a/source/util.d +++ b/source/util.d @@ -9,7 +9,7 @@ import std.stdio : stderr, stdin, writef; import std.file; import std.format : format; import std.string : strip, toLower; -import std.path : buildNormalizedPath; +import std.path : buildNormalizedPath, relativePath, absolutePath; /** Prints a formatted error message to stderr with the program name at the @@ -89,7 +89,8 @@ void createMissingFolders() { /** Attempts to rename a file `src` to `tgt`, but if that fails with `EXDEV` then the `src` and `tgt` paths are on different devices and cannot be renamed - across. In that case perform a copy then remove + across. In that case perform a copy then remove, descending recursively if + needed. */ void renameOrCopy(in string src, in string tgt) { try { @@ -101,14 +102,17 @@ void renameOrCopy(in string src, in string tgt) { if (src.isFile) { src.copy(tgt); src.remove(); - } else if (src.isDir) { - foreach(string name; src.dirEntries(SpanMode.shallow)) { - name.renameOrCopy(buildNormalizedPath(tgt, name)); - } - src.rmdir(); - } else { - err("path was neither file or directory"); - } + } else if (src.isDir) { + tgt.mkdir(); + foreach (string name; src.dirEntries(SpanMode.shallow)) { + string rel = name.absolutePath().relativePath(src.absolutePath()); + string path = buildNormalizedPath(tgt, rel); + name.renameOrCopy(path); + } + src.rmdir(); + } else { + err("path was neither file or directory"); + } } }