#pragma once #if !defined(MIJIN_VIRTUAL_FILESYSTEM_FILESYSTEM_HPP_INCLUDED) #define MIJIN_VIRTUAL_FILESYSTEM_FILESYSTEM_HPP_INCLUDED 1 #include #include #include #include #include #include #include "../container/optional.hpp" #include "../io/stream.hpp" #include "../internal/common.hpp" #include "../util/hash.hpp" namespace fs = std::filesystem; namespace mijin { // // public defines // // // public constants // // // public types // struct FileInfo { fs::path path; std::size_t size = 0; bool exists : 1 = false; bool isFolder : 1 = false; bool isSymlink : 1 = false; bool isSpecial : 1 = false; bool isHidden : 1 = false; }; // basically just a thin wrapper around adapter + path, for utility class PathReference { private: class FileSystemAdapter* adapter_ = nullptr; fs::path path_ = {}; public: PathReference() = default; PathReference(const PathReference&) = default; PathReference(PathReference&&) = default; PathReference(class FileSystemAdapter* adapter, fs::path path) MIJIN_NOEXCEPT : adapter_(adapter), path_(std::move(path)) {} PathReference& operator=(const PathReference&) = default; PathReference& operator=(PathReference&&) MIJIN_NOEXCEPT = default; auto operator<=>(const PathReference& other) const MIJIN_NOEXCEPT = default; [[nodiscard]] bool empty() const MIJIN_NOEXCEPT { return adapter_ == nullptr; } [[nodiscard]] class FileSystemAdapter* getAdapter() const noexcept { return adapter_; } [[nodiscard]] const fs::path& getPath() const MIJIN_NOEXCEPT { return path_; } [[nodiscard]] inline FileInfo getInfo() const; [[nodiscard]] inline Optional getNativePath() const; [[nodiscard]] inline StreamError open(FileOpenMode mode, std::unique_ptr& outStream) const; [[nodiscard]] PathReference operator/(const fs::path& more) const { return PathReference(adapter_, path_ / more); } }; class FileSystemAdapter { public: virtual ~FileSystemAdapter() = default; [[nodiscard]] virtual std::vector getRoots() = 0; [[nodiscard]] virtual fs::path getHomeFolder() = 0; [[nodiscard]] virtual std::vector listFiles(const fs::path& folder) = 0; [[nodiscard]] virtual FileInfo getFileInfo(const fs::path& file) = 0; [[nodiscard]] virtual Optional getNativePath(const fs::path& /* file */) { return NULL_OPTIONAL; } [[nodiscard]] virtual StreamError open(const fs::path& path, FileOpenMode mode, std::unique_ptr& outStream) = 0; [[nodiscard]] PathReference getPath(fs::path path) MIJIN_NOEXCEPT { return PathReference(this, std::move(path)); } }; class OSFileSystemAdapter : public FileSystemAdapter { public: std::vector getRoots() override; fs::path getHomeFolder() override; std::vector listFiles(const fs::path& folder) override; FileInfo getFileInfo(const fs::path& file) override; Optional getNativePath(const fs::path& file) override; StreamError open(const fs::path& path, FileOpenMode mode, std::unique_ptr& outStream) override; static OSFileSystemAdapter& getInstance(); }; // // public functions // inline FileInfo PathReference::getInfo() const { return adapter_->getFileInfo(path_); } Optional PathReference::getNativePath() const { return adapter_->getNativePath(path_); } inline StreamError PathReference::open(FileOpenMode mode, std::unique_ptr& outStream) const { return adapter_->open(path_, mode, outStream); } inline std::string formatFileType(const FileInfo& info) { if (info.isFolder) { return "Folder"; } if (info.isSpecial) { return "Special"; } return "File"; } inline std::string formatFileSize(std::size_t sizeInBytes) { static constexpr std::array suffixes = {"bytes", "KiB", "MiB", "GiB", "TiB"}; // enough? if (sizeInBytes == 0) { return "0 bytes"; } const std::size_t pos = std::min(static_cast(std::ceil(std::log2(sizeInBytes))) / 10, suffixes.size() - 1); std::stringstream oss; oss << std::setprecision(2) << std::fixed; if (pos == 0) { oss << sizeInBytes << " bytes"; } else { const float converted = static_cast(sizeInBytes) / std::pow(2.f, 10.f * static_cast(pos)); oss << converted << " " << suffixes[pos]; } return oss.str(); } } // namespace mijin template<> struct std::hash { std::size_t operator()(const mijin::PathReference& pathRef) const MIJIN_NOEXCEPT { std::size_t hash = 0; mijin::hashCombine(hash, pathRef.getAdapter()); mijin::hashCombine(hash, pathRef.getPath()); return hash; } }; #endif // !defined(MIJIN_VIRTUAL_FILESYSTEM_FILESYSTEM_HPP_INCLUDED)