Files
BreezeCLI/source/BreezeCore/src/Fs.cpp
T
2026-10-24 09:53:48 -03:00

212 lines
4.9 KiB
C++

#include "breeze/Fs.hpp"
#include <cstdlib>
#include <algorithm>
#include <fstream>
#include <random>
#include <sstream>
#include <system_error>
#include "breeze/Error.hpp"
#if defined(_WIN32)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
#include <unistd.h>
#else
#include <unistd.h>
#endif
#if !defined(_WIN32)
#include <sys/stat.h>
#endif
namespace breeze::fs
{
std::optional<std::string> tryReadText(const path& file)
{
std::ifstream in(file, std::ios::binary);
if (!in)
return std::nullopt;
std::ostringstream buffer;
buffer << in.rdbuf();
return buffer.str();
}
std::string readText(const path& file)
{
std::optional<std::string> text = tryReadText(file);
if (!text)
throw Error("cannot read '" + generic(file) + "'");
return *text;
}
void writeText(const path& file, const std::string& text)
{
std::error_code ec;
if (file.has_parent_path())
std::filesystem::create_directories(file.parent_path(), ec);
std::ofstream out(file, std::ios::binary | std::ios::trunc);
if (!out)
throw Error("cannot write '" + generic(file) + "'");
out << text;
if (!out)
throw Error("cannot write '" + generic(file) + "'");
}
void copyTree(const path& from, const path& to, const std::vector<std::string>& skipNames)
{
namespace sfs = std::filesystem;
std::error_code ec;
sfs::create_directories(to, ec);
for (const sfs::directory_entry& entry : sfs::directory_iterator(from))
{
std::string name = entry.path().filename().string();
if (std::find(skipNames.begin(), skipNames.end(), name) != skipNames.end())
continue;
path target = to / name;
if (entry.is_symlink())
{
sfs::copy_symlink(entry.path(), target, ec);
if (ec)
throw Error("cannot copy '" + generic(entry.path()) + "': " + ec.message());
}
else if (entry.is_directory())
{
copyTree(entry.path(), target, skipNames);
}
else
{
sfs::copy_file(entry.path(), target, sfs::copy_options::overwrite_existing, ec);
if (ec)
throw Error("cannot copy '" + generic(entry.path()) + "': " + ec.message());
}
}
}
void removeTree(const path& p)
{
std::error_code ec;
std::filesystem::remove_all(p, ec);
if (ec)
throw Error("cannot remove '" + generic(p) + "': " + ec.message());
}
path makeTempDir(const std::string& prefix)
{
std::random_device rd;
std::mt19937_64 gen(rd());
for (int attempt = 0; attempt < 100; ++attempt)
{
std::ostringstream name;
name << prefix << "-" << std::hex << gen();
path dir = std::filesystem::temp_directory_path() / name.str();
std::error_code ec;
if (std::filesystem::create_directory(dir, ec))
return dir;
}
throw Error("cannot create a temporary directory");
}
path homeDir()
{
#if defined(_WIN32)
if (const char* p = std::getenv("USERPROFILE"))
return path(p);
#endif
if (const char* p = std::getenv("HOME"))
return path(p);
throw Error("cannot tell where your home directory is (HOME is not set)");
}
path executablePath()
{
#if defined(_WIN32)
std::wstring buffer(32768, L'\0');
DWORD n = GetModuleFileNameW(nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
buffer.resize(n);
return path(buffer);
#elif defined(__APPLE__)
std::uint32_t size = 0;
_NSGetExecutablePath(nullptr, &size);
std::string buffer(size, '\0');
if (_NSGetExecutablePath(buffer.data(), &size) != 0)
throw Error("cannot tell where this program is");
buffer.resize(std::char_traits<char>::length(buffer.c_str()));
std::error_code ec;
path canonical = std::filesystem::canonical(buffer, ec);
return ec ? path(buffer) : canonical;
#else
std::error_code ec;
path p = std::filesystem::read_symlink("/proc/self/exe", ec);
if (ec)
throw Error("cannot tell where this program is: " + ec.message());
return p;
#endif
}
std::string hostPlatform()
{
#if defined(_WIN32)
return "windows";
#elif defined(__APPLE__)
return "macos";
#else
return "linux";
#endif
}
std::string hostArch()
{
#if defined(__x86_64__) || defined(_M_X64)
return "x64";
#elif defined(__aarch64__) || defined(_M_ARM64)
return "arm64";
#elif defined(__arm__) || defined(_M_ARM)
return "arm32";
#else
return "x86";
#endif
}
void makeExecutable(const path& file)
{
#if !defined(_WIN32)
std::error_code ec;
std::filesystem::permissions(file,
std::filesystem::perms::owner_exec |
std::filesystem::perms::group_exec |
std::filesystem::perms::others_exec,
std::filesystem::perm_options::add, ec);
if (ec)
throw Error("cannot make '" + generic(file) + "' executable: " + ec.message());
#else
(void)file;
#endif
}
bool isExecutable(const path& file)
{
std::error_code ec;
if (!std::filesystem::is_regular_file(file, ec))
return false;
#if defined(_WIN32)
return true;
#else
return ::access(file.c_str(), X_OK) == 0;
#endif
}
std::string generic(const path& p)
{
return p.generic_string();
}
}