Skip to content

Commit

Permalink
Move common methods to path
Browse files Browse the repository at this point in the history
  • Loading branch information
bugdea1er committed Jan 27, 2024
1 parent d4654da commit 55eb145
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 126 deletions.
50 changes: 6 additions & 44 deletions include/tmp/directory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,63 +40,25 @@ namespace tmp {
/// the tmp::directory object goes out of scope and the temporary directory is
/// deleted along with all of its contents.
class directory final : public path {
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);
}
}

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);
}

/// Deletes this directory recursively when the enclosing scope is exited
~directory() noexcept { this->remove(); }
~directory() noexcept = 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
56 changes: 10 additions & 46 deletions include/tmp/file.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,30 +63,6 @@ class file final : public path {
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 @@ -98,42 +74,30 @@ class file final : public path {
}

/// Deletes this file when the enclosing scope is exited
~file() noexcept { this->remove(); }
~file() noexcept = 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

/// 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->p, mode | std::ios::binary }
: std::ofstream { this->p, mode };
}
};

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

#include <filesystem>
#include <unistd.h>
#include <utility>

namespace tmp {

class path {
protected:
std::filesystem::path p; ///< This file path

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

public:
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->p = arg;
}

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

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

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

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

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

const std::filesystem::path* operator->() const noexcept {
return std::addressof(this->p);
}

friend bool operator==(const path& lhs, const path& rhs) noexcept {
return lhs.p == rhs.p;
}

friend bool operator!=(const path& lhs, const path& rhs) noexcept {
return lhs.p != rhs.p;
}
};

} // namespace tmp
29 changes: 14 additions & 15 deletions tests/directory_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@

#include <gtest/gtest.h>

using tmp::directory;
namespace fs = std::filesystem;

TEST(DirectoryTest, CreateDirectory) {
{
const auto tmpdir = directory("test");
const auto tmpdir = tmp::directory("test");
ASSERT_TRUE(fs::exists(tmpdir));
}
{
const auto tmpdir = directory();
const auto tmpdir = tmp::directory();
ASSERT_TRUE(fs::exists(tmpdir));
}
}

TEST(DirectoryTest, RemoveDirectory) {
fs::path path;
auto path = fs::path();
{
const auto tmpdir = directory("test");
const auto tmpdir = tmp::directory("test");
path = tmpdir;
ASSERT_TRUE(fs::exists(path));
}
Expand All @@ -30,36 +29,36 @@ TEST(DirectoryTest, RemoveDirectory) {
TEST(DirectoryTest, CreateMultiple) {
const auto path = "test";

const auto fst = directory(path);
const auto fst = tmp::directory(path);
ASSERT_TRUE(fs::exists(fst));

const auto snd = directory(path);
const auto snd = tmp::directory(path);
ASSERT_TRUE(fs::exists(snd));

EXPECT_NE(fst.path(), snd.path());
EXPECT_NE(fst, snd);
}

TEST(DirectoryTest, SubpathTest) {
const auto tmpdir = directory("test");
const auto tmpdir = tmp::directory("test");
const auto child = tmpdir / "child";

ASSERT_EQ(tmpdir, child.parent_path());
}

TEST(DirectoryTest, MoveConstruction) {
auto fst = directory("test");
auto fst = tmp::directory("test");
const auto snd = std::move(fst);

ASSERT_TRUE(fst.path().empty());
ASSERT_TRUE(fst->empty());
ASSERT_TRUE(fs::exists(snd));
}

TEST(DirectoryTest, MoveAssignment) {
auto fst = directory("test");
auto snd = directory("");
auto fst = tmp::directory("test");
auto snd = tmp::directory("");

const auto path1 = fst.path();
const auto path2 = snd.path();
const auto path1 = fs::path(fst);
const auto path2 = fs::path(snd);

fst = std::move(snd);

Expand Down
39 changes: 18 additions & 21 deletions tests/file_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@

#include <gtest/gtest.h>

using tmp::file;
namespace fs = std::filesystem;

TEST(FileTest, CreateFile) {
{
const auto tmpfile = file("test");
const auto tmpfile = tmp::file("test");
ASSERT_TRUE(fs::exists(tmpfile));
}
{
const auto tmpfile = file();
const auto tmpfile = tmp::file();
ASSERT_TRUE(fs::exists(tmpfile));
}
}

TEST(FileTest, RemoveDirectory) {
fs::path path;
auto path = fs::path();
{
const auto tmpfile = file("test");
const auto tmpfile = tmp::file("test");
path = tmpfile;
ASSERT_TRUE(fs::exists(path));
}
Expand All @@ -30,29 +29,29 @@ TEST(FileTest, RemoveDirectory) {
TEST(FileTest, CreateMultiple) {
const auto path = "test";

const auto fst = file(path);
const auto fst = tmp::file(path);
ASSERT_TRUE(fs::exists(fst));

const auto snd = file(path);
const auto snd = tmp::file(path);
ASSERT_TRUE(fs::exists(snd));

EXPECT_NE(fst.path(), snd.path());
EXPECT_NE(fst, snd);
}

TEST(FileTest, MoveConstruction) {
auto fst = file("test");
auto fst = tmp::file("test");
const auto snd = std::move(fst);

ASSERT_TRUE(fst.path().empty());
ASSERT_TRUE(fst->empty());
ASSERT_TRUE(fs::exists(snd));
}

TEST(FileTest, MoveAssignment) {
auto fst = file("test");
auto snd = file("");
auto fst = tmp::file("test");
auto snd = tmp::file("");

const auto path1 = fst.path();
const auto path2 = snd.path();
const auto path1 = fs::path(fst);
const auto path2 = fs::path(snd);

fst = std::move(snd);

Expand All @@ -63,23 +62,21 @@ TEST(FileTest, MoveAssignment) {
}

TEST(FileTest, Write) {
const auto tmpfile = file();
const auto tmpfile = tmp::file();
tmpfile.write("Hello");

std::ifstream stream(tmpfile.path());
std::ifstream stream(tmpfile);
std::string content(std::istreambuf_iterator<char>(stream), {});
ASSERT_EQ(content, "Hello");
}

TEST(FileTest, Append) {
const auto tmpfile = file();
tmpfile.write("Hello");
const auto tmpfile = tmp::file();

tmpfile.write("Hello");
tmpfile.append(", world!");

std::cout << tmpfile.path() << std::endl;

std::ifstream stream(tmpfile.path());
std::ifstream stream(tmpfile);
std::string content(std::istreambuf_iterator<char>(stream), {});
ASSERT_EQ(content, "Hello, world!");
}

0 comments on commit 55eb145

Please sign in to comment.