#include "breeze/Fs.hpp" #include #include #include #include #include #include #include "breeze/Error.hpp" #if defined(_WIN32) #ifndef NOMINMAX #define NOMINMAX #endif #include #elif defined(__APPLE__) #include #include #else #include #endif #if !defined(_WIN32) #include #endif namespace breeze::fs { std::optional 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 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& 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(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::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(); } }