Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move out common code #5

Merged
merged 12 commits into from
Jan 27, 2024
57 changes: 10 additions & 47 deletions include/tmp/directory.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#pragma once

#include <tmp/path.hpp>

#include <filesystem>
#include <string_view>
#include <system_error>
#include <unistd.h>

namespace tmp {
Expand Down Expand Up @@ -37,64 +38,26 @@ namespace tmp {
/// directory with the product identifier prefix. When the function returns,
/// the tmp::directory object goes out of scope and the temporary directory is
/// deleted along with all of its contents.
class directory {
std::filesystem::path p; ///< This directory path

/// Deletes this directory recursively
void remove() const noexcept {
if (!this->p.empty()) {
std::error_code ec;
std::filesystem::remove_all(this->p, ec);
}
}

class directory final : public path {
public:
/// Creates a unique temporary directory using the system's default location
/// for temporary files. If a prefix is provided to the constructor, the
/// directory is created in the path <temp dir>/prefix/. The prefix can be
/// a path consisting of multiple segments.
explicit directory(std::string_view prefix = "") {
const auto parent = std::filesystem::temp_directory_path() / prefix;
std::string arg = parent / "XXXXXX";

std::filesystem::create_directories(parent);
this->p = ::mkdtemp(arg.data());
}

/// Creates a directory from a moved @p other
directory(directory&& other) noexcept : p(std::move(other.p)) {
other.p.clear();
}

/// Deletes this directory recursively and assigns to it a moved @p other
directory& operator=(directory&& other) noexcept {
this->remove();
this->p = std::move(other.p);
other.p.clear();
return *this;
}

/// Returns this directory path
operator const std::filesystem::path&() const noexcept { return this->p; }

/// Returns this directory path
const std::filesystem::path& path() const noexcept { return this->p; }
explicit directory(std::string_view prefix = "") : path(prefix, mkdtemp) {}

/// Concatenates this directory path with a given @p source
std::filesystem::path operator/(std::string_view source) const {
return this->p / source;
}

/// Provides access to this directory path members
const std::filesystem::path* operator->() const noexcept {
return std::addressof(this->p);
return this->underlying / source;
}

/// Deletes this directory recursively when the enclosing scope is exited
~directory() noexcept { this->remove(); }
~directory() noexcept override = default;

directory(const directory&) = delete; ///< not copy-constructible
auto operator=(const directory&) = delete; ///< not copy-assignable
directory(directory&&) noexcept = default; ///< move-constructible
directory& operator=(directory&&) noexcept = default; ///< move-assignable
directory(const directory&) = delete; ///< not copy-constructible
auto operator=(const directory&) = delete; ///< not copy-assignable
};

} // namespace tmp
63 changes: 13 additions & 50 deletions include/tmp/file.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#pragma once

#include <filesystem>
#include <tmp/path.hpp>

#include <fstream>
#include <string_view>
#include <system_error>
#include <unistd.h>

namespace tmp {
Expand Down Expand Up @@ -45,7 +45,7 @@ namespace tmp {
/// The above example uses a tmp::file object to create a temporary file with
/// the product identifier prefix. When the function returns, the tmp::file
/// object goes out of scope and the temporary file is deleted.
class file {
class file final : public path {
public:
/// Creates a unique temporary binary file using the system's default
/// location for temporary files. If a prefix is provided to the
Expand All @@ -61,30 +61,6 @@ class file {
return file(prefix, /*binary=*/false);
}

/// Creates a file from a moved @p other
file(file&& other) noexcept : p(std::move(other.p)) {
other.p.clear();
}

/// Deletes this file and assigns to it a moved @p other
file& operator=(file&& other) noexcept {
this->remove();
this->p = std::move(other.p);
other.p.clear();
return *this;
}

/// Returns this file path
operator const std::filesystem::path&() const noexcept { return this->p; }

/// Returns this file path
const std::filesystem::path& path() const noexcept { return this->p; }

/// Provides access to this file path members
const std::filesystem::path* operator->() const noexcept {
return std::addressof(this->p);
}

/// Writes the given @p content to this file discarding any previous content
void write(std::string_view content) const {
this->stream(/*append=*/false) << content;
Expand All @@ -96,42 +72,29 @@ class file {
}

/// Deletes this file when the enclosing scope is exited
~file() noexcept { this->remove(); }
~file() noexcept override = default;

file(const file&) = delete; ///< not copy-constructible
auto operator=(const file&) = delete; ///< not copy-assignable
file(file&&) noexcept = default; ///< move-constructible
file& operator=(file&&) noexcept = default; ///< move-assignable
file(const file&) = delete; ///< not copy-constructible
auto operator=(const file&) = delete; ///< not copy-assignable

private:
std::filesystem::path p; ///< This file path
bool binary; ///< This file write mode
bool binary; ///< This file write mode

/// Creates a unique temporary file using the system's default location
/// for temporary files. If a prefix is provided to the constructor, the
/// directory is created in the path <temp dir>/prefix/. The prefix can be
/// a path consisting of multiple segments.
explicit file(std::string_view prefix, bool binary) : binary(binary) {
const auto parent = std::filesystem::temp_directory_path() / prefix;
std::string arg = parent / "XXXXXX";

std::filesystem::create_directories(parent);
::mkstemp(arg.data());
this->p = arg;
}
explicit file(std::string_view prefix, bool binary) : path(prefix, mkstemp),
binary(binary) {}

/// Returns a stream for this file
std::ofstream stream(bool append) const noexcept {
std::ios::openmode mode = append ? std::ios::app : std::ios::trunc;
return this->binary
? std::ofstream { this->path(), mode | std::ios::binary }
: std::ofstream { this->path(), mode };
}

/// Deletes this file
void remove() const noexcept {
if (!this->p.empty()) {
std::error_code ec;
std::filesystem::remove_all(this->p, ec);
}
? std::ofstream { this->underlying, mode | std::ios::binary }
: std::ofstream { this->underlying, mode };
}
};

Expand Down
70 changes: 70 additions & 0 deletions include/tmp/path.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#pragma once

#include <filesystem>
#include <string_view>
#include <system_error>
#include <utility>

namespace tmp {

/// The tmp::path class is a smart handle that owns and manages a temporary path
/// and disposes of it when this handle goes out of scope.
///
/// Subclass this and provide mktemp-like function to the constructor to create
/// temporary files and directories.
class path {
public:
/// Creates a path from a moved @p other
path(path&& other) noexcept : underlying(std::move(other.underlying)) {
other.underlying.clear();
}

/// Deletes this path and assigns to it a moved @p other
path& operator=(path&& other) noexcept {
this->remove();
this->underlying = std::move(other.underlying);
other.underlying.clear();
return *this;
}

path(const path&) = delete; ///< not copy-constructible
auto operator=(const path&) = delete; ///< not copy-assignable

/// Deletes this path recursively when the enclosing scope is exited
virtual ~path() noexcept { this->remove(); }

/// Returns the underlying path
operator const std::filesystem::path&() const noexcept {
return this->underlying;
}

/// Provides access to the underlying path members
const std::filesystem::path* operator->() const noexcept {
return std::addressof(this->underlying);
}

protected:
std::filesystem::path underlying; ///< This file path

/// Creates a unique temporary path using the given constructor function
template<typename C>
explicit path(std::string_view prefix, C constructor) {
const auto parent = std::filesystem::temp_directory_path() / prefix;
std::string arg = parent / "XXXXXX";

std::filesystem::create_directories(parent);
constructor(arg.data());
this->underlying = arg;
}

private:
/// Deletes this path recursively, ignoring any errors
void remove() const noexcept {
if (!this->underlying.empty()) {
std::error_code ec;
std::filesystem::remove_all(*this, ec);
}
}
};

} // namespace tmp
Loading