intial commit
This commit is contained in:
128
source/mijin/virtual_filesystem/filesystem.cpp
Normal file
128
source/mijin/virtual_filesystem/filesystem.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
|
||||
#include "./filesystem.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// internal defines
|
||||
//
|
||||
|
||||
//
|
||||
// internal constants
|
||||
//
|
||||
|
||||
//
|
||||
// internal types
|
||||
//
|
||||
|
||||
//
|
||||
// internal variables
|
||||
//
|
||||
|
||||
//
|
||||
// internal functions
|
||||
//
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
std::vector<fs::path> OSFileSystemAdapter::getRoots()
|
||||
{
|
||||
return {
|
||||
"/" // TODO: other OSs
|
||||
};
|
||||
}
|
||||
|
||||
fs::path OSFileSystemAdapter::getHomeFolder()
|
||||
{
|
||||
return "/home/mewin"; // very TODO
|
||||
}
|
||||
|
||||
std::vector<FileInfo> OSFileSystemAdapter::listFiles(const fs::path& folder)
|
||||
{
|
||||
std::vector<FileInfo> entries;
|
||||
std::error_code err;
|
||||
fs::directory_iterator iterator(folder, fs::directory_options::skip_permission_denied, err);
|
||||
if (err) {
|
||||
return {}; // TODO: propagate?
|
||||
}
|
||||
for (const fs::directory_entry& entry : iterator)
|
||||
{
|
||||
FileInfo& info = entries.emplace_back();
|
||||
info.path = entry.path();
|
||||
info.exists = true;
|
||||
info.isFolder = entry.is_directory(err);
|
||||
info.isSymlink = entry.is_symlink(err);
|
||||
info.isSpecial = !info.isFolder && !entry.is_regular_file(err);
|
||||
info.isHidden = info.path.filename().string().starts_with('.'); // at least for Linux
|
||||
if (info.isFolder) {
|
||||
try {
|
||||
info.size = std::distance(fs::directory_iterator(info.path), fs::directory_iterator());
|
||||
}
|
||||
catch(std::runtime_error&) {
|
||||
info.size = 0;
|
||||
}
|
||||
}
|
||||
else if (!info.isSpecial)
|
||||
{
|
||||
info.size = entry.file_size(err);
|
||||
if (err) {
|
||||
info.size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
FileInfo OSFileSystemAdapter::getFileInfo(const fs::path& file)
|
||||
{
|
||||
FileInfo info = {};
|
||||
std::error_code err;
|
||||
info.path = file;
|
||||
info.exists = fs::exists(file, err);
|
||||
if (info.exists)
|
||||
{
|
||||
info.isFolder = fs::is_directory(file, err);
|
||||
info.isSymlink = fs::is_symlink(file, err);
|
||||
info.isSpecial = !info.isFolder && !fs::is_regular_file(file, err);
|
||||
info.isHidden = info.path.filename().string().starts_with('.'); // at least for Linux
|
||||
if (info.isFolder) {
|
||||
try {
|
||||
info.size = std::distance(fs::directory_iterator(info.path), fs::directory_iterator());
|
||||
}
|
||||
catch(std::runtime_error&) {
|
||||
info.size = 0;
|
||||
}
|
||||
}
|
||||
else if (!info.isSpecial)
|
||||
{
|
||||
info.size = fs::file_size(file, err);
|
||||
if (err) {
|
||||
info.size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
StreamError OSFileSystemAdapter::open(const fs::path& path, FileOpenMode mode, std::unique_ptr<Stream>& outStream)
|
||||
{
|
||||
const std::string pathStr = path.string();
|
||||
auto stream = std::make_unique<FileStream>();
|
||||
const StreamError error = stream->open(pathStr.c_str(), mode);
|
||||
if (error != StreamError::SUCCESS) {
|
||||
return error;
|
||||
}
|
||||
outStream = std::move(stream);
|
||||
return StreamError::SUCCESS;
|
||||
}
|
||||
|
||||
OSFileSystemAdapter& OSFileSystemAdapter::getInstance() // static
|
||||
{
|
||||
static OSFileSystemAdapter instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
105
source/mijin/virtual_filesystem/filesystem.hpp
Normal file
105
source/mijin/virtual_filesystem/filesystem.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
#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 "../io/stream.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;
|
||||
};
|
||||
|
||||
class FileSystemAdapter
|
||||
{
|
||||
public:
|
||||
virtual ~FileSystemAdapter() = default;
|
||||
|
||||
[[nodiscard]] virtual std::vector<fs::path> getRoots() = 0;
|
||||
[[nodiscard]] virtual fs::path getHomeFolder() = 0;
|
||||
[[nodiscard]] virtual std::vector<FileInfo> listFiles(const fs::path& folder) = 0;
|
||||
[[nodiscard]] virtual FileInfo getFileInfo(const fs::path& file) = 0;
|
||||
[[nodiscard]] virtual StreamError open(const fs::path& path, FileOpenMode mode, std::unique_ptr<Stream>& outStream) = 0;
|
||||
};
|
||||
|
||||
class OSFileSystemAdapter : public FileSystemAdapter
|
||||
{
|
||||
public:
|
||||
std::vector<fs::path> getRoots() override;
|
||||
fs::path getHomeFolder() override;
|
||||
std::vector<FileInfo> listFiles(const fs::path& folder) override;
|
||||
FileInfo getFileInfo(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 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 mijin
|
||||
|
||||
#endif // !defined(MIJIN_VIRTUAL_FILESYSTEM_FILESYSTEM_HPP_INCLUDED)
|
||||
87
source/mijin/virtual_filesystem/relative.hpp
Normal file
87
source/mijin/virtual_filesystem/relative.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_VIRTUAL_FILESYSTEM_RELATIVE_HPP_INCLUDED)
|
||||
#define MIJIN_VIRTUAL_FILESYSTEM_RELATIVE_HPP_INCLUDED 1
|
||||
|
||||
#include "./filesystem.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
template<typename TWrapped>
|
||||
class RelativeFileSystemAdapter : public FileSystemAdapter
|
||||
{
|
||||
private:
|
||||
TWrapped wrapped_;
|
||||
fs::path root_;
|
||||
public:
|
||||
template<typename... TArgs>
|
||||
explicit RelativeFileSystemAdapter(fs::path root, TArgs&&... args)
|
||||
: wrapped_(std::forward<TArgs>(args)...), root_(std::move(root)) {}
|
||||
RelativeFileSystemAdapter(const RelativeFileSystemAdapter&) = default;
|
||||
RelativeFileSystemAdapter(RelativeFileSystemAdapter&&) noexcept = default;
|
||||
|
||||
RelativeFileSystemAdapter& operator=(const RelativeFileSystemAdapter&) = default;
|
||||
RelativeFileSystemAdapter& operator=(RelativeFileSystemAdapter&&) noexcept = default;
|
||||
|
||||
std::vector<fs::path> getRoots() override;
|
||||
fs::path getHomeFolder() override;
|
||||
std::vector<FileInfo> listFiles(const fs::path& folder) override;
|
||||
FileInfo getFileInfo(const fs::path& file) override;
|
||||
StreamError open(const fs::path& path, FileOpenMode mode, std::unique_ptr<Stream>& outStream) override;
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
template<typename TWrapped>
|
||||
std::vector<fs::path> RelativeFileSystemAdapter<TWrapped>::getRoots()
|
||||
{
|
||||
return { root_ };
|
||||
}
|
||||
|
||||
template<typename TWrapped>
|
||||
fs::path RelativeFileSystemAdapter<TWrapped>::getHomeFolder()
|
||||
{
|
||||
return root_;
|
||||
}
|
||||
|
||||
template<typename TWrapped>
|
||||
std::vector<FileInfo> RelativeFileSystemAdapter<TWrapped>::listFiles(const fs::path& folder)
|
||||
{
|
||||
std::vector<FileInfo> result = wrapped_.listFiles(root_ / folder);
|
||||
for (FileInfo& fileInfo : result) {
|
||||
fileInfo.path = "/" / fileInfo.path.lexically_relative(root_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename TWrapped>
|
||||
FileInfo RelativeFileSystemAdapter<TWrapped>::getFileInfo(const fs::path& file)
|
||||
{
|
||||
return wrapped_.getFileInfo(root_ / file);
|
||||
}
|
||||
|
||||
template<typename TWrapped>
|
||||
StreamError RelativeFileSystemAdapter<TWrapped>::open(const fs::path& path, FileOpenMode mode, std::unique_ptr<Stream>& outStream)
|
||||
{
|
||||
return wrapped_.open(root_ / path, mode, outStream);
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_VIRTUAL_FILESYSTEM_RELATIVE_HPP_INCLUDED)
|
||||
105
source/mijin/virtual_filesystem/stacked.cpp
Normal file
105
source/mijin/virtual_filesystem/stacked.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
#include "./stacked.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// internal defines
|
||||
//
|
||||
|
||||
//
|
||||
// internal constants
|
||||
//
|
||||
|
||||
//
|
||||
// internal types
|
||||
//
|
||||
|
||||
//
|
||||
// internal variables
|
||||
//
|
||||
|
||||
//
|
||||
// internal functions
|
||||
//
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
std::vector<fs::path> StackedFileSystemAdapter::getRoots()
|
||||
{
|
||||
std::vector<fs::path> roots;
|
||||
|
||||
for (auto& adapter : adapters_)
|
||||
{
|
||||
for (const fs::path& root : adapter->getRoots())
|
||||
{
|
||||
auto it = std::find(roots.begin(), roots.end(), root);
|
||||
if (it == roots.end()) {
|
||||
roots.push_back(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
fs::path StackedFileSystemAdapter::getHomeFolder()
|
||||
{
|
||||
if (adapters_.empty()) {
|
||||
return fs::path();
|
||||
}
|
||||
return adapters_.front()->getHomeFolder();
|
||||
}
|
||||
|
||||
std::vector<FileInfo> StackedFileSystemAdapter::listFiles(const fs::path& folder)
|
||||
{
|
||||
std::vector<FileInfo> files;
|
||||
|
||||
for (auto& adapter : adapters_)
|
||||
{
|
||||
for (const FileInfo& file : adapter->listFiles(folder))
|
||||
{
|
||||
auto it = std::find_if(files.begin(), files.end(), [&](const FileInfo& existing)
|
||||
{
|
||||
return existing.path == file.path;
|
||||
});
|
||||
if (it != files.end()) {
|
||||
files.push_back(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
FileInfo StackedFileSystemAdapter::getFileInfo(const fs::path& file)
|
||||
{
|
||||
for (auto& adapter : adapters_)
|
||||
{
|
||||
FileInfo fileInfo = adapter->getFileInfo(file);
|
||||
if (fileInfo.exists) {
|
||||
return fileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StreamError StackedFileSystemAdapter::open(const fs::path& path, FileOpenMode mode, std::unique_ptr<Stream>& outStream)
|
||||
{
|
||||
for (auto& adapter : adapters_)
|
||||
{
|
||||
FileInfo fileInfo = adapter->getFileInfo(path);
|
||||
if (fileInfo.exists) {
|
||||
return adapter->open(path, mode, outStream);
|
||||
}
|
||||
}
|
||||
return StreamError::IO_ERROR;
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
52
source/mijin/virtual_filesystem/stacked.hpp
Normal file
52
source/mijin/virtual_filesystem/stacked.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_VIRTUAL_FILESYSTEM_STACKED_HPP_INCLUDED)
|
||||
#define MIJIN_VIRTUAL_FILESYSTEM_STACKED_HPP_INCLUDED 1
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "./filesystem.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
class StackedFileSystemAdapter : public FileSystemAdapter
|
||||
{
|
||||
private:
|
||||
std::vector<std::unique_ptr<FileSystemAdapter>> adapters_;
|
||||
public:
|
||||
std::vector<fs::path> getRoots() override;
|
||||
fs::path getHomeFolder() override;
|
||||
std::vector<FileInfo> listFiles(const fs::path& folder) override;
|
||||
FileInfo getFileInfo(const fs::path& file) override;
|
||||
StreamError open(const fs::path& path, FileOpenMode mode, std::unique_ptr<Stream>& outStream) override;
|
||||
|
||||
inline void addAdapter(std::unique_ptr<FileSystemAdapter>&& adapter) {
|
||||
adapters_.push_back(std::move(adapter));
|
||||
}
|
||||
template<typename TAdapter, typename... TArgs>
|
||||
inline void emplaceAdapter(TArgs&&... args) {
|
||||
addAdapter(std::make_unique<TAdapter>(std::forward<TArgs>(args)...));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_VIRTUAL_FILESYSTEM_STACKED_HPP_INCLUDED)
|
||||
Reference in New Issue
Block a user