// The file system, as packutils needs it: links never followed unless asked, every failure as a // `FileSystem` failure naming its cause and path, and paths as UTF-8 throughout (wide only inside // the Windows half). `fs_posix.cpp` and `fs_windows.cpp` implement it; each compiles to nothing // on the other platform. AI-authored (Claude). #pragma once #include "failure.hpp" #include #include #include #include #include #include namespace packutils::fs { #ifdef _WIN32 inline constexpr char separator = '\\'; #else inline constexpr char separator = '/'; #endif /// What is at a path, without following a link. enum class State : std::uint8_t { missing, link, directory, file, other }; /// A file's size and modification time, which say whether its source changed since a copy. struct Stamp { std::uint64_t size = 0; std::int64_t mtime_ns = 0; bool operator==(const Stamp &) const = default; }; struct Entry { std::string name; State kind = State::other; }; /// `a` joined with `b` by the platform's separator, as Zig's `std.fs.path.join` joins two parts. [[nodiscard]] std::string join(std::string_view a, std::string_view b); [[nodiscard]] Result state(const std::string &path); /// The file's bytes, or nothing when it does not exist. Larger than `limit` fails. [[nodiscard]] Result> read(const std::string &path, std::size_t limit); /// Replaces `path` through a flushed temp file beside it, creating the directory first. [[nodiscard]] Result write_atomic(const std::string &path, std::string_view bytes); [[nodiscard]] Result make_path(const std::string &path); /// One directory; an existing one fails with cause `PathAlreadyExists`. [[nodiscard]] Result make_dir(const std::string &path); [[nodiscard]] Result remove_file(const std::string &path); /// An empty directory, or (Windows) a junction itself. [[nodiscard]] Result remove_dir(const std::string &path); [[nodiscard]] Result rename(const std::string &from, const std::string &to); /// Replaces `to` with a copy of `from`. [[nodiscard]] Result copy_file(const std::string &from, const std::string &to); /// The real full path; a missing path fails. [[nodiscard]] Result real_path(const std::string &path); /// The entries of a directory, `.` and `..` left out. The directory itself is followed when it is /// a link only if `follow` says so. [[nodiscard]] Result> list(const std::string &path, bool follow); [[nodiscard]] Result stamp(const std::string &path); /// Whether `path` is a directory, not a link, with nothing in it. [[nodiscard]] bool is_empty_directory(const std::string &path); /// Where the link at `path` points, or nothing when it is not a link. [[nodiscard]] std::optional read_link(const std::string &path); /// Whether two link targets name the same place: on Windows without regard to separators or /// ASCII case, as the file system resolves them. [[nodiscard]] bool same_path(std::string_view a, std::string_view b); /// A directory link at `link` (which must not exist) to the directory `target`: a symlink, or on /// Windows a junction. False when the file system takes none; a refusal is never an error. [[nodiscard]] bool link_directory(const std::string &link, const std::string &target); /// An exclusive lock on a file, released when this goes away. class FileLock { public: FileLock() = default; FileLock(const FileLock &) = delete; FileLock &operator=(const FileLock &) = delete; FileLock(FileLock &&other) noexcept; FileLock &operator=(FileLock &&other) noexcept; ~FileLock(); /// The lock on `path` (created when missing), or nothing while another process holds it. [[nodiscard]] static Result> try_acquire(const std::string &path); private: #ifdef _WIN32 void *handle_ = nullptr; #else int fd_ = -1; #endif void release() noexcept; }; /// Sleeps for `milliseconds`. void pause(std::uint32_t milliseconds); /// A junction's reparse data (`REPARSE_DATA_BUFFER`, mount point variant): the substitute name /// (`\??\C:\...`) and the print name (`C:\...`), each followed by a NUL. Nothing when the names do /// not fit the 16-bit lengths. Pure, so it is tested on every platform. [[nodiscard]] std::optional> junction_buffer(const std::u16string &substitute, const std::u16string &print); } // namespace packutils::fs