2#ifndef LIBSBX_FILESYSTEM_VIRTUAL_FILESYSTEM_HPP_
3#define LIBSBX_FILESYSTEM_VIRTUAL_FILESYSTEM_HPP_
5#include <unordered_map>
6#include <unordered_set>
16#include <libsbx/filesystem/filesystem.hpp>
17#include <libsbx/filesystem/file_base.hpp>
18#include <libsbx/filesystem/alias.hpp>
20namespace sbx::filesystem {
26 using filesystem_list = std::vector<filesystem_ptr>;
27 using filesystem_map = std::unordered_map<alias, filesystem_list>;
32 auto lock = std::scoped_lock{_mutex};
34 for (
auto& [
alias, list] : _filesystems) {
35 for (
auto& filesystem : list) {
37 filesystem->shutdown();
43 void add_filesystem(
const alias&
alias,
const filesystem_ptr& filesystem) {
48 auto lock = std::scoped_lock{_mutex};
50 _filesystems[
alias].push_back(filesystem);
52 if (std::find(_sorted_alias.begin(), _sorted_alias.end(),
alias) == _sorted_alias.end()) {
53 _sorted_alias.push_back(
alias);
57 return lhs.length() > rhs.length();
61 void add_filesystem(std::string
alias,
const filesystem_ptr& filesystem) {
65 template<
typename Type,
typename... Args>
66 [[nodiscard]]
auto create_filesystem(
const alias&
alias, Args&&... args) -> std::shared_ptr<Type> {
67 auto filesystem = std::make_shared<Type>(
alias.string(), std::forward<Args>(args)...);
69 if (!filesystem || !filesystem->initialize()) {
73 add_filesystem(
alias, filesystem);
78 template<
typename Type,
typename... Args>
79 [[nodiscard]]
auto create_filesystem(std::string
alias, Args&&... args) -> std::shared_ptr<Type> {
83 auto remove_filesystem(
const alias&
alias,
const filesystem_ptr& filesystem) ->
void {
84 auto lock = std::scoped_lock{_mutex};
86 auto entry = _filesystems.find(
alias);
88 if (entry == _filesystems.end()) {
92 auto& list = entry->second;
94 list.erase(std::remove(list.begin(), list.end(), filesystem), list.end());
97 _filesystems.erase(entry);
99 _sorted_alias.erase(std::remove(_sorted_alias.begin(), _sorted_alias.end(),
alias), _sorted_alias.end());
103 auto remove_filesystem(std::string
alias,
const filesystem_ptr& filesystem) ->
void {
107 [[nodiscard]]
auto has_filesystem(
const alias&
alias,
const filesystem_ptr& filesystem)
const ->
bool {
108 auto lock = std::scoped_lock{_mutex};
110 auto entry = _filesystems.find(
alias);
112 if (entry == _filesystems.end()) {
116 const auto& list = entry->second;
118 return std::find(list.begin(), list.end(), filesystem) != list.end();
121 [[nodiscard]]
auto has_filesystem(std::string
alias,
const filesystem_ptr& filesystem)
const ->
bool {
126 auto lock = std::scoped_lock{_mutex};
128 _filesystems.erase(
alias);
130 _sorted_alias.erase(std::remove(_sorted_alias.begin(), _sorted_alias.end(),
alias), _sorted_alias.end());
133 void unregister_alias(std::string
alias) {
137 [[nodiscard]]
auto is_alias_registered(
const alias&
alias)
const ->
bool {
138 auto lock = std::scoped_lock{_mutex};
140 return _filesystems.find(
alias) != _filesystems.end();
143 [[nodiscard]]
auto is_alias_registered(std::string
alias)
const ->
bool {
147 [[nodiscard]]
auto filesystems(
const alias&
alias) -> std::optional<std::reference_wrapper<const filesystem_list>> {
148 auto lock = std::scoped_lock{_mutex};
150 auto entry = _filesystems.find(
alias);
152 if (entry != _filesystems.end()) {
153 return std::cref(entry->second);
159 [[nodiscard]]
auto filesystems(std::string
alias) -> std::optional<std::reference_wrapper<const filesystem_list>> {
163 [[nodiscard]]
auto open_file(
const std::string& path, file_base::mode mode) -> file_ptr {
164 auto lock = std::scoped_lock{_mutex};
166 auto result = _visit(path, [&](
const filesystem_ptr& filesystem, [[maybe_unused]]
auto is_main) -> std::optional<file_ptr> {
167 if (filesystem->exists(path)) {
168 if (auto file = filesystem->open_file(path, mode); file) {
171 }
else if (!filesystem->is_read_only() && file_base::mode_has_flag(mode, file_base::mode::write)) {
172 if (auto file = filesystem->open_file(path, mode); file) {
180 return result.value_or(
nullptr);
183 [[nodiscard]]
auto create_file(
const std::string& path) -> file_ptr {
184 auto lock = std::scoped_lock{_mutex};
186 auto result = _visit(path, [&](
const filesystem_ptr& filesystem, [[maybe_unused]]
auto is_main) -> std::optional<file_ptr> {
187 if (!filesystem->is_read_only()) {
188 if (auto file = filesystem->create_file(path); file) {
196 return result.value_or(
nullptr);
199 [[nodiscard]]
auto exists(
const std::string& path)
const ->
bool {
200 auto lock = std::scoped_lock{_mutex};
202 auto result = _visit(path, [&](
const filesystem_ptr& filesystem, [[maybe_unused]]
auto is_main) -> std::optional<bool> {
203 if (filesystem->exists(path)) {
210 return result.value_or(
false);
213 [[nodiscard]]
auto all_files() const -> std::vector<std::
string> {
214 auto lock = std::scoped_lock{_mutex};
216 auto result = std::vector<std::string>{};
217 auto seen = std::unordered_set<std::string>{};
219 for (
const auto& alias : _sorted_alias) {
220 auto entry = _filesystems.find(alias);
222 if (entry == _filesystems.end()) {
226 const auto& list = entry->second;
228 for (
auto entry = list.rbegin(); entry != list.rend(); ++entry) {
229 auto filesystem = *entry;
230 const auto& files = filesystem->files();
232 for (
const auto& info : files) {
233 const auto& path = info.virtual_path();
235 if (seen.emplace(path).second) {
236 result.push_back(path);
242 std::sort(result.begin(), result.end());
247 [[nodiscard]]
auto native_path_of(
const std::string& virtual_path)
const -> std::filesystem::path {
248 auto lock = std::scoped_lock{_mutex};
250 for (
const auto& alias : _sorted_alias) {
251 const auto& prefix = alias.string();
253 if (!virtual_path.starts_with(prefix)) {
257 auto entry = _filesystems.find(alias);
259 if (entry == _filesystems.end() || entry->second.empty()) {
263 const auto& base = entry->second.back()->base_path();
264 const auto tail = std::string_view{virtual_path}.substr(prefix.size());
266 auto result = std::filesystem::path{base};
269 result /= std::string{tail};
275 return std::filesystem::path{virtual_path};
278 [[nodiscard]]
auto native_path_of(
const std::filesystem::path& virtual_path)
const -> std::filesystem::path {
279 return native_path_of(virtual_path.string());
284 template<
typename Callback>
285 requires (std::is_invocable_v<Callback, const filesystem_ptr&, bool>)
286 auto _visit(
const std::string& path, Callback&& callback)
const -> std::invoke_result_t<Callback, const filesystem_ptr&, bool> {
287 using result_type = std::invoke_result_t<Callback, const filesystem_ptr&, bool>;
289 for (
const auto& alias : _sorted_alias) {
290 if (!path.starts_with(alias.string())) {
294 auto entry = _filesystems.find(alias);
296 if (entry == _filesystems.end()) {
300 const auto& list = entry->second;
302 for (
auto entry = list.rbegin(); entry != list.rend(); ++entry) {
303 auto filesystem = *entry;
304 auto is_main = (filesystem == list.front());
305 auto result = std::invoke(callback, filesystem, is_main);
313 return result_type{};
316 filesystem_map _filesystems;
317 std::vector<alias> _sorted_alias;
319 mutable std::mutex _mutex;
323using virtual_filesystem_ptr = std::shared_ptr<virtual_filesystem>;
324using virtual_filesystem_weak_ptr = std::weak_ptr<virtual_filesystem>;
Definition: virtual_filesystem.hpp:22