202 lines
5.4 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 <sstream>
#include <string>
#include <vector>
#include "../container/optional.hpp"
#include "../io/stream.hpp"
#include "../internal/common.hpp"
#include "../types/path.hpp"
#include "../util/hash.hpp"
namespace mijin
{
//
// public defines
//
//
// public constants
//
//
// public types
//
struct FileInfo
{
/// 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;
};
struct FolderEntry
{
Path path;
FileInfo info;
};
// basically just a thin wrapper around adapter + path, for utility
class PathReference
{
private:
class FileSystemAdapter* adapter_ = nullptr;
Path path_ = {};
public:
PathReference() = default;
PathReference(const PathReference&) = default;
PathReference(PathReference&&) = default;
PathReference(class FileSystemAdapter* adapter, 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 Path& getPath() const MIJIN_NOEXCEPT { return path_; }
[[nodiscard]] inline FileInfo getInfo() const;
[[nodiscard]] inline Optional<NativePath> getNativePath() const;
[[nodiscard]] inline StreamError open(FileOpenMode mode, std::unique_ptr<Stream>& outStream) const;
[[nodiscard]]
PathReference operator/(const Path& more) const
{
return PathReference(adapter_, path_ / more);
}
};
class FileSystemAdapter
{
public:
virtual ~FileSystemAdapter() = default;
[[nodiscard]] virtual std::vector<FolderEntry> listFiles(PathView folder) = 0;
[[nodiscard]] virtual FileInfo getFileInfo(PathView file) = 0;
[[nodiscard]] virtual Optional<NativePath> getNativePath(PathView /* file */) { return NULL_OPTIONAL; }
[[nodiscard]] virtual StreamError open(PathView path, FileOpenMode mode, std::unique_ptr<Stream>& outStream) = 0;
virtual void getAllPaths(PathView path, std::vector<PathReference>& outPaths) { outPaths.push_back(getPath(Path(path))); }
[[nodiscard]] PathReference getPath(Path path) MIJIN_NOEXCEPT { return PathReference(this, std::move(path)); }
[[nodiscard]] std::vector<PathReference> getAllPaths(PathView path)
{
std::vector<PathReference> paths;
getAllPaths(path, paths);
return paths;
}
};
class OSFileSystemAdapter : public FileSystemAdapter
{
public:
std::vector<FolderEntry> listFiles(PathView folder) override;
FileInfo getFileInfo(PathView file) override;
Optional<NativePath> getNativePath(PathView file) override;
StreamError open(PathView, FileOpenMode mode, std::unique_ptr<Stream>& outStream) override;
static OSFileSystemAdapter& getInstance();
};
//
// public functions
//
inline FileInfo PathReference::getInfo() const
{
return adapter_->getFileInfo(path_);
}
Optional<NativePath> 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)