Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Checks: >
-modernize-use-auto,
-modernize-use-nodiscard,
-modernize-use-trailing-return-type,
-readability-named-parameter
-readability-named-parameter,
-readability-redundant-declaration

CheckOptions:
readability-identifier-length.IgnoredParameterNames: 'ec|to'
Expand Down
37 changes: 30 additions & 7 deletions include/tmp/directory
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
#include <cstddef>
#include <filesystem>
#include <string_view>
#include <utility>

namespace tmp {

extern "C++" std::filesystem::path create_directory(std::string_view prefix);
extern "C++" void remove_all(const std::filesystem::path& path) noexcept;

/// tmp::directory is a smart handle that manages a temporary directory,
/// ensuring its recursive deletion when the handle goes out of scope
///
Expand Down Expand Up @@ -45,7 +49,29 @@ public:
/// @param prefix A prefix to add to the temporary directory name
/// @throws std::filesystem::filesystem_error if cannot create a directory
/// @throws std::invalid_argument if the prefix contains a directory separator
explicit directory(std::string_view prefix = "");
explicit directory(std::string_view prefix = "")
: pathobject(create_directory(prefix)) {}

directory(const directory&) = delete;
directory& operator=(const directory&) = delete;

/// Moves the ownership of the directory managed by `other` to a new handle
/// @note After the move, `other` is not associated with a directory
/// @param other Another directory that will be moved from
directory(directory&& other) noexcept
: pathobject(std::exchange(other.pathobject, std::filesystem::path())) {}

/// Deletes the managed temporary directory, then moves the ownership
/// of the directory managed by `other` to `this`
/// @note After the assignment, `other` is not associated with a directory
/// @param other Another directory that will be moved from
/// @returns `*this`
directory& operator=(directory&& other) noexcept {
remove_all(*this);
pathobject = std::exchange(other.pathobject, std::filesystem::path());

return *this;
}

/// Returns the path of this directory
/// @returns The full path of this directory
Expand All @@ -67,12 +93,9 @@ public:
}

/// Deletes this directory recursively
~directory() noexcept;

directory(directory&&) noexcept;
directory& operator=(directory&&) noexcept;
directory(const directory&) = delete;
directory& operator=(const directory&) = delete;
~directory() noexcept {
remove_all(*this);
}

private:
/// This directory path
Expand Down
7 changes: 6 additions & 1 deletion src/abi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#ifndef TMP_ABI_H
#define TMP_ABI_H

#include <filesystem>
#include <string_view>

#ifdef _WIN32
#define abi __declspec(dllexport)
#else
Expand All @@ -12,7 +15,9 @@

namespace tmp {

class abi directory;
std::filesystem::path abi create_directory(std::string_view prefix);
void abi remove_all(const std::filesystem::path& path) noexcept;

class abi file;
} // namespace tmp

Expand Down
36 changes: 9 additions & 27 deletions src/directory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@

namespace tmp {
namespace {

namespace fs = std::filesystem;

/// Checks if the given prefix is valid to attach to a temporary directory name
/// @param[in] prefix The prefix to check validity for
/// @param prefix The prefix to check validity for
/// @returns `true` if the prefix is valid, `false` otherwise
bool is_prefix_valid(const fs::path& prefix) {
// We also need to check that the prefix does not contain a root path
Expand All @@ -38,7 +37,7 @@ bool is_prefix_valid(const fs::path& prefix) {
#ifdef _WIN32
/// Creates a temporary path with the given prefix
/// @note prefix must be valid
/// @param[in] prefix A prefix to attach to the path pattern
/// @param prefix A prefix to attach to the path pattern
/// @returns A unique temporary path
fs::path make_path(std::string_view prefix) {
GUID guid;
Expand All @@ -61,9 +60,10 @@ fs::path make_path(std::string_view prefix) {
return path;
}
#endif
} // namespace

/// Creates a temporary directory in the current user's temporary directory
/// @param[in] prefix A prefix to attach to the temporary directory name
/// @param prefix A prefix to attach to the temporary directory name
/// @returns A path to the created temporary directory
/// @throws fs::filesystem_error if cannot create a temporary directory
/// @throws std::invalid_argument if the prefix contains a directory separator
Expand Down Expand Up @@ -95,38 +95,20 @@ fs::path create_directory(std::string_view prefix) {
return path;
}

/// Deletes a directory recursively, ignoring any errors
/// @param[in] directory The directory to delete
void remove_directory(const directory& directory) noexcept {
/// Deletes a path recursively, ignoring any errors
/// @param path The path to delete
void remove_all(const fs::path& path) noexcept {
try {
if (!directory.path().empty()) {
if (!path.empty()) {
// Calling the `std::error_code` overload of `fs::remove_all` should be
// more optimal here since it would not require creating
// a `fs::filesystem_error` message before we suppress the exception
std::error_code ec;
fs::remove_all(directory, ec);
fs::remove_all(path, ec);
}
} catch (...) {
// Do nothing: if we failed to delete the temporary directory,
// the system should do it later
}
}
} // namespace

directory::directory(std::string_view prefix)
: pathobject(create_directory(prefix)) {}

directory::~directory() noexcept {
remove_directory(*this);
}

directory::directory(directory&& other) noexcept
: pathobject(std::exchange(other.pathobject, fs::path())) {}

directory& directory::operator=(directory&& other) noexcept {
remove_directory(*this);

pathobject = std::exchange(other.pathobject, fs::path());
return *this;
}
} // namespace tmp