202 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_VIRTUAL_FILESYSTEM_FILESYSTEM_HPP_INCLUDED)
 | |
| #define MIJIN_VIRTUAL_FILESYSTEM_FILESYSTEM_HPP_INCLUDED 1
 | |
| 
 | |
| #include <array>
 | |
| #include <cmath>
 | |
| #include <filesystem>
 | |
| #include <sstream>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| #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;
 | |
|     /// either file size in bytes, or number of entries if folder
 | |
|     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<fs::path> getNativePath() const;
 | |
|     [[nodiscard]] inline StreamError open(FileOpenMode mode, std::unique_ptr<Stream>& outStream) const;
 | |
| 
 | |
|     [[nodiscard]]
 | |
|     PathReference operator/(const fs::path& more) const
 | |
|     {
 | |
|         return PathReference(adapter_, path_ / more);
 | |
|     }
 | |
| };
 | |
| 
 | |
| class FileSystemAdapter
 | |
| {
 | |
| public:
 | |
|     virtual ~FileSystemAdapter() = default;
 | |
| 
 | |
|     [[deprecated("Will be removed ASAP")]]
 | |
|     [[nodiscard]] virtual fs::path getHomeFolder() { return {}; } // TODO: get rid of this ...
 | |
|     [[nodiscard]] virtual std::vector<FileInfo> listFiles(const fs::path& folder) = 0;
 | |
|     [[nodiscard]] virtual FileInfo getFileInfo(const fs::path& file) = 0;
 | |
|     [[nodiscard]] virtual Optional<fs::path> getNativePath(const fs::path& /* file */) { return NULL_OPTIONAL; }
 | |
|     [[nodiscard]] virtual StreamError open(const fs::path& path, FileOpenMode mode, std::unique_ptr<Stream>& outStream) = 0;
 | |
|     virtual void getAllPaths(const fs::path& path, std::vector<PathReference>& outPaths) { outPaths.push_back(getPath(path)); }
 | |
| 
 | |
|     [[nodiscard]] PathReference getPath(fs::path path) MIJIN_NOEXCEPT { return PathReference(this, std::move(path)); }
 | |
|     [[nodiscard]] std::vector<PathReference> getAllPaths(const fs::path& path)
 | |
|     {
 | |
|         std::vector<PathReference> paths;
 | |
|         getAllPaths(path, paths);
 | |
|         return paths;
 | |
|     }
 | |
| };
 | |
| 
 | |
| class OSFileSystemAdapter : public FileSystemAdapter
 | |
| {
 | |
| public:
 | |
|     fs::path getHomeFolder() override;
 | |
|     std::vector<FileInfo> listFiles(const fs::path& folder) override;
 | |
|     FileInfo getFileInfo(const fs::path& file) override;
 | |
|     Optional<fs::path> getNativePath(const fs::path& file) override;
 | |
|     StreamError open(const fs::path& path, FileOpenMode mode, std::unique_ptr<Stream>& outStream) override;
 | |
| 
 | |
|     static OSFileSystemAdapter& getInstance();
 | |
| };
 | |
| 
 | |
| //
 | |
| // public functions
 | |
| //
 | |
| 
 | |
| inline FileInfo PathReference::getInfo() const
 | |
| {
 | |
|     return adapter_->getFileInfo(path_);
 | |
| }
 | |
| 
 | |
| Optional<fs::path> PathReference::getNativePath() const
 | |
| {
 | |
|     return adapter_->getNativePath(path_);
 | |
| }
 | |
| 
 | |
| inline StreamError PathReference::open(FileOpenMode mode, std::unique_ptr<Stream>& 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::size_t>(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<float>(sizeInBytes) / std::pow(2.f, 10.f * static_cast<float>(pos));
 | |
|         oss << converted << " " << suffixes[pos];
 | |
|     }
 | |
|     return oss.str();
 | |
| }
 | |
| 
 | |
| namespace vfs_pipe
 | |
| {
 | |
| template<typename TReal, typename TAdapter = FileSystemAdapter>
 | |
| struct Builder
 | |
| {
 | |
|     using adapter_t = TAdapter;
 | |
| 
 | |
|     operator std::unique_ptr<FileSystemAdapter>()
 | |
|     {
 | |
|         return static_cast<TReal&>(*this).build();
 | |
|     }
 | |
| };
 | |
| 
 | |
| struct OSBuilder : Builder<OSBuilder, OSFileSystemAdapter>
 | |
| {
 | |
|     std::unique_ptr<OSFileSystemAdapter> build()
 | |
|     {
 | |
|         return std::make_unique<OSFileSystemAdapter>();
 | |
|     }
 | |
| };
 | |
| 
 | |
| [[nodiscard]]
 | |
| inline OSBuilder os() noexcept { return {}; }
 | |
| }
 | |
| } // namespace mijin
 | |
| 
 | |
| template<>
 | |
| struct std::hash<mijin::PathReference>
 | |
| {
 | |
|     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)
 |