202 lines
5.7 KiB
C++
202 lines
5.7 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, use getKnownFolder(KnownFolder::USER_HOME) from platform/folders.hpp instead.")]]
|
|
[[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)
|