}
}
+void
+wipe_path(const std::string& path)
+{
+ if (!Stat::lstat(path)) {
+ return;
+ }
+ traverse(path, [](const std::string& p, bool is_dir) {
+ if (is_dir) {
+ if (rmdir(p.c_str()) != 0 && errno != ENOENT && errno != ESTALE) {
+ throw Error(fmt::format("failed to rmdir {}: {}", p, strerror(errno)));
+ }
+ } else if (unlink(p.c_str()) != 0 && errno != ENOENT && errno != ESTALE) {
+ throw Error(fmt::format("failed to unlink {}: {}", p, strerror(errno)));
+ }
+ });
+}
+
void
write_file(const std::string& path, const std::string& data, bool binary)
{
// Throws Error on error.
void traverse(const std::string& path, const TraverseVisitor& visitor);
+// Remove `path` (and its contents if it's a directory). A non-existing path is
+// not considered an error.
+//
+// Throws Error on error.
+void wipe_path(const std::string& path);
+
// Write file data from a string.
//
// Throws `Error` on error. The description contains the error message without
CHECK(visited[2] == "[d] traverse/dir-with-subdir-and-file");
}
}
+
+TEST_CASE("Util::wipe_path")
+{
+ SECTION("Wipe non-existing path")
+ {
+ CHECK_NOTHROW(Util::wipe_path("a"));
+ }
+
+ SECTION("Wipe file")
+ {
+ Util::write_file("a", "");
+ CHECK_NOTHROW(Util::wipe_path("a"));
+ CHECK(!Stat::stat("a"));
+ }
+
+ SECTION("Wipe directory")
+ {
+ REQUIRE(Util::create_dir("a/b"));
+ Util::write_file("a/1", "");
+ Util::write_file("a/b/1", "");
+ CHECK_NOTHROW(Util::wipe_path("a"));
+ CHECK(!Stat::stat("a"));
+ }
+
+ SECTION("Wipe bad path")
+ {
+#ifdef _WIN32
+ const char error[] = "failed to rmdir .: Permission denied";
+#else
+ const char error[] = "failed to rmdir .: Invalid argument";
+#endif
+ CHECK_THROWS_WITH(Util::wipe_path("."), error);
+ }
+}